patch-2.2.16 linux/kernel/kmod.c

Next file: linux/kernel/ksyms.c
Previous file: linux/kernel/capability.c
Back to the patch index
Back to the overall index

diff -urN v2.2.15/linux/kernel/kmod.c linux/kernel/kmod.c
@@ -7,6 +7,9 @@
 
 	Modified to avoid chroot and file sharing problems.
 	Mikael Pettersson
+
+	Back port check for modprobe loops from 2.3.
+	Keith Owens <kaos@ocs.com.au> May 2000
 */
 
 #define __KERNEL_SYSCALLS__
@@ -41,16 +44,20 @@
 	dput(fs->pwd);
 	fs->root = dget(init_task.fs->root);
 	fs->pwd = dget(init_task.fs->pwd);
+	fs->umask = 0022;
 
 	unlock_kernel();
 }
 
-static int exec_modprobe(void * module_name)
+int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
 {
-	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;
 
+	current->session = 1;
+	current->pgrp = 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
@@ -62,10 +69,6 @@
 	flush_signal_handlers(current);
 	spin_unlock_irq(&current->sigmask_lock);
 
-	/* Copy root dir and cwd from init */
-	use_init_fs_context();
-
-	/* Close our copies of user's open files */
 	for (i = 0; i < current->files->max_fds; i++ ) {
 		if (current->files->fd[i]) close(i);
 	}
@@ -73,33 +76,57 @@
 	/* Drop the "current user" thing */
 	free_uid(current);
 
-	/* Give kmod all privileges.. */
+	/* Give kmod all effective 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) {
+	if (execve(program_path, argv, envp) < 0)
+		return -errno;
+	return 0;
+}
+
+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, "-s", "-k", (char*)module_name, NULL };
+	int ret;
+
+	ret = exec_usermodehelper(modprobe_path, argv, envp);
+	if (ret) {
 		printk(KERN_ERR
 		       "kmod: failed to exec %s -s -k %s, errno = %d\n",
 		       modprobe_path, (char*) module_name, errno);
-		return -errno;
 	}
-	return 0;
+	return ret;
 }
 
-/*
-	request_module: the function that everyone calls when they need
-	a module.
-*/
+/**
+ *	request_module - try to load a kernel module
+ *	@module_name: Name of module
+ *
+ * 	Load a module using the user mode module loader. The function returns
+ *	zero on success or a negative errno code on failure. Note that a
+ * 	successful module load does not mean the module did not then unload
+ *	and exit on an error of its own. Callers must check that the service
+ *	they requested is now available not blindly invoke it.
+ *
+ *	If module auto-loading support is disabled then this function
+ *	becomes a no-operation.
+ */
+ 
 int request_module(const char * module_name)
 {
 	int pid;
 	int waitpid_result;
 	sigset_t tmpsig;
+	int i;
+	static atomic_t kmod_concurrent = ATOMIC_INIT(0);
+#define MAX_KMOD_CONCURRENT 50	/* Completely arbitrary value - KAO */
+	static int kmod_loop_msg;
 
 	/* Don't allow request_module() before the root fs is mounted!  */
 	if ( ! current->fs->root ) {
@@ -108,9 +135,31 @@
 		return -EPERM;
 	}
 
+	/* If modprobe needs a service that is in a module, we get a recursive
+	 * loop.  Limit the number of running kmod threads to NR_TASKS/2 or
+	 * MAX_KMOD_CONCURRENT, whichever is the smaller.  A cleaner method
+	 * would be to run the parents of this process, counting how many times
+	 * kmod was invoked.  That would mean accessing the internals of the
+	 * 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.
+	 */
+	i = NR_TASKS/2;
+	if (i > MAX_KMOD_CONCURRENT)
+		i = MAX_KMOD_CONCURRENT;
+	atomic_inc(&kmod_concurrent);
+	if (atomic_read(&kmod_concurrent) > i) {
+		if (kmod_loop_msg++ < 5)
+			printk(KERN_ERR
+			       "kmod: runaway modprobe loop assumed and stopped\n");
+		atomic_dec(&kmod_concurrent);
+		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);
 		return pid;
 	}
 
@@ -122,6 +171,7 @@
 	spin_unlock_irq(&current->sigmask_lock);
 
 	waitpid_result = waitpid(pid, NULL, __WCLONE);
+	atomic_dec(&kmod_concurrent);
 
 	/* Allow signals again.. */
 	spin_lock_irq(&current->sigmask_lock);
@@ -130,8 +180,8 @@
 	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);
+		printk(KERN_ERR "request_module[%s]: waitpid(%d,...) failed, errno %d\n",
+		       module_name, pid, -waitpid_result);
 	}
 	return 0;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)