SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act, struct sigaction __user *, oact) { struct k_sigaction new_ka, old_ka; int ret; int err = 0; if (act) { old_sigset_t mask; if (!access_ok(VERIFY_READ, act, sizeof(*act))) return -EFAULT; err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); err |= __get_user(mask, &act->sa_mask.sig[0]); if (err) return -EFAULT; siginitset(&new_ka.sa.sa_mask, mask); } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) return -EFAULT; err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); err |= __put_user(0, &oact->sa_mask.sig[1]); err |= __put_user(0, &oact->sa_mask.sig[2]); err |= __put_user(0, &oact->sa_mask.sig[3]); if (err) return -EFAULT; } return ret; }
asmlinkage long compat_sys_sigaction(int sig, struct old_sigaction32 __user *act, struct old_sigaction32 __user *oact) { struct k_sigaction new_ka, old_ka; int ret; if (sig < 0) { set_thread_flag(TIF_NEWSIGNALS); sig = -sig; } if (act) { compat_old_sigset_t mask; u32 u_handler, u_restorer; ret = get_user(u_handler, &act->sa_handler); new_ka.sa.sa_handler = compat_ptr(u_handler); ret |= __get_user(u_restorer, &act->sa_restorer); new_ka.sa.sa_restorer = compat_ptr(u_restorer); ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); ret |= __get_user(mask, &act->sa_mask); if (ret) return ret; new_ka.ka_restorer = NULL; siginitset(&new_ka.sa.sa_mask, mask); } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); ret |= __put_user(ptr_to_compat(old_ka.sa.sa_restorer), &oact->sa_restorer); ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); ret |= __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } return ret; }
asmlinkage long sys32_rt_sigaction(int sig, const struct sigaction32 __user *act, struct sigaction32 __user *oact, size_t sigsetsize) { struct k_sigaction new_ka, old_ka; unsigned long sa_handler; int ret; compat_sigset_t set32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; if (act) { ret = get_user(sa_handler, &act->sa_handler); ret |= __copy_from_user(&set32, &act->sa_mask, sizeof(compat_sigset_t)); new_ka.sa.sa_mask.sig[0] = set32.sig[0] | (((long)set32.sig[1]) << 32); ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); if (ret) return -EFAULT; new_ka.sa.sa_handler = (__sighandler_t) sa_handler; } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); set32.sig[0] = old_ka.sa.sa_mask.sig[0]; ret = put_user((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler); ret |= __copy_to_user(&oact->sa_mask, &set32, sizeof(compat_sigset_t)); ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); }
asmlinkage long sys32_sigaction(int sig, const struct old_sigaction32 __user *act, struct old_sigaction32 __user *oact) { struct k_sigaction new_ka, old_ka; unsigned long sa_handler, sa_restorer; int ret; if (act) { compat_old_sigset_t mask; if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(sa_handler, &act->sa_handler) || __get_user(sa_restorer, &act->sa_restorer) || __get_user(new_ka.sa.sa_flags, &act->sa_flags) || __get_user(mask, &act->sa_mask)) return -EFAULT; new_ka.sa.sa_handler = (__sighandler_t) sa_handler; new_ka.sa.sa_restorer = (void (*)(void)) sa_restorer; siginitset(&new_ka.sa.sa_mask, mask); } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { sa_handler = (unsigned long) old_ka.sa.sa_handler; sa_restorer = (unsigned long) old_ka.sa.sa_restorer; if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(sa_handler, &oact->sa_handler) || __put_user(sa_restorer, &oact->sa_restorer) || __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) return -EFAULT; } return ret; }
asmlinkage int sparc_sigaction (int sig, const struct old_sigaction __user *act, struct old_sigaction __user *oact) { struct k_sigaction new_ka, old_ka; int ret; WARN_ON_ONCE(sig >= 0); sig = -sig; if (act) { unsigned long mask; if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || __get_user(new_ka.sa.sa_flags, &act->sa_flags) || __get_user(mask, &act->sa_mask)) return -EFAULT; siginitset(&new_ka.sa.sa_mask, mask); new_ka.ka_restorer = NULL; } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) return -EFAULT; } return ret; }
int ckpt_restore_signals(ckpt_desc_t desc) { int i; int ret; stack_t sigstack; sigset_t sigblocked; log_restore_signals("restoring sigstack ..."); if (ckpt_read(desc, &sigstack, sizeof(stack_t)) != sizeof(stack_t)) { log_err("failed to get sigstack"); return -EIO; } ret = compat_sigaltstack(current, &sigstack, NULL, 0); if (ret) { log_err("failed to restore sigstack (ret=%d)", ret); return ret; } log_restore_signals("restoring sigblocked ..."); if (ckpt_read(desc, &sigblocked, sizeof(sigset_t)) != sizeof(sigset_t)) { log_err("failed to restore sigstack"); return -EIO; } sigdelsetmask(&sigblocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); spin_lock_irq(¤t->sighand->siglock); current->blocked = sigblocked; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); log_restore_signals("restoring pending ..."); ret = ckpt_restore_sigpending(¤t->pending, 0, desc); if (ret) { log_err("failed to restore pending"); return ret; } ret = ckpt_restore_sigpending(¤t->signal->shared_pending, 1, desc); if (ret) { log_err("failed to restore shared_pending"); return ret; } log_restore_signals("restoring sigaction ..."); for (i = 0; i < _NSIG; i++) { struct k_sigaction sigaction; if (ckpt_read(desc, &sigaction, sizeof(struct k_sigaction)) != sizeof(struct k_sigaction)) { log_err("failed to get sigaction"); return -EIO; } if ((i != SIGKILL - 1) && (i != SIGSTOP - 1)) { ret = do_sigaction(i + 1, &sigaction, 0); if (ret) { log_err("failed to restore sigaction (ret=%d)", ret); return ret; } } } log_restore_pos(desc); return 0; }
static uint32_t sys_linux_sigaction(uint32_t arg[]) { return do_sigaction((int)arg[0], (const struct sigaction *)arg[1], (struct sigaction *)arg[2]); }
/** * <Ring 1> The main loop of TASK MM. * *****************************************************************************/ PUBLIC void task_mm() { init_mm(); while (1) { send_recv(RECEIVE, ANY, &mm_msg); int src = mm_msg.source; int reply = 1; int msgtype = mm_msg.type; switch (msgtype) { case FORK: mm_msg.RETVAL = do_fork(); break; case EXIT: do_exit(mm_msg.STATUS); reply = 0; break; case EXEC: mm_msg.RETVAL = do_exec(); break; case WAIT: do_wait(); reply = 0; break; case KILL: mm_msg.RETVAL = do_kill(); break; case RAISE: mm_msg.RETVAL = do_raise(); break; case BRK: mm_msg.RETVAL = do_brk(); break; case ACCT: mm_msg.RETVAL = do_acct(); break; case GETUID: mm_msg.RETVAL = do_getuid(); break; case SETUID: mm_msg.RETVAL = do_setuid(); break; case GETGID: mm_msg.RETVAL = do_getgid(); break; case SETGID: mm_msg.RETVAL = do_setgid(); break; case GETEUID: mm_msg.RETVAL = do_geteuid(); break; case GETEGID: mm_msg.RETVAL = do_getegid(); break; case SIGACTION: mm_msg.RETVAL = do_sigaction(); break; case ALARM: mm_msg.RETVAL = do_alarm(); break; default: dump_msg("MM::unknown msg", &mm_msg); assert(0); break; } if (reply) { mm_msg.type = SYSCALL_RET; send_recv(SEND, src, &mm_msg); } } }
/* * This is the task which runs the usermode application */ static int ____call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; int retval; /* Install input pipe when needed */ if (sub_info->stdin) { struct files_struct *f = current->files; struct fdtable *fdt; /* no races because files should be private here */ sys_close(0); fd_install(0, sub_info->stdin); spin_lock(&f->file_lock); fdt = files_fdtable(f); FD_SET(0, fdt->open_fds); FD_CLR(0, fdt->close_on_exec); spin_unlock(&f->file_lock); /* and disallow core files too */ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0}; } /* We can run anywhere, unlike our parent keventd(). */ set_cpus_allowed(current, CPU_MASK_ALL); retval = __exec_usermodehelper(sub_info->path, sub_info->argv, sub_info->envp, sub_info->ring); /* Exec failed? */ sub_info->retval = retval; do_exit(0); } /* Keventd can't block, but this (a child) can. */ static int wait_for_helper(void *data) { struct subprocess_info *sub_info = data; pid_t pid; struct k_sigaction sa; /* Install a handler: if SIGCLD isn't handled sys_wait4 won't * populate the status, but will return -ECHILD. */ sa.sa.sa_handler = SIG_IGN; sa.sa.sa_flags = 0; siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); do_sigaction(SIGCHLD, &sa, NULL); allow_signal(SIGCHLD); pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD); if (pid < 0) { sub_info->retval = pid; } else { /* * Normally it is bogus to call wait4() from in-kernel because * wait4() wants to write the exit code to a userspace address. * But wait_for_helper() always runs as keventd, and put_user() * to a kernel address works OK for kernel threads, due to their * having an mm_segment_t which spans the entire address space. * * Thus the __user pointer cast is valid here. */ sys_wait4(pid, (int __user *) &sub_info->retval, 0, NULL); } complete(sub_info->complete); return 0; } /* This is run by khelper thread */ static void __call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; pid_t pid; int wait = sub_info->wait; /* CLONE_VFORK: wait until the usermode helper has execve'd * successfully We need the data structures to stay around * until that is done. */ if (wait) pid = kernel_thread(wait_for_helper, sub_info, CLONE_FS | CLONE_FILES | SIGCHLD); else pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); if (pid < 0) { sub_info->retval = pid; complete(sub_info->complete); } else if (!wait) complete(sub_info->complete); } /** * call_usermodehelper_keys - start a usermode application * @path: pathname for the application * @argv: null-terminated argument list * @envp: null-terminated environment list * @session_keyring: session keyring for process (NULL for an empty keyring) * @wait: wait for the application to finish and return status. * * Runs a user-space application. The application is started * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). * * Must be called from process context. Returns a negative error code * if program was not execed successfully, or 0. */ int call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, int wait) { DECLARE_COMPLETION_ONSTACK(done); struct subprocess_info sub_info = { .complete = &done, .path = path, .argv = argv, .envp = envp, .ring = session_keyring, .wait = wait, .retval = 0, }; DECLARE_WORK(work, __call_usermodehelper, &sub_info); if (!khelper_wq) return -EBUSY; if (path[0] == '\0') return 0; queue_work(khelper_wq, &work); wait_for_completion(&done); return sub_info.retval; } EXPORT_SYMBOL(call_usermodehelper_keys); int call_usermodehelper_pipe(char *path, char **argv, char **envp, struct file **filp) { DECLARE_COMPLETION(done); struct subprocess_info sub_info = { .complete = &done, .path = path, .argv = argv, .envp = envp, .retval = 0, }; struct file *f; DECLARE_WORK(work, __call_usermodehelper, &sub_info); if (!khelper_wq) return -EBUSY; if (path[0] == '\0') return 0; f = create_write_pipe(); if (!f) return -ENOMEM; *filp = f; f = create_read_pipe(f); if (!f) { free_write_pipe(*filp); return -ENOMEM; } sub_info.stdin = f; queue_work(khelper_wq, &work); wait_for_completion(&done); return sub_info.retval; } EXPORT_SYMBOL(call_usermodehelper_pipe); void __init usermodehelper_init(void) { khelper_wq = create_singlethread_workqueue("khelper"); BUG_ON(!khelper_wq); }