summaryrefslogtreecommitdiffstats
path: root/kernel/kmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r--kernel/kmod.c57
1 files changed, 46 insertions, 11 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 7468e4382..ec0d85d32 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -14,14 +14,15 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/unistd.h>
-#include <asm/smp_lock.h>
+#include <linux/smp_lock.h>
+#include <linux/signal.h>
+
#include <asm/uaccess.h>
/*
modprobe_path is set via /proc/sys.
*/
char modprobe_path[256] = "/sbin/modprobe";
-static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
/*
exec_modprobe is spawned from a kernel-mode user process,
@@ -33,20 +34,22 @@ static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
#define task_init task[smp_num_cpus]
static inline void
-use_init_file_context(void) {
+use_init_file_context(void)
+{
lock_kernel();
/* don't use the user's root, use init's root instead */
exit_fs(current); /* current->fs->count--; */
current->fs = task_init->fs;
- current->fs->count++;
+ atomic_inc(&current->fs->count);
unlock_kernel();
}
static int exec_modprobe(void * module_name)
{
- char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};
+ static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL };
int i;
use_init_file_context();
@@ -63,11 +66,21 @@ static int exec_modprobe(void * module_name)
spin_unlock_irq(&current->sigmask_lock);
for (i = 0; i < current->files->max_fds; i++ ) {
- if (current->files->fd[i]) close(i);
+ if (current->files->fd[i]) close(i);
}
- set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */
+ /* Drop the "current user" thing */
+ free_uid(current);
+
+ /* Give kmod all privileges.. */
current->uid = current->euid = current->fsuid = 0;
+ cap_set_full(current->cap_inheritable);
+ cap_set_full(current->cap_effective);
+
+ /* Allow execve args to be in kernel space. */
+ set_fs(KERNEL_DS);
+
+ /* Go, go, go... */
if (execve(modprobe_path, argv, envp) < 0) {
printk(KERN_ERR
"kmod: failed to exec %s -s -k %s, errno = %d\n",
@@ -85,14 +98,36 @@ int request_module(const char * module_name)
{
int pid;
int waitpid_result;
+ sigset_t tmpsig;
+
+ /* Don't allow request_module() before the root fs is mounted! */
+ if ( ! current->fs->root ) {
+ printk(KERN_ERR "request_module[%s]: Root fs not mounted\n",
+ module_name);
+ return -EPERM;
+ }
- pid = kernel_thread(exec_modprobe, (void*) module_name,
- CLONE_FS | SIGCHLD);
+ pid = kernel_thread(exec_modprobe, (void*) module_name, CLONE_FS);
if (pid < 0) {
- printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
+ printk(KERN_ERR "request_module[%s]: fork failed, errno %d\n", module_name, -pid);
return pid;
}
- waitpid_result = waitpid(pid, NULL, 0);
+
+ /* Block everything but SIGKILL/SIGSTOP */
+ spin_lock_irq(&current->sigmask_lock);
+ tmpsig = current->blocked;
+ siginitset(&current->blocked, ~(sigmask(SIGKILL)|sigmask(SIGSTOP)));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ waitpid_result = waitpid(pid, NULL, __WCLONE);
+
+ /* Allow signals again.. */
+ spin_lock_irq(&current->sigmask_lock);
+ current->blocked = tmpsig;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
if (waitpid_result != pid) {
printk (KERN_ERR "kmod: waitpid(%d,NULL,0) failed, returning %d.\n",
pid, waitpid_result);