Files
Matheus 9a9740469f kcov: move thread attach from open to start
The current kcov code attaches a kcov object to the thread which opened
it. This is not desirable, as syzkaller opens /dev/kcov in a parent
process and passes it down to the child.

Move the thread attachment from open() time to start time.
2026-10-08 18:42:25 -03:00

113 lines
3.4 KiB
C

#include <kernel/thread.h>
#include <kernel/proc.h>
#include <kernel/slab.h>
#include <logging.h>
#include <arch/context.h>
static scache_t *thread_cache;
thread_t *sched_newthread(void *ip, size_t kstacksize, int nice, proc_t *proc, void *ustack) {
if (thread_cache == NULL) {
thread_cache = slab_newcache(sizeof(thread_t), 0, NULL, NULL);
__assert(thread_cache);
}
thread_t *thread = slab_allocate(thread_cache);
if (thread == NULL)
return NULL;
memset(thread, 0, sizeof(thread_t));
if (arch_extracontext_init(&thread->extracontext)) {
slab_free(thread_cache, thread);
return NULL;
}
thread->kernelstack = mm_map(NULL, kstacksize, MM_RANGE_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC, NULL);
if (thread->kernelstack == NULL) {
arch_extracontext_free(&thread->extracontext);
slab_free(thread_cache, thread);
return NULL;
}
thread->kernelstacktop = (void *)((uintptr_t)thread->kernelstack + kstacksize);
// non kernel thread mm contexts are handled by the caller
thread->mmctx = proc ? NULL : &mm_kernel_ctx;
thread->proc = proc;
thread->kernelstacksize = kstacksize;
thread->nice = nice;
if (proc) {
// each thread holds one reference to proc
PROC_HOLD(proc);
thread->tid = proc_allocate_pid();
}
CTX_INIT(&thread->context, proc != NULL, true);
CTX_SP(&thread->context) = proc ? (ctxreg_t)ustack : (ctxreg_t)thread->kernelstacktop;
CTX_IP(&thread->context) = (ctxreg_t)ip;
SPINLOCK_INIT(thread->sleeplock);
SPINLOCK_INIT(thread->signals.lock);
EVENT_INITHEADER(&thread->signals.waitpendingevent);
memset(&thread->metrics, 0, sizeof(thread->metrics));
thread->metrics.sleep_time_avg_us = 1; // to prevent a division by 0 when the thread first gets scheduled
thread->class = THREAD_CLASS_TIMESHARE;
return thread;
}
void sched_destroythread(thread_t *thread) {
mm_unmap(thread->kernelstack, thread->kernelstacksize, 0);
arch_extracontext_free(&thread->extracontext);
slab_free(thread_cache, thread);
}
static void threadexit_internal(context_t *, void *) {
thread_t *thread = current_thread();
// we don't need to access the current thread anymore, as any state will be ignored and we are running on the scheduler stack
// as well as not being preempted at all
interrupt_set(false);
current_cpu()->thread = NULL;
thread->flags |= THREAD_FLAGS_DEAD;
if (thread->proc)
EVENT_SIGNAL(&thread->proc->thread_exit_event);
// because a thread deallocating its own data is a nightmare, thread deallocation and such will be left to whoever frees the proc it's tied to
// (likely an exit(2) call)
// FIXME this doesn't apply to kernel threads and something should be figured out for them
sched_stop_current_thread();
}
__attribute__((noreturn)) void sched_threadexit() {
thread_t *thread = current_thread();
proc_t *proc = thread->proc;
#ifdef KCOV_ENABLED
if (thread->kcov)
kcov_set_mode(thread->kcov, KCOV_MODE_DISABLED);
#endif
mm_context_t *oldctx = thread->mmctx;
mm_switch_context(&mm_kernel_ctx);
if (proc) {
__atomic_fetch_sub(&proc->runningthreadcount, 1, __ATOMIC_SEQ_CST);
__assert(oldctx != &mm_kernel_ctx);
if (proc->runningthreadcount == 0) {
if (thread->shouldexit)
proc->status = -1;
proc_exit();
mm_destroy_context(oldctx);
}
PROC_RELEASE(proc);
}
interrupt_set(false);
arch_context_saveandcall(threadexit_internal, current_cpu()->schedulerstack, NULL);
__builtin_unreachable();
}