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
- Lines: 163
- Date:
Wed Jun 7 14:26:44 2000
- Orig file:
v2.2.15/linux/kernel/kmod.c
- Orig date:
Tue Jan 4 10:12:25 2000
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(¤t->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(¤t->sigmask_lock);
waitpid_result = waitpid(pid, NULL, __WCLONE);
+ atomic_dec(&kmod_concurrent);
/* Allow signals again.. */
spin_lock_irq(¤t->sigmask_lock);
@@ -130,8 +180,8 @@
spin_unlock_irq(¤t->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)