From fd44ae6845e56ca7ac3435e2ec3ad984d9b7a4b7 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sat, 15 Feb 2003 12:30:25 -0500 Subject: Clean up ptrace_setoptions and PT_* constants --- kernel/exit.c | 6 ++---- kernel/ptrace.c | 25 +++---------------------- kernel/signal.c | 1 + 3 files changed, 6 insertions(+), 26 deletions(-) (limited to 'kernel') diff --git a/kernel/exit.c b/kernel/exit.c index 0523eb305c17..58546ab8fe54 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -694,10 +694,8 @@ static void exit_notify(struct task_struct *tsk) * only has special meaning to our real parent. */ if (tsk->exit_signal != -1) { - if (tsk->parent == tsk->real_parent) - do_notify_parent(tsk, tsk->exit_signal); - else - do_notify_parent(tsk, SIGCHLD); + int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD; + do_notify_parent(tsk, signal); } tsk->state = TASK_ZOMBIE; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index f67675f3d9d3..287b81b65c34 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -252,49 +252,30 @@ int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int static int ptrace_setoptions(struct task_struct *child, long data) { + child->ptrace &= ~PT_TRACE_MASK; + if (data & PTRACE_O_TRACESYSGOOD) child->ptrace |= PT_TRACESYSGOOD; - else - child->ptrace &= ~PT_TRACESYSGOOD; if (data & PTRACE_O_TRACEFORK) child->ptrace |= PT_TRACE_FORK; - else - child->ptrace &= ~PT_TRACE_FORK; if (data & PTRACE_O_TRACEVFORK) child->ptrace |= PT_TRACE_VFORK; - else - child->ptrace &= ~PT_TRACE_VFORK; if (data & PTRACE_O_TRACECLONE) child->ptrace |= PT_TRACE_CLONE; - else - child->ptrace &= ~PT_TRACE_CLONE; if (data & PTRACE_O_TRACEEXEC) child->ptrace |= PT_TRACE_EXEC; - else - child->ptrace &= ~PT_TRACE_EXEC; if (data & PTRACE_O_TRACEVFORKDONE) child->ptrace |= PT_TRACE_VFORK_DONE; - else - child->ptrace &= ~PT_TRACE_VFORK_DONE; if (data & PTRACE_O_TRACEEXIT) child->ptrace |= PT_TRACE_EXIT; - else - child->ptrace &= ~PT_TRACE_EXIT; - - if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK - | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE - | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT - | PTRACE_O_TRACEVFORKDONE)) - != data) - return -EINVAL; - return 0; + return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; } static int ptrace_getsiginfo(struct task_struct *child, long data) diff --git a/kernel/signal.c b/kernel/signal.c index d0d09cfbe863..b8fc6a05c031 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From d946b9ee265d41ea5015fd85028b202605836f9b Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sat, 15 Feb 2003 12:40:07 -0500 Subject: Set ptrace_message before PT_TRACE_EXIT. --- kernel/exit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/exit.c b/kernel/exit.c index 58546ab8fe54..d2d7c72f1e81 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -729,8 +729,10 @@ NORET_TYPE void do_exit(long code) profile_exit_task(tsk); - if (unlikely(current->ptrace & PT_TRACE_EXIT)) + if (unlikely(current->ptrace & PT_TRACE_EXIT)) { + current->ptrace_message = code; ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); + } acct_process(code); __exit_mm(tsk); -- cgit v1.2.3 From 4883c96a0e3245ca007ad7dd79714daa1c573495 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Feb 2003 00:15:57 -0800 Subject: Clean up and fix locking around signal rendering --- fs/proc/array.c | 85 ++++++++++++++++++++++++++++++++------------------ include/linux/signal.h | 2 -- kernel/sched.c | 15 --------- 3 files changed, 54 insertions(+), 48 deletions(-) (limited to 'kernel') diff --git a/fs/proc/array.c b/fs/proc/array.c index 5cba4d1a70ff..53edb284329b 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -180,51 +180,74 @@ static inline char * task_state(struct task_struct *p, char *buffer) return buffer; } +static char * render_sigset_t(const char *header, sigset_t *set, char *buffer) +{ + int i, len; + + len = strlen(header); + memcpy(buffer, header, len); + buffer += len; + + i = _NSIG; + do { + int x = 0; + + i -= 4; + if (sigismember(set, i+1)) x |= 1; + if (sigismember(set, i+2)) x |= 2; + if (sigismember(set, i+3)) x |= 4; + if (sigismember(set, i+4)) x |= 8; + *buffer++ = (x < 10 ? '0' : 'a' - 10) + x; + } while (i >= 4); + + *buffer++ = '\n'; + *buffer = 0; + return buffer; +} + static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, sigset_t *catch) { struct k_sigaction *k; int i; - sigemptyset(ign); - sigemptyset(catch); + k = p->sighand->action; + for (i = 1; i <= _NSIG; ++i, ++k) { + if (k->sa.sa_handler == SIG_IGN) + sigaddset(ign, i); + else if (k->sa.sa_handler != SIG_DFL) + sigaddset(catch, i); + } +} +static inline char * task_sig(struct task_struct *p, char *buffer) +{ + sigset_t pending, shpending, blocked, ignored, caught; + + sigemptyset(&pending); + sigemptyset(&shpending); + sigemptyset(&blocked); + sigemptyset(&ignored); + sigemptyset(&caught); + + /* Gather all the data with the appropriate locks held */ read_lock(&tasklist_lock); if (p->sighand) { spin_lock_irq(&p->sighand->siglock); - k = p->sighand->action; - for (i = 1; i <= _NSIG; ++i, ++k) { - if (k->sa.sa_handler == SIG_IGN) - sigaddset(ign, i); - else if (k->sa.sa_handler != SIG_DFL) - sigaddset(catch, i); - } + pending = p->pending.signal; + shpending = p->signal->shared_pending.signal; + blocked = p->blocked; + collect_sigign_sigcatch(p, &ignored, &caught); spin_unlock_irq(&p->sighand->siglock); } read_unlock(&tasklist_lock); -} - -static inline char * task_sig(struct task_struct *p, char *buffer) -{ - sigset_t ign, catch; - buffer += sprintf(buffer, "SigPnd:\t"); - buffer = render_sigset_t(&p->pending.signal, buffer); - *buffer++ = '\n'; - buffer += sprintf(buffer, "ShdPnd:\t"); - buffer = render_sigset_t(&p->signal->shared_pending.signal, buffer); - *buffer++ = '\n'; - buffer += sprintf(buffer, "SigBlk:\t"); - buffer = render_sigset_t(&p->blocked, buffer); - *buffer++ = '\n'; - - collect_sigign_sigcatch(p, &ign, &catch); - buffer += sprintf(buffer, "SigIgn:\t"); - buffer = render_sigset_t(&ign, buffer); - *buffer++ = '\n'; - buffer += sprintf(buffer, "SigCgt:\t"); /* Linux 2.0 uses "SigCgt" */ - buffer = render_sigset_t(&catch, buffer); - *buffer++ = '\n'; + /* render them all */ + buffer = render_sigset_t("SigPnd:\t", &pending, buffer); + buffer = render_sigset_t("ShdPnd:\t", &shpending, buffer); + buffer = render_sigset_t("SigBlk:\t", &blocked, buffer); + buffer = render_sigset_t("SigIgn:\t", &ignored, buffer); + buffer = render_sigset_t("SigCgt:\t", &caught, buffer); return buffer; } diff --git a/include/linux/signal.h b/include/linux/signal.h index 09e4d8587f0a..05e1fd671f82 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -151,8 +151,6 @@ static inline void sigfillset(sigset_t *set) } } -extern char * render_sigset_t(sigset_t *set, char *buffer); - /* Some extensions for manipulating the low 32 signals in particular. */ static inline void sigaddsetmask(sigset_t *set, unsigned long mask) diff --git a/kernel/sched.c b/kernel/sched.c index 1981a92ab129..7a38971d997e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2095,21 +2095,6 @@ static void show_task(task_t * p) } } -char * render_sigset_t(sigset_t *set, char *buffer) -{ - int i = _NSIG, x; - do { - i -= 4, x = 0; - if (sigismember(set, i+1)) x |= 1; - if (sigismember(set, i+2)) x |= 2; - if (sigismember(set, i+3)) x |= 4; - if (sigismember(set, i+4)) x |= 8; - *buffer++ = (x < 10 ? '0' : 'a' - 10) + x; - } while (i >= 4); - *buffer = 0; - return buffer; -} - void show_state(void) { task_t *g, *p; -- cgit v1.2.3 From 817f3eeecfc635d6c88f4bc38ef6619e99deaca0 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 16 Feb 2003 04:02:30 -0800 Subject: [PATCH] compat futex fix Fix compile errors after futex merge. --- kernel/compat.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/compat.c b/kernel/compat.c index 892b49f14f5f..7b04cf90c847 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -17,6 +17,7 @@ #include #include #include /* for MAX_SCHEDULE_TIMEOUT */ +#include /* for FUTEX_WAIT */ #include @@ -210,7 +211,7 @@ asmlinkage long compat_sys_sigprocmask(int how, compat_old_sigset_t *set, return ret; } -extern long do_futex(u32 *, int, int, unsigned long); +extern long do_futex(unsigned long, int, int, unsigned long); asmlinkage long compat_sys_futex(u32 *uaddr, int op, int val, struct compat_timespec *utime) @@ -221,7 +222,7 @@ asmlinkage long compat_sys_futex(u32 *uaddr, int op, int val, if ((op == FUTEX_WAIT) && utime) { if (get_compat_timespec(&t, utime)) return -EFAULT; - timeout = timespec_to_jiffies(t) + 1; + timeout = timespec_to_jiffies(&t) + 1; } return do_futex((unsigned long)uaddr, op, val, timeout); } -- cgit v1.2.3 From c290649e47664daaf7ef0b9a50545d7bb121535d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 16 Feb 2003 04:02:37 -0800 Subject: [PATCH] get rid of exec_usermodehelper, replace with call_usermodehelper Urban Widmark points out that modprobe calls system() in many configurations, which drops privs since request_module() doesn't set ruid and rguid. This gets rid of exec_usermodehelper and makes everyone use call_usermodehelper, which has a new "wait" flag. --- arch/i386/mach-voyager/voyager_thread.c | 17 +- drivers/acpi/thermal.c | 2 +- drivers/base/hotplug.c | 2 +- drivers/bluetooth/bt3c_cs.c | 61 +------ drivers/ieee1394/nodemgr.c | 2 +- drivers/input/input.c | 2 +- drivers/isdn/hardware/eicon/divasmain.c | 2 +- drivers/net/hamradio/baycom_epp.c | 45 +----- drivers/pnp/pnpbios/core.c | 2 +- include/linux/kmod.h | 4 +- kernel/kmod.c | 272 +++++++++----------------------- net/bluetooth/hci_core.c | 2 +- net/core/dev.c | 2 +- 13 files changed, 104 insertions(+), 311 deletions(-) (limited to 'kernel') diff --git a/arch/i386/mach-voyager/voyager_thread.c b/arch/i386/mach-voyager/voyager_thread.c index c82afd309f4b..6f74f51992fb 100644 --- a/arch/i386/mach-voyager/voyager_thread.c +++ b/arch/i386/mach-voyager/voyager_thread.c @@ -57,7 +57,7 @@ voyager_thread_start(void) } static int -execute_helper(void *string) +execute(const char *string) { int ret; @@ -74,22 +74,13 @@ execute_helper(void *string) NULL, }; - if((ret = exec_usermodehelper(argv[0], argv, envp)) < 0) { - printk(KERN_ERR "Voyager failed to execute \"%s\"\n", - (char *)string); + if ((ret = call_usermodehelper(argv[0], argv, envp, 1)) != 0) { + printk(KERN_ERR "Voyager failed to run \"%s\": %i\n", + string, ret); } return ret; } -static void -execute(char *string) -{ - if(kernel_thread(execute_helper, (void *)string, CLONE_VFORK | SIGCHLD) < 0) { - printk(KERN_ERR "Voyager failed to fork before exec of \"%s\"\n", - string); - } -} - static void check_from_kernel(void) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7f4cac66fb97..c72f8a1594a3 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -436,7 +436,7 @@ acpi_thermal_call_usermode ( envp[0] = "HOME=/"; envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - call_usermodehelper(argv[0], argv, envp); + call_usermodehelper(argv[0], argv, envp, 0); return_VALUE(0); } diff --git a/drivers/base/hotplug.c b/drivers/base/hotplug.c index 6c66892b23cb..503522d25906 100644 --- a/drivers/base/hotplug.c +++ b/drivers/base/hotplug.c @@ -114,7 +114,7 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action, pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv [0], argv[1], envp[0], envp[1], envp[2], envp[3]); - retval = call_usermodehelper (argv [0], argv, envp); + retval = call_usermodehelper (argv [0], argv, envp, 0); if (retval) pr_debug ("%s - call_usermodehelper returned %d\n", __FUNCTION__, retval); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index de14aeafa20c..b755a362dc11 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -24,18 +24,14 @@ #include #include -#define __KERNEL_SYSCALLS__ - #include #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -405,7 +401,6 @@ void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs) - /* ======================== HCI interface ======================== */ @@ -489,65 +484,23 @@ static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long #define FW_LOADER "/sbin/bluefw" -static int errno; - - -static int bt3c_fw_loader_exec(void *dev) -{ - char *argv[] = { FW_LOADER, "pccard", dev, NULL }; - char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - int err; - - err = exec_usermodehelper(FW_LOADER, argv, envp); - if (err) - printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev); - - return err; -} static int bt3c_firmware_load(bt3c_info_t *info) { - sigset_t tmpsig; char dev[16]; - pid_t pid; - int result; + int err; - /* Check if root fs is mounted */ - if (!current->fs->root) { - printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n"); - return -EPERM; - } + char *argv[] = { FW_LOADER, "pccard", dev, NULL }; + char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; sprintf(dev, "%04x", info->link.io.BasePort1); - pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0); - if (pid < 0) { - printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid); - return pid; - } - - /* Block signals, everything but SIGKILL/SIGSTOP */ - spin_lock_irq(¤t->sighand->siglock); - tmpsig = current->blocked; - siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - result = waitpid(pid, NULL, __WCLONE); - - /* Allow signals again */ - spin_lock_irq(¤t->sighand->siglock); - current->blocked = tmpsig; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (result != pid) { - printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result); - return -result; - } + err = call_usermodehelper(FW_LOADER, argv, envp, 1); + if (err) + printk(KERN_WARNING "bt3c_cs: Failed to run \"%s pccard %s\" (errno=%d).\n", FW_LOADER, dev, err); - return 0; + return err; } diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index ef8a00dc076e..970aa1e09de6 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -786,7 +786,7 @@ static void nodemgr_call_policy(char *verb, struct unit_directory *ud) #ifdef CONFIG_IEEE1394_VERBOSEDEBUG HPSB_DEBUG("NodeMgr: %s %s %016Lx", argv[0], verb, (long long unsigned)ud->ne->guid); #endif - value = call_usermodehelper(argv[0], argv, envp); + value = call_usermodehelper(argv[0], argv, envp, 0); kfree(buf); kfree(envp); if (value != 0) diff --git a/drivers/input/input.c b/drivers/input/input.c index fdfe4150aafa..30796fc98f74 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -383,7 +383,7 @@ static void input_call_hotplug(char *verb, struct input_dev *dev) argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]); #endif - value = call_usermodehelper(argv [0], argv, envp); + value = call_usermodehelper(argv [0], argv, envp, 0); kfree(buf); kfree(envp); diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c index bac79a46bd73..7bc8c445c7cf 100644 --- a/drivers/isdn/hardware/eicon/divasmain.c +++ b/drivers/isdn/hardware/eicon/divasmain.c @@ -263,7 +263,7 @@ static void diva_adapter_trapped(void *context) pdpc->card_failed = 0; argv[2] = &adapter[0]; - ret = call_usermodehelper(argv[0], argv, envp); + ret = call_usermodehelper(argv[0], argv, envp, 0); if (ret) { printk(KERN_ERR diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 93237b2af18a..0842e54df8bd 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -369,15 +369,14 @@ static char eppconfig_path[256] = "/usr/sbin/eppfpga"; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; -static int errno; - -static int exec_eppfpga(void *b) +/* eppconfig: called during ifconfig up to configure the modem */ +static int eppconfig(struct baycom_state *bc) { - struct baycom_state *bc = (struct baycom_state *)b; char modearg[256]; char portarg[16]; - char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, NULL}; - int i; + char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, + NULL }; + int ret; /* set up arguments */ sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", @@ -388,39 +387,7 @@ static int exec_eppfpga(void *b) sprintf(portarg, "%ld", bc->pdev->port->base); printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - i = exec_usermodehelper(eppconfig_path, argv, envp); - if (i < 0) { - printk(KERN_ERR "%s: failed to exec %s -s -p %s -m %s, errno = %d\n", - bc_drvname, eppconfig_path, portarg, modearg, i); - return i; - } - return 0; -} - - -/* eppconfig: called during ifconfig up to configure the modem */ - -static int eppconfig(struct baycom_state *bc) -{ - int i, pid, r; - mm_segment_t fs; - - pid = kernel_thread(exec_eppfpga, bc, CLONE_FS); - if (pid < 0) { - printk(KERN_ERR "%s: fork failed, errno %d\n", bc_drvname, -pid); - return pid; - } - fs = get_fs(); - set_fs(KERNEL_DS); /* Allow i to be in kernel space. */ - r = waitpid(pid, &i, __WCLONE); - set_fs(fs); - if (r != pid) { - printk(KERN_ERR "%s: waitpid(%d) failed, returning %d\n", - bc_drvname, pid, r); - return -1; - } - printk(KERN_DEBUG "%s: eppfpga returned %d\n", bc_drvname, i); - return i; + return call_usermodehelper(eppconfig_path, argv, envp, 1); } /* ---------------------------------------------------------------------- */ diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index 82944c33a021..cde17aa6823b 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -602,7 +602,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) info->location_id, info->serial, info->capabilities); envp[i] = 0; - value = call_usermodehelper (argv [0], argv, envp); + value = call_usermodehelper (argv [0], argv, envp, 0); kfree (buf); kfree (envp); return 0; diff --git a/include/linux/kmod.h b/include/linux/kmod.h index ca24ceb8e2e2..9e9e2c38ab34 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -21,6 +21,7 @@ #include #include +#include #ifdef CONFIG_KMOD extern int request_module(const char * name); @@ -29,8 +30,7 @@ static inline int request_module(const char * name) { return -ENOSYS; } #endif #define try_then_request_module(x, mod) ((x) ?: request_module(mod), (x)) -extern int exec_usermodehelper(char *program_path, char *argv[], char *envp[]); -extern int call_usermodehelper(char *path, char *argv[], char *envp[]); +extern int call_usermodehelper(char *path, char *argv[], char *envp[], int wait); #ifdef CONFIG_HOTPLUG extern char hotplug_path []; diff --git a/kernel/kmod.c b/kernel/kmod.c index 15dacc2e6bd8..a715e06ab4a7 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -14,8 +14,10 @@ Unblock all signals when we exec a usermode process. Shuu Yamaguchi December 2000 -*/ + call_usermodehelper wait flag, and remove exec_usermodehelper. + Rusty Russell Jan 2003 +*/ #define __KERNEL_SYSCALLS__ #include @@ -31,112 +33,11 @@ #include #include #include +#include #include extern int max_threads, system_running; -static inline void -use_init_fs_context(void) -{ - struct fs_struct *our_fs, *init_fs; - struct dentry *root, *pwd; - struct vfsmount *rootmnt, *pwdmnt; - struct namespace *our_ns, *init_ns; - - /* - * Make modprobe's fs context be a copy of init's. - * - * We cannot use the user's fs context, because it - * may have a different root than init. - * Since init was created with CLONE_FS, we can grab - * its fs context from "init_task". - * - * The fs context has to be a copy. If it is shared - * with init, then any chdir() call in modprobe will - * also affect init and the other threads sharing - * init_task's fs context. - * - * We created the exec_modprobe thread without CLONE_FS, - * so we can update the fields in our fs context freely. - */ - - init_fs = init_task.fs; - init_ns = init_task.namespace; - get_namespace(init_ns); - our_ns = current->namespace; - current->namespace = init_ns; - put_namespace(our_ns); - read_lock(&init_fs->lock); - rootmnt = mntget(init_fs->rootmnt); - root = dget(init_fs->root); - pwdmnt = mntget(init_fs->pwdmnt); - pwd = dget(init_fs->pwd); - read_unlock(&init_fs->lock); - - /* FIXME - unsafe ->fs access */ - our_fs = current->fs; - our_fs->umask = init_fs->umask; - set_fs_root(our_fs, rootmnt, root); - set_fs_pwd(our_fs, pwdmnt, pwd); - write_lock(&our_fs->lock); - if (our_fs->altroot) { - struct vfsmount *mnt = our_fs->altrootmnt; - struct dentry *dentry = our_fs->altroot; - our_fs->altrootmnt = NULL; - our_fs->altroot = NULL; - write_unlock(&our_fs->lock); - dput(dentry); - mntput(mnt); - } else - write_unlock(&our_fs->lock); - dput(root); - mntput(rootmnt); - dput(pwd); - mntput(pwdmnt); -} - -int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) -{ - int i; - struct task_struct *curtask = current; - - set_special_pids(1, 1); - - use_init_fs_context(); - - /* Prevent parent user process from sending signals to child. - Otherwise, if the modprobe program does not exist, it might - be possible to get a user defined signal handler to execute - as the super user right after the execve fails if you time - the signal just right. - */ - flush_signals(curtask); - flush_signal_handlers(curtask); - spin_lock_irq(&curtask->sighand->siglock); - sigemptyset(&curtask->blocked); - recalc_sigpending(); - spin_unlock_irq(&curtask->sighand->siglock); - - for (i = 0; i < curtask->files->max_fds; i++ ) { - if (curtask->files->fd[i]) close(i); - } - - switch_uid(INIT_USER); - - /* Give kmod all effective privileges.. */ - curtask->euid = curtask->fsuid = 0; - curtask->egid = curtask->fsgid = 0; - security_task_kmod_set_label(); - - /* Allow execve args to be in kernel space. */ - set_fs(KERNEL_DS); - - /* Go, go, go... */ - if (execve(program_path, argv, envp) < 0) - return -errno; - return 0; -} - #ifdef CONFIG_KMOD /* @@ -144,29 +45,6 @@ int exec_usermodehelper(char *program_path, char *argv[], char *envp[]) */ char modprobe_path[256] = "/sbin/modprobe"; -static int exec_modprobe(void * module_name) -{ - static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - char *argv[] = { modprobe_path, "--", (char*)module_name, NULL }; - int ret; - - if (!system_running) - return -EBUSY; - - ret = exec_usermodehelper(modprobe_path, argv, envp); - if (ret) { - static unsigned long last; - unsigned long now = jiffies; - if (now - last > HZ) { - last = now; - printk(KERN_DEBUG - "kmod: failed to exec %s -s -k %s, errno = %d\n", - modprobe_path, (char*) module_name, errno); - } - } - return ret; -} - /** * request_module - try to load a kernel module * @module_name: Name of module @@ -180,24 +58,18 @@ static int exec_modprobe(void * module_name) * If module auto-loading support is disabled then this function * becomes a no-operation. */ -int request_module(const char * module_name) +int request_module(const char *module_name) { - pid_t pid; - int waitpid_result; - sigset_t tmpsig; - int i, ret; + unsigned int max_modprobes; + int ret; + char *argv[] = { modprobe_path, "--", (char*)module_name, NULL }; + static char *envp[] = { "HOME=/", + "TERM=linux", + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", + NULL }; static atomic_t kmod_concurrent = ATOMIC_INIT(0); #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ static int kmod_loop_msg; - unsigned long saved_policy = current->policy; - - current->policy = SCHED_NORMAL; - /* Don't allow request_module() when the system isn't set up */ - if ( ! system_running ) { - printk(KERN_ERR "request_module[%s]: not ready\n", module_name); - ret = -EPERM; - goto out; - } /* If modprobe needs a service that is in a module, we get a recursive * loop. Limit the number of running kmod threads to max_threads/2 or @@ -207,61 +79,44 @@ int request_module(const char * module_name) * process tables to get the command line, proc_pid_cmdline is static * and it is not worth changing the proc code just to handle this case. * KAO. + * + + * "trace the ppid" is simple, but will fail if someone's + * parent exits. I think this is as good as it gets. --RR */ - i = max_threads/2; - if (i > MAX_KMOD_CONCURRENT) - i = MAX_KMOD_CONCURRENT; + max_modprobes = min(max_threads/2, MAX_KMOD_CONCURRENT); atomic_inc(&kmod_concurrent); - if (atomic_read(&kmod_concurrent) > i) { + if (atomic_read(&kmod_concurrent) > max_modprobes) { + /* We may be blaming an innocent here, but unlikely */ if (kmod_loop_msg++ < 5) printk(KERN_ERR - "kmod: runaway modprobe loop assumed and stopped\n"); + "request_module: runaway loop modprobe %s\n", + module_name); atomic_dec(&kmod_concurrent); - ret = -ENOMEM; - goto out; + return -ENOMEM; } - pid = kernel_thread(exec_modprobe, (void*) module_name, 0); - if (pid < 0) { - printk(KERN_ERR "request_module[%s]: fork failed, errno %d\n", module_name, -pid); - atomic_dec(&kmod_concurrent); - ret = pid; - goto out; + ret = call_usermodehelper(modprobe_path, argv, envp, 1); + if (ret != 0) { + static unsigned long last; + unsigned long now = jiffies; + if (now - last > HZ) { + last = now; + printk(KERN_DEBUG + "request_module: failed %s -- %s. error = %d\n", + modprobe_path, module_name, ret); + } } - - /* Block everything but SIGKILL/SIGSTOP */ - spin_lock_irq(¤t->sighand->siglock); - tmpsig = current->blocked; - siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - waitpid_result = waitpid(pid, NULL, __WCLONE); atomic_dec(&kmod_concurrent); - - /* Allow signals again.. */ - spin_lock_irq(¤t->sighand->siglock); - current->blocked = tmpsig; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (waitpid_result != pid) { - printk(KERN_ERR "request_module[%s]: waitpid(%d,...) failed, errno %d\n", - module_name, pid, -waitpid_result); - } - ret = 0; -out: - current->policy = saved_policy; return ret; } #endif /* CONFIG_KMOD */ - #ifdef CONFIG_HOTPLUG /* hotplug path is set via /proc/sys invoked by hotplug-aware bus drivers, - with exec_usermodehelper and some thread-spawner + with call_usermodehelper argv [0] = hotplug_path; argv [1] = "usb", "scsi", "pci", "network", etc; @@ -285,7 +140,8 @@ struct subprocess_info { char *path; char **argv; char **envp; - pid_t retval; + int wait; + int retval; }; /* @@ -298,13 +154,30 @@ static int ____call_usermodehelper(void *data) retval = -EPERM; if (current->fs->root) - retval = exec_usermodehelper(sub_info->path, sub_info->argv, sub_info->envp); + retval = execve(sub_info->path, sub_info->argv,sub_info->envp); /* Exec failed? */ - sub_info->retval = (pid_t)retval; + 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; + + pid = kernel_thread(____call_usermodehelper, sub_info, + CLONE_VFORK | SIGCHLD); + if (pid < 0) + sub_info->retval = pid; + else + sys_wait4(pid, (unsigned int *)&sub_info->retval, 0, NULL); + + complete(sub_info->complete); + return 0; +} + /* * This is run by keventd. */ @@ -313,14 +186,21 @@ static void __call_usermodehelper(void *data) struct subprocess_info *sub_info = data; pid_t pid; - /* - * CLONE_VFORK: wait until the usermode helper has execve'd successfully - * We need the data structures to stay around until that is done. - */ - pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); - if (pid < 0) + /* CLONE_VFORK: wait until the usermode helper has execve'd + * successfully We need the data structures to stay around + * until that is done. */ + if (sub_info->wait) + pid = kernel_thread(wait_for_helper, sub_info, + CLONE_KERNEL | SIGCHLD); + else + pid = kernel_thread(____call_usermodehelper, sub_info, + CLONE_VFORK | SIGCHLD); + + if (pid < 0) { sub_info->retval = pid; - complete(sub_info->complete); + complete(sub_info->complete); + } else if (!sub_info->wait) + complete(sub_info->complete); } /** @@ -328,15 +208,17 @@ static void __call_usermodehelper(void *data) * @path: pathname for the application * @argv: null-terminated argument list * @envp: null-terminated environment list + * @wait: wait for the application to finish and return status. * - * Runs a user-space application. The application is started asynchronously. It - * runs as a child of keventd. It runs with full root capabilities. keventd silently - * reaps the child when it exits. + * 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 zero on success, else a negative - * error code. + * Must be called from process context. Returns a negative error code + * if program was not execed successfully, or (exitcode << 8 + signal) + * of the application (0 if wait is not set). */ -int call_usermodehelper(char *path, char **argv, char **envp) +int call_usermodehelper(char *path, char **argv, char **envp, int wait) { DECLARE_COMPLETION(done); struct subprocess_info sub_info = { @@ -344,6 +226,7 @@ int call_usermodehelper(char *path, char **argv, char **envp) .path = path, .argv = argv, .envp = envp, + .wait = wait, .retval = 0, }; DECLARE_WORK(work, __call_usermodehelper, &sub_info); @@ -381,7 +264,6 @@ void dev_probe_unlock(void) up(&dev_probe_sem); } -EXPORT_SYMBOL(exec_usermodehelper); EXPORT_SYMBOL(call_usermodehelper); #ifdef CONFIG_KMOD diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7ce528ae021e..eced2f8d69ef 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -114,7 +114,7 @@ static int hci_run_hotplug(char *dev, char *action) envp[3] = astr; envp[4] = NULL; - return call_usermodehelper(argv[0], argv, envp); + return call_usermodehelper(argv[0], argv, envp, 0); } #else #define hci_run_hotplug(A...) diff --git a/net/core/dev.c b/net/core/dev.c index c70d31cea486..5535abdd4c3a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2942,6 +2942,6 @@ static int net_run_sbin_hotplug(struct net_device *dev, char *action) envp [i++] = action_str; envp [i] = 0; - return call_usermodehelper(argv [0], argv, envp); + return call_usermodehelper(argv [0], argv, envp, 0); } #endif -- cgit v1.2.3 From 2c707d9bf1f45ce6cec9eb99e4a288e5aef87929 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 Feb 2003 19:51:37 -0800 Subject: Fix locking for "send_sig_info()", to avoid possible races with signal state changes due to execve() and exit(). We need to hold the tasklist lock to guarantee stability of "task->sighand". --- kernel/signal.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/signal.c b/kernel/signal.c index b8fc6a05c031..b7ac6a557ddb 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1109,21 +1109,31 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) * These are for backward compatibility with the rest of the kernel source. */ +/* + * XXX should probably nix these interfaces and update the kernel + * to specify explicitly whether the signal is a group signal or + * specific to a thread. + */ int send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { int ret; - /* XXX should nix these interfaces and update the kernel */ + /* + * We need the tasklist lock even for the specific + * thread case (when we don't need to follow the group + * lists) in order to avoid races with "p->sighand" + * going away or changing from under us. + */ + read_lock(&tasklist_lock); if (T(sig, SIG_KERNEL_BROADCAST_MASK)) { - read_lock(&tasklist_lock); ret = group_send_sig_info(sig, info, p); - read_unlock(&tasklist_lock); } else { spin_lock_irq(&p->sighand->siglock); ret = specific_send_sig_info(sig, info, p); spin_unlock_irq(&p->sighand->siglock); } + read_unlock(&tasklist_lock); return ret; } -- cgit v1.2.3