diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 608c435e1..b5c851869 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -29,6 +29,7 @@ if (${ARCH} STREQUAL "arm64") endif() set(MCKERNEL_SRCS + sysctrl-signalonfork_wait.c init.c mem.c debug.c mikc.c listeners.c ap.c syscall.c cls.c host.c process.c copy.c waitq.c futex.c timer.c plist.c fileobj.c shmobj.c zeroobj.c procfs.c devobj.c sysfs.c xpmem.c profile.c freeze.c rbtree.c pager.c diff --git a/kernel/include/sysctrl-signalonfork_wait.h b/kernel/include/sysctrl-signalonfork_wait.h new file mode 100644 index 000000000..2e4833e1c --- /dev/null +++ b/kernel/include/sysctrl-signalonfork_wait.h @@ -0,0 +1,49 @@ +#ifndef __SYSCTRL_SIGNALONFORK_WAIT_H__ +#define __SYSCTRL_SIGNALONFORK_WAIT_H__ + +enum signalonfork_error { + signalonfork_error_null = 0, + + do_fork_release_cpuid_0 = 0x010000, + do_fork_destroy_thread_0 = 0x010100, + do_fork_destroy_thread_1 = 0x010101, + do_fork_destroy_thread_2 = 0x010102, + do_fork_release_ids_0 = 0x010200, + do_fork_release_ids_1 = 0x010201, + do_fork_free_mod_clone_arg_0 = 0x010300, + do_fork_free_mod_clone_arg_1 = 0x010301, + + clone_thread_free_thread_0 = 0x020000, + clone_thread_free_thread_1 = 0x020001, + clone_thread_free_fp_regs_0 = 0x020100, + clone_thread_free_fp_regs_1 = 0x020101, + clone_thread_free_fork_process_proc_0 = 0x020200, + clone_thread_free_fork_process_proc_1 = 0x020201, + clone_thread_free_fork_process_asp_0 = 0x020300, + clone_thread_free_fork_process_vm_0 = 0x020400, + clone_thread_free_fork_process_cmdline_0 = 0x020500, + clone_thread_free_fork_process_cmdline_1 = 0x020501, + clone_thread_free_fork_process_mckfd_0 = 0x020600, + clone_thread_free_fork_clone_process_0 = 0x020700, + clone_thread_free_copy_user_ranges_0 = 0x020800, + + copy_user_ranges_err_rollback_0 = 0x030000, +}; + +static inline int __sof_err(enum signalonfork_error err) +{ + extern enum signalonfork_error sof_error; + return (err == sof_error); +} + +#define sof_err(err) ({ \ + int ret = __sof_err(err); \ + if (ret) { \ + kprintf("sof error injection: %s\n", \ + #err); \ + } \ + ret; \ + }) + + +#endif /*__SYSCTRL_SIGNALONFORK_WAIT_H__*/ diff --git a/kernel/init.c b/kernel/init.c index 8781c1274..81bcf312d 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -341,6 +341,9 @@ static void populate_sysfs(void) numa_sysfs_setup(); dynamic_debug_sysfs_setup(); //setup_remote_snooping_samples(); + + extern void signalonfork_test_sysfs_setup(void); + signalonfork_test_sysfs_setup(); } /* populate_sysfs() */ int host_ikc_inited = 0; diff --git a/kernel/process.c b/kernel/process.c index 6d07b5ca4..1be3d6352 100644 --- a/kernel/process.c +++ b/kernel/process.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -395,6 +396,9 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, IHK_MC_AP_NOWAIT)) == NULL) { return NULL; } + if (sof_err(clone_thread_free_thread_0)) { + goto free_thread; + } memset(thread, 0, sizeof(struct thread)); INIT_LIST_HEAD(&thread->hash_list); @@ -409,10 +413,19 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, ihk_mc_init_user_process(&thread->ctx, &thread->uctx, ((char *)thread) + KERNEL_STACK_NR_PAGES * PAGE_SIZE, pc, sp); + if (sof_err(clone_thread_free_thread_1)) { + goto free_thread; + } + /* copy fp_regs from parent */ if (save_fp_regs(org)) { goto free_thread; } + + if (sof_err(clone_thread_free_fp_regs_0)) { + goto free_fp_regs; + } + if (copy_fp_regs(org, thread)) { goto free_fp_regs; } @@ -437,23 +450,47 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, } /* fork() */ else { + if (sof_err(clone_thread_free_fp_regs_1)) { + goto free_fp_regs; + } + proc = kmalloc(sizeof(struct process), IHK_MC_AP_NOWAIT); if(!proc) goto free_fp_regs; + + if (sof_err(clone_thread_free_fork_process_proc_0)) { + goto free_fork_process_proc; + } + memset(proc, '\0', sizeof(struct process)); init_process(proc, org->proc); #ifdef PROFILE_ENABLE proc->profile = org->proc->profile; #endif proc->termsig = termsig; + + if (sof_err(clone_thread_free_fork_process_proc_1)) { + goto free_fork_process_proc; + } + asp = create_address_space(cpu_local_var(resource_set), 1); if (!asp) { goto free_fork_process_proc; } + + if (sof_err(clone_thread_free_fork_process_asp_0)) { + goto free_fork_process_asp; + } + proc->vm = kmalloc(sizeof(struct process_vm), IHK_MC_AP_NOWAIT); if (!proc->vm) { goto free_fork_process_asp; } + + if (sof_err(clone_thread_free_fork_process_vm_0)) { + goto free_fork_process_vm; + } + memset(proc->vm, '\0', sizeof(struct process_vm)); proc->saved_cmdline_len = org->proc->saved_cmdline_len; @@ -462,6 +499,11 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, if (!proc->saved_cmdline) { goto free_fork_process_vm; } + + if (sof_err(clone_thread_free_fork_process_cmdline_0)) { + goto free_fork_process_cmdline; + } + memcpy(proc->saved_cmdline, org->proc->saved_cmdline, proc->saved_cmdline_len); @@ -484,6 +526,10 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, /* Copy user-space mappings. * TODO: do this with COW later? */ v->on_fork_vm = proc->vm; + if (sof_err(clone_thread_free_fork_process_cmdline_1)) { + v->on_fork_vm = NULL; + goto free_fork_process_cmdline; + } if (copy_user_ranges(proc->vm, org->vm) != 0) { v->on_fork_vm = NULL; goto free_fork_process_cmdline; @@ -492,6 +538,9 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, /* Copy mckfd list FIXME: Replace list manipulation with list_add() etc. */ + if (sof_err(clone_thread_free_copy_user_ranges_0)) { + goto free_fork_process_mckfd; + } long irqstate = ihk_mc_spinlock_lock(&proc->mckfd_lock); struct mckfd *cur; for (cur = org->proc->mckfd; cur; cur = cur->next) { @@ -517,6 +566,9 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, } } ihk_mc_spinlock_unlock(&proc->mckfd_lock, irqstate); + if (sof_err(clone_thread_free_fork_process_mckfd_0)) { + goto free_fork_process_mckfd; + } thread->vm->vdso_addr = org->vm->vdso_addr; thread->vm->vvar_addr = org->vm->vvar_addr; @@ -536,6 +588,13 @@ clone_thread(struct thread *org, unsigned long pc, unsigned long sp, /* copy signal handlers (i.e., fork()) */ else { dkprintf("fork(): sigcommon\n"); + if (sof_err(clone_thread_free_fork_clone_process_0)) { + if (clone_flags & CLONE_VM) { + goto free_clone_process; + } + goto free_fork_process_mckfd; + } + thread->sigcommon = kmalloc(sizeof(struct sig_common), IHK_MC_AP_NOWAIT); if (!thread->sigcommon) { @@ -849,6 +908,10 @@ static int copy_user_ranges(struct process_vm *vm, struct process_vm *orgvm) // memory_stat_rss_add() is called in child-node, i.e. copy_user_pte() } + if (sof_err(copy_user_ranges_err_rollback_0)) { + goto err_rollback; + } + memory_range_write_unlock(orgvm, &irqflags); return 0; diff --git a/kernel/syscall.c b/kernel/syscall.c index 1012d66ba..28cf7553b 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -2737,6 +2738,10 @@ unsigned long do_fork(int clone_flags, unsigned long newsp, kprintf("do_fork,core not available\n"); return -EAGAIN; } + if (sof_err(do_fork_release_cpuid_0)) { + err = -EFAULT; + goto release_cpuid; + } new = clone_thread(old, curpc, newsp ? newsp : cursp, clone_flags); @@ -2747,6 +2752,10 @@ unsigned long do_fork(int clone_flags, unsigned long newsp, } newproc = new->proc; + if (sof_err(do_fork_destroy_thread_0)) { + err = -EFAULT; + goto destroy_thread; + } cpu_set(cpuid, &new->vm->address_space->cpu_set, &new->vm->address_space->cpu_set_lock); @@ -2766,8 +2775,13 @@ unsigned long do_fork(int clone_flags, unsigned long newsp, goto destroy_thread; } - newproc->tids = kmalloc(sizeof(struct mcexec_tid) * - NR_TIDS, IHK_MC_AP_NOWAIT); + if (sof_err(do_fork_destroy_thread_1)) { + newproc->tids = NULL; + } else { + newproc->tids = kmalloc(sizeof(struct mcexec_tid) * + NR_TIDS, IHK_MC_AP_NOWAIT); + } + if (!newproc->tids) { mcs_rwlock_writer_unlock(&newproc->threads_lock, &lock); kfree(tids); @@ -2775,7 +2789,12 @@ unsigned long do_fork(int clone_flags, unsigned long newsp, goto destroy_thread; } - if ((err = settid(new, NR_TIDS, tids)) < 0) { + if (sof_err(do_fork_release_ids_0)) { + err = -EFAULT; + } else { + err = settid(new, NR_TIDS, tids); + } + if (err < 0) { mcs_rwlock_writer_unlock(&newproc->threads_lock, &lock); kfree(tids); @@ -2835,7 +2854,11 @@ retry_tid: if(oldproc->ppid_parent->pid != 1) request1.args[0] = clone_flags; } - newproc->pid = do_syscall(&request1, ihk_mc_get_processor_id()); + if (sof_err(do_fork_destroy_thread_2)) { + newproc->pid = -EFAULT; + } else { + newproc->pid = do_syscall(&request1, ihk_mc_get_processor_id()); + } if (newproc->pid < 0) { kprintf("ERROR: forking host process\n"); err = newproc->pid; @@ -2900,6 +2923,11 @@ retry_tid: ihk_mc_syscall_ret(new->uctx) = 0; new->status = PS_RUNNING; + + if (sof_err(do_fork_release_ids_1)) { + err = -EFAULT; + goto release_ids; + } /* Only the first do_fork() call creates a thread on a Linux CPU */ if (__sync_bool_compare_and_swap(&old->mod_clone, SPAWN_TO_REMOTE, SPAWN_TO_LOCAL)) { @@ -2918,6 +2946,12 @@ retry_tid: } } chain_thread(new); + + if (sof_err(do_fork_free_mod_clone_arg_0)) { + err = -EFAULT; + goto free_mod_clone_arg; + } + if (!(clone_flags & CLONE_VM)) { newproc->status = PS_RUNNING; if(clone_flags & CLONE_PARENT){ @@ -2960,7 +2994,12 @@ retry_tid: request1.number = __NR_clone; request1.args[0] = 1; request1.args[1] = new->tid; - err = do_syscall(&request1, ihk_mc_get_processor_id()); + + if (sof_err(do_fork_free_mod_clone_arg_1)) { + err = -EFAULT; + } else { + err = do_syscall(&request1, ihk_mc_get_processor_id()); + } if (err) { goto free_mod_clone_arg; } diff --git a/kernel/sysctrl-signalonfork_wait.c b/kernel/sysctrl-signalonfork_wait.c new file mode 100644 index 000000000..5cc031780 --- /dev/null +++ b/kernel/sysctrl-signalonfork_wait.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +enum signalonfork_error sof_error; + +static ssize_t signalonfork_test_show(struct sysfs_ops *ops, + void *instance, void *buf, size_t size) +{ + return snprintf(buf, size, "%d\n", sof_error); +} + +static ssize_t signalonfork_test_store(struct sysfs_ops *ops, + void *instance, void *buf, size_t size) +{ + sof_error = strtol(buf, NULL, 0); + return size; +} + +static struct sysfs_ops signalonfork_test_ops = { + .show = &signalonfork_test_show, + .store = &signalonfork_test_store, +}; + +void signalonfork_test_sysfs_setup(void) +{ + int error; + + error = sysfs_createf(&signalonfork_test_ops, NULL, 0666, + "/sys/kernel/debug/signalonfork_test"); + if (error) { + kprintf("%s: ERROR: creating signalonfork_test sysfs file", + __func__); + } +}