patch-2.1.99 linux/kernel/kmod.c

Next file: linux/kernel/sysctl.c
Previous file: linux/init/main.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.98/linux/kernel/kmod.c linux/kernel/kmod.c
@@ -1,6 +1,12 @@
 /*
 	kmod, the new module loader (replaces kerneld)
 	Kirk Petersen
+
+	Reorganized not to be a daemon by Adam Richter, with guidance
+	from Greg Zornetzer.
+
+	Modified to avoid chroot and file sharing problems.
+	Mikael Pettersson
 */
 
 #define __KERNEL_SYSCALLS__
@@ -8,147 +14,88 @@
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/unistd.h>
+#include <asm/smp_lock.h>
+#include <asm/uaccess.h>
 
 /*
-	kmod_unload_delay and modprobe_path are set via /proc/sys.
+	modprobe_path is set via /proc/sys.
 */
-int kmod_unload_delay = 60;
 char modprobe_path[256] = "/sbin/modprobe";
-static char module_name[64] = "";
-static char * argv[] = { modprobe_path, "-s", "-k", module_name, NULL };
 static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
 
 /*
-	kmod_queue synchronizes the kmod thread and the rest of the system
-	kmod_unload_timer is what we use to unload modules
-	after kmod_unload_delay seconds
-*/
-static struct wait_queue * kmod_queue = NULL;
-static struct timer_list kmod_unload_timer;
+	exec_modprobe is spawned from a kernel-mode user process,
+	then changes its state to behave _as_if_ it was spawned
+	from the kernel's init process
+	(ppid and {e,}gid are not adjusted, but that shouldn't
+	be a problem since we trust modprobe)
+*/
+#define task_init task[smp_num_cpus]
+
+static inline 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++;
 
-/*
-	It is not easy to implement a full fork in kernel-space on some
-	systems (Alpha), and it is not necessary for us here.  This is 
-	a new thread that does the exec.
-*/
-static int kmod_exec_modprobe(void * data)
-{
-	sigemptyset(&current->blocked);
-	execve(modprobe_path, argv, envp);
-	printk(KERN_ERR "kmod: failed to load module %s\n", module_name);
-	return 0;
+	unlock_kernel();
 }
 
-/*
-	kmod_thread is the thread that does most of the work.  kmod_unload and
-	request_module tell it to wake up and do work.
-*/
-static int kmod_thread(void * data)
+static int exec_modprobe(void * module_name)
 {
-	int pid;
-
-	/*
-		Initialize basic thread information
-	*/
-	current->session = 1;
-	current->pgrp = 1;
-	sprintf(current->comm, "kmod");
-	sigfillset(&current->blocked);
-
-	/*
-		This is the main kmod_thread loop.  It first sleeps, then
-		handles requests from request_module or kmod_unload.
-	*/
-
-	while (1) {
-		interruptible_sleep_on(&kmod_queue);
+	char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};
+	int i;
 
-		/*
-			If request_module woke us up, we should try to
-			load module_name.  If not, kmod_unload woke us up,
-			do call delete_module.
-			(if somehow both want us to do something, ignore the
-			 delete_module request)
-		*/
-		if (module_name[0] == '\0') {
-			delete_module(NULL);
-		} else {
-			pid = kernel_thread(kmod_exec_modprobe, NULL, SIGCHLD);
-			if (pid > 0) {
-				waitpid(pid, NULL, 0);
-				module_name[0] = '\0';
-				wake_up(&kmod_queue);
-			} else {
-				printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
-			}
-		}
-	}
+	use_init_file_context();
 
-	return 0;	/* Never reached. */
-}
-
-/*
-	kmod_unload is the function that the kernel calls when
-	the kmod_unload_timer expires
-*/
-void kmod_unload(unsigned long x)
-{
-	/*
-		wake up the kmod thread, which does the work
-		(we can't call delete_module, as it locks the kernel and
-		 we are in the bottom half of the kernel (right?))
-		once it is awake, reset the timer
+	/* 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.
 	*/
-	wake_up(&kmod_queue);
-	kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ);
-	add_timer(&kmod_unload_timer);
-}
-
-int kmod_init(void)
-{
-	printk("Starting kmod\n");
+	spin_lock_irq(&current->sigmask_lock);
+	flush_signals(current);
+	flush_signal_handlers(current);
+	spin_unlock_irq(&current->sigmask_lock);
 
-	/*
-	 * CLONE_FS means that our "cwd" will follow that of init.
-	 * CLONE_FILES just saves some space (we don't need any
-	 * new file descriptors). Ditto for CLONE_SIGHAND.
-	 */
-	kernel_thread(kmod_thread, NULL, CLONE_FILES | CLONE_FS | CLONE_SIGHAND);
-
-	kmod_unload_timer.next = NULL;
-	kmod_unload_timer.prev = NULL;
-	kmod_unload_timer.expires = jiffies + (5 * 60 * HZ);
-	kmod_unload_timer.data = 0L;
-	kmod_unload_timer.function = kmod_unload;
-	add_timer(&kmod_unload_timer);
+	for (i = 0; i < current->files->max_fds; i++ ) {
+	    if (current->files->fd[i]) close(i);
+	}
 
+	set_fs(KERNEL_DS);	/* Allow execve args to be in kernel space. */
+	current->uid = current->euid = current->fsuid = 0;
+	if (execve(modprobe_path, argv, envp) < 0) {
+		printk(KERN_ERR
+		       "kmod: failed to exec %s -s -k %s, errno = %d\n",
+		       modprobe_path, (char*) module_name, errno);
+		return -errno;
+	}
 	return 0;
 }
 
 /*
-	request_module, the function that everyone calls when they need a
-	module to be loaded
+	request_module: the function that everyone calls when they need
+	a module.
 */
-int request_module(const char * name)
+int request_module(const char * module_name)
 {
-	/* first, copy the name of the module into module_name */
-	/* then wake_up() the kmod daemon */
-	/* wait for the kmod daemon to finish (it will wake us up) */
-
-	/*
-		kmod_thread is sleeping, so start by copying the name of
-		the module into module_name.  Once that is done, wake up
-		kmod_thread.
-	*/
-	strncpy(module_name, name, sizeof(module_name));
-	module_name[sizeof(module_name)-1] = '\0';
-	wake_up(&kmod_queue);
-
-	/*
-		Now that we have told kmod_thread what to do, we want to
-		go to sleep and let it do its work.  It will wake us up,
-		at which point we will be done (the module will be loaded).
-	*/
-	interruptible_sleep_on(&kmod_queue);
+	int pid;
+	int waitpid_result;
+
+	pid = kernel_thread(exec_modprobe, (void*) module_name,
+			    CLONE_FS | SIGCHLD);
+	if (pid < 0) {
+		printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
+		return pid;
+	}
+	waitpid_result = waitpid(pid, NULL, 0);
+	if (waitpid_result != pid) {
+		printk (KERN_ERR "kmod: waitpid(%d,NULL,0) failed, returning %d.\n",
+			pid, waitpid_result);
+	}
 	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov