static void kerext_process_startup(void *data) { struct kerargs_process_startup *kap = data; THREAD *act = actives[KERNCPU]; PROCESS *prp; struct _cred_info info; void *lcp; if((prp = lookup_pid(kap->pid)) == NULL || (prp->flags & _NTO_PF_LOADING) == 0) { kererr(act, ESRCH); return; } lcp = prp->lcp; if(kap->start) { int status; uintptr_t start_ip; THREAD *thp; status = procmgr.process_start ? procmgr.process_start(prp, &start_ip) : ENOSYS; if(status != EOK) { kererr(act, status); return; } lock_kernel(); info = prp->cred->info; info.suid = info.euid; info.sgid = info.egid; cred_set(&prp->cred, &info); thp = prp->valid_thp; if(!(prp->flags & _NTO_PF_FORKED)) { SETKIP_FUNC(thp, start_ip); SETKSP(thp, prp->initial_esp); } cpu_process_startup(thp, prp->flags & _NTO_PF_FORKED); _TRACE_NOARGS(thp); /* @@@ This should be examined in more detail later!!! */ prp->boundry_addr = WITHIN_BOUNDRY(prp->initial_esp, prp->initial_esp, user_boundry_addr) ? user_boundry_addr : VM_KERN_SPACE_BOUNDRY; prp->flags &= ~(_NTO_PF_LOADING | _NTO_PF_RING0); prp->lcp = 0; _TRACE_PR_EMIT_CREATE_NAME(prp); } lock_kernel(); SETKSTATUS(act, lcp); }
static void kerext_process_create(void *data) { struct kerargs_process_create *kap = data; pid_t pid; PROCESS *prp, *parent; THREAD *act = actives[KERNCPU]; int status, i; struct _cred_info info; if((parent = lookup_pid(kap->parent_pid)) == NULL) { kererr(act, ESRCH); return; } if (parent->num_processes >= parent->rlimit_vals_soft[RLIMIT_NPROC]) { kererr(act, EAGAIN); return; } lock_kernel(); // Check that we haven't run out of process vector entries // The process index & PID_MASK should never be all zeros // or all ones. All ones could cause SYNC_*() to return // valid looking numbers from an uninitialized sync. if((process_vector.nentries - process_vector.nfree) >= PID_MASK - 1) { kererr(act, EAGAIN); return; } // Alloc a process entry. if((prp = object_alloc(NULL, &process_souls)) == NULL) { kererr(act, ENOMEM); return; } if(kap->parent_pid) { prp->flags = _NTO_PF_LOADING | _NTO_PF_NOZOMBIE | _NTO_PF_RING0; prp->lcp = kap->lcp; } snap_time(&prp->start_time, 1); MUTEX_INIT(prp, &prp->mpartlist_lock); MUTEX_INIT(prp, &prp->spartlist_lock); CRASHCHECK((kap->extra == NULL) || (kap->extra->mpart_list == NULL) || ((kap->extra->spart_list == NULL) && SCHEDPART_INSTALLED())); { part_list_t *mpart_list = kap->extra->mpart_list; part_list_t *spart_list = kap->extra->spart_list; /* first thing is to associate with all specified partitions */ for (i=0; i<mpart_list->num_entries; i++) { if ((status = MEMPART_ASSOCIATE(prp, mpart_list->i[i].id, mpart_list->i[i].flags)) != EOK) { (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); object_free(NULL, &process_souls, prp); kererr(act, status); return; } } if (SCHEDPART_INSTALLED()) { for (i=0; i<spart_list->num_entries; i++) { if ((status = SCHEDPART_ASSOCIATE(prp, spart_list->i[i].id, spart_list->i[i].flags)) != EOK) { (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); object_free(NULL, &process_souls, prp); kererr(act, status); return; } } } } // Allocate a vector for 1 thread but don't get a thread entry. if(vector_add(&prp->threads, NULL, 1) == -1) { (void)SCHEDPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); object_free(NULL, &process_souls, prp); kererr(act, ENOMEM); return; } // Add process to the process table vector. if((pid = vector_add(&process_vector, prp, 0)) == -1) { (void)SCHEDPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); vector_free(&prp->threads); object_free(NULL, &process_souls, prp); kererr(act, ENOMEM); return; } prp->boundry_addr = VM_KERN_SPACE_BOUNDRY; prp->pid = pid | pid_unique; // adjust pid_unique during process destroy SIGMASK_SPECIAL(&prp->sig_queue); // Call out to allow memory manager to initialize the address space if((status = memmgr.mcreate(prp)) != EOK) { (void)SCHEDPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); vector_rem(&process_vector, PINDEX(pid)); vector_free(&prp->threads); object_free(NULL, &process_souls, prp); kererr(act, status); return; } // Inherit parents information info = parent->cred->info; info.sgid = parent->cred->info.egid; info.suid = 0; // The loader will set to euid after loading... cred_set(&prp->cred, &info); prp->seq = 1; // inherit setrlimit/getrlimit settings for(i=0; i < RLIM_NLIMITS; i++) { prp->rlimit_vals_soft[i] = parent->rlimit_vals_soft[i]; prp->rlimit_vals_hard[i] = parent->rlimit_vals_hard[i]; } prp->max_cpu_time = parent->max_cpu_time; // stop core file generation if RLIMIT_CORE is 0 if (prp->rlimit_vals_soft[RLIMIT_CORE] == 0) { prp->flags |= _NTO_PF_NOCOREDUMP; } // Inherit default scheduling partition // from creating thread. prp->default_dpp = SELECT_DPP(act, prp, schedpart_getid(prp)); prp->pgrp = parent->pgrp; prp->umask = parent->umask; prp->sig_ignore = parent->sig_ignore; SIGMASK_NO_KILLSTOP(&prp->sig_ignore); if((prp->limits = lookup_limits(parent->cred->info.euid)) || (prp->limits = parent->limits)) { prp->limits->links++; } if((prp->session = parent->session)) { atomic_add(&prp->session->links, 1); } // Link the new process in as a child of its creator. prp->child = NULL; prp->parent = parent; prp->sibling = parent->child; parent->child = prp; ++parent->num_processes; _TRACE_PR_EMIT_CREATE(prp); SETKSTATUS(act, prp); }
static void kerext_process_destroy(void *data) { PROCESS *prp, *parent, *child; pid_t pid = (pid_t)data; THREAD *act = actives[KERNCPU]; lock_kernel(); if(!(prp = vector_lookup(&process_vector, PINDEX(pid)))) { kererr(act, ESRCH); return; } if(prp->querying) { // Some proc thread is looking at the process fields // (a QueryOject() has been done on it). We have to wait // before we can destroy the process and free the memory SETKSTATUS(act, 0); return; } _TRACE_PR_EMIT_DESTROY(prp, pid); // retarget all children to PROC if((child = prp->child)) { PROCESS *proc = sysmgr_prp; do { //Loop might take a while - give intr queue a chance to drain. KEREXT_PREEMPT(act); prp->child = child->sibling; if(procmgr.process_threads_destroyed) { if (((child->flags & (_NTO_PF_LOADING | _NTO_PF_TERMING | _NTO_PF_ZOMBIE)) == _NTO_PF_ZOMBIE) || (child->flags & _NTO_PF_NOZOMBIE)) { struct sigevent ev; child->flags &= ~(_NTO_PF_ZOMBIE | _NTO_PF_WAITINFO); (*procmgr.process_threads_destroyed)(child, &ev); sigevent_proc(&ev); } } child->parent = proc; child->sibling = proc->child; proc->child = child; --prp->num_processes; ++proc->num_processes; } while((child = prp->child)); } vector_rem(&process_vector, PINDEX(pid)); // Remove the thread vector if(prp->threads.nentries && prp->threads.nentries == prp->threads.nfree) vector_free(&prp->threads); // Remove the timer vector if(prp->timers.nentries && prp->timers.nentries == prp->timers.nfree) vector_free(&prp->timers); // Remove the fd vector if(prp->fdcons.nentries && prp->fdcons.nentries == prp->fdcons.nfree) vector_free(&prp->fdcons); // Remove the channel vector if(prp->chancons.nentries && prp->chancons.nentries == prp->chancons.nfree) vector_free(&prp->chancons); // Unlink the credential if(prp->cred) { cred_set(&prp->cred, NULL); } // undo all session stuff if(prp->session && atomic_sub_value(&prp->session->links, 1) == 1) { _sfree(prp->session, sizeof *prp->session); } prp->session = 0; // Unlink the limits if(prp->limits && --prp->limits->links == 0) { LINK1_REM(limits_list, prp->limits, LIMITS); object_free(NULL, &limits_souls, prp->limits); } if(prp->limits && prp->limits->links == ~0U) crash(); if(prp->pid) crash(); if(prp->cred) crash(); if(prp->alarm) crash(); if(pril_first(&prp->sig_pending)) crash(); if(prp->sig_table) crash(); if(prp->nfds) crash(); if(prp->chancons.vector) crash(); if(prp->fdcons.vector) crash(); if(prp->threads.vector) crash(); if(prp->timers.vector) crash(); if(prp->memory) crash(); if(prp->join_queue) crash(); // if(prp->session) crash(); if(prp->debugger) crash(); if(prp->lock) crash(); if(prp->num_active_threads) crash(); if(prp->vfork_info) crash(); // FIX ME - this is not NULL now ... why? if(prp->rsrc_list) crash(); if(prp->conf_table) crash(); // Unlink from parent parent = prp->parent; --parent->num_processes; if(prp == parent->child) { parent->child = prp->sibling; } else { for(parent = parent->child ; parent ; parent = parent->sibling) { if(parent->sibling == prp) { parent->sibling = prp->sibling; break; } } } //Keep pids as positive numbers pid_unique = (pid_unique + (PID_MASK + 1)) & INT_MAX; /* diassociate prp from all partitions */ /* apparently prp->pid gets looked at even though we are about to object_free(prp). prp->pid = pid; // stick it back in so we have the info for the disassociate event */ (void)SCHEDPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MEMPART_DISASSOCIATE(prp, part_id_t_INVALID); (void)MUTEX_DESTROY(prp, &prp->mpartlist_lock); (void)MUTEX_DESTROY(prp, &prp->spartlist_lock); CRASHCHECK(prp->mpart_list.vector != NULL); CRASHCHECK(prp->spart_list != NULL); // no vector for sched partitions since only 1 object_free(NULL, &process_souls, prp); SETKSTATUS(act, 1); }
/* * Enact a scenario by looping through the four test cases for the scenario, * spawning off pairs of processes with the desired credentials, and * reporting results to stdout. */ static int enact_scenario(int scenario) { pid_t pid1, pid2; char *name, *tracefile; int error, desirederror, loop; for (loop = 0; loop < LOOP_MAX+1; loop++) { /* * Spawn the first child, target of the operation. */ pid1 = fork(); switch (pid1) { case -1: return (-1); case 0: /* child */ error = cred_set(scenarios[scenario].sc_cred2); if (error) { perror("cred_set"); return (error); } /* 200 seconds should be plenty of time. */ sleep(200); exit(0); default: /* parent */ break; } /* * XXX * This really isn't ideal -- give proc 1 a chance to set * its credentials, or we may get spurious errors. Really, * some for of IPC should be used to allow the parent to * wait for the first child to be ready before spawning * the second child. */ sleep(1); /* * Spawn the second child, source of the operation. */ pid2 = fork(); switch (pid2) { case -1: return (-1); case 0: /* child */ error = cred_set(scenarios[scenario].sc_cred1); if (error) { perror("cred_set"); return (error); } /* * Initialize errno to zero so as to catch any * generated errors. In each case, perform the * operation. Preserve the error number for later * use so it doesn't get stomped on by any I/O. * Determine the desired error for the given case * by extracting it from the scenario table. * Initialize a function name string for output * prettiness. */ errno = 0; switch (loop) { case LOOP_PTRACE: error = ptrace(PT_ATTACH, pid1, NULL, 0); error = errno; name = "ptrace"; desirederror = scenarios[scenario].sc_canptrace_errno; break; case LOOP_KTRACE: tracefile = mktemp("/tmp/testuid_ktrace.XXXXXX"); if (tracefile == NULL) { error = errno; perror("mktemp"); break; } error = ktrace(tracefile, KTROP_SET, KTRFAC_SYSCALL, pid1); error = errno; name = "ktrace"; desirederror = scenarios[scenario].sc_canktrace_errno; unlink(tracefile); break; case LOOP_SIGHUP: error = kill(pid1, SIGHUP); error = errno; name = "sighup"; desirederror = scenarios[scenario].sc_cansighup_errno; break; case LOOP_SIGSEGV: error = kill(pid1, SIGSEGV); error = errno; name = "sigsegv"; desirederror = scenarios[scenario].sc_cansigsegv_errno; break; case LOOP_SEE: getpriority(PRIO_PROCESS, pid1); error = errno; name = "see"; desirederror = scenarios[scenario].sc_cansee_errno; break; case LOOP_SCHED: error = setpriority(PRIO_PROCESS, pid1, 0); error = errno; name = "sched"; desirederror = scenarios[scenario].sc_cansched_errno; break; default: name = "broken"; } if (error != desirederror) { fprintf(stdout, "[%s].%s: expected %s, got %s\n ", scenarios[scenario].sc_name, name, errno_to_string(desirederror), errno_to_string(error)); cred_print(stdout, scenarios[scenario].sc_cred1); cred_print(stdout, scenarios[scenario].sc_cred2); fprintf(stdout, "\n"); } exit(0); default: /* parent */ break; } error = waitpid(pid2, NULL, 0); /* * Once pid2 has died, it's safe to kill pid1, if it's still * alive. Mask signal failure in case the test actually * killed pid1 (not unlikely: can occur in both signal and * ptrace cases). */ kill(pid1, SIGKILL); error = waitpid(pid2, NULL, 0); } return (0); }