Testers wanted: exorcised kmod.c: no more daemon!

Adam J. Richter (adam@yggdrasil.com)
Mon, 13 Apr 1998 12:28:04 -0700


The following patch eliminates the kmod deamon by having
request_module() fork and exec modprobe directly. This should
eliminate the timing problems with kmod and hopefully make kmod
a reliable feature for everybody.

Advantages:
o Problems with recursive request_module calls and other
timing issues should now be solved completely.
o kernel/kmod.c is simpler
o The problem with kmod not following chroot's is
automatically solved
o All references to kmod in init/main.c are now deleted,
since there is not daemon to initialize.
o Potential race involving some initially loaded modules
on a boot floppy being automatically unloaded too soon
is solved, because module unloading is done from cron
(or by some other userland method of your choosing),
which is only started once the system is up.
o Related simplifications: kmod process and
/proc/sys/kernel/kmod_unload_time eliminated.

Caveats:
o To get automatic unloading of stale modules, you must
put a line like the following in root's crontab entry:

0-59/5 * * * * /sbin/rmmod -a

o If you want autoloading of modules from anonymous FTP
for some reason, you must make a copy of /sbin/modprobe
and lib/modules/<kernel_version> accessible from the
anonymous FTP area, as you already do with the executables
shared libraries if you want to use them from anonymous
FTP sessions.

Adam J. Richter __ ______________ 4880 Stevens Creek Blvd, Suite 205
adam@yggdrasil.com \ / San Jose, California 95129-1034
+1 408 261-6630 | g g d r a s i l United States of America
fax +1 408 261-6631 "Free Software For The Rest Of Us."
-----------------------CUT HERE----------------------------------------
--- /tmp/linux-2.1.95/Documentation/kmod.txt Tue Mar 10 14:43:13 1998
+++ linux/Documentation/kmod.txt Mon Apr 13 10:31:07 1998
@@ -12,11 +12,12 @@

echo "/sbin/modprobe" > /proc/sys/kernel/modprobe

-To tell kmod when to unload unused modules, do something like:
+To periodically unload unused modules, do put something like the following
+in root's crontab entry:

- echo "120" > /proc/sys/kernel/kmod_unload_delay
+ 0-59/5 * * * * /sbin/rmmod -a

-Kmod only loads and unloads modules. Kerneld could do more (although
+Kmod only loads modules. Kerneld could do more (although
nothing in the standard kernel used its other features). If you
require features such as request_route, we suggest that you take
a similar approach. A simple request_route function could be called,
--- /tmp/linux-2.1.95/Documentation/sysctl/kernel.txt Thu Mar 19 15:04:30 1998
+++ linux/Documentation/sysctl/kernel.txt Mon Apr 13 10:31:11 1998
@@ -25,7 +25,6 @@
- inode-max
- inode-nr
- inode-state
-- kmod_unload_delay ==> Documentation/kmod.txt
- modprobe ==> Documentation/kmod.txt
- osrelease
- ostype
--- /tmp/linux-2.1.95/include/linux/sysctl.h Wed Apr 1 16:19:57 1998
+++ linux/include/linux/sysctl.h Mon Apr 13 10:33:03 1998
@@ -72,7 +72,6 @@
KERN_STATINODE,
KERN_DENTRY, /* dentry statistics */
KERN_MODPROBE,
- KERN_KMOD_UNLOAD_DELAY
};


--- /tmp/linux-2.1.95/init/main.c Wed Apr 8 18:10:01 1998
+++ linux/init/main.c Mon Apr 13 11:53:29 1998
@@ -1162,13 +1162,6 @@
smp_begin();
#endif

-#ifdef CONFIG_KMOD
- {
- extern int kmod_init(void);
- kmod_init();
- }
-#endif
-
#ifdef CONFIG_UMSDOS_FS
{
/*
--- /tmp/linux-2.1.95/kernel/kmod.c Tue Apr 7 08:05:05 1998
+++ linux/kernel/kmod.c Mon Apr 13 12:04:35 1998
@@ -1,6 +1,9 @@
/*
kmod, the new module loader (replaces kerneld)
Kirk Petersen
+
+ Reorganized not to be a daemon by Adam Richter, with guidance
+ from Greg Zornetzer.
*/

#define __KERNEL_SYSCALLS__
@@ -8,142 +11,72 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/unistd.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;
-
-/*
- 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;
-}
-
-/*
- 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)
-{
- int pid;
-
- /*
- Initialize basic thread information
+static void
+security_paranoia(void) {
+ int fd;
+
+ /* 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.
*/
- current->session = 1;
- current->pgrp = 1;
- sprintf(current->comm, "kmod");
+ spin_lock_irq(&current->sigmask_lock);
sigfillset(&current->blocked);
+ spin_unlock_irq(&current->sigmask_lock);

- /*
- 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);
-
- /*
- 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);
- }
- }
+ /* Prevent open file descriptors from being passed to privileged
+ modprobe program. With the stock modprobe, the only problem
+ is the potential annoyance of an error message being inserted
+ into a program's stderr. However, we want to be safe for
+ customized modprobe replacements as well. -adam@yggdrasil.com */
+ for (fd = 0; fd < current->files->max_fds; fd++ ) {
+ close(fd);
}
-
- 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)
+static int exec_modprobe(void * module_name)
{
- /*
- 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
- */
- 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");
-
- kernel_thread(kmod_thread, NULL, 0);
-
- 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);
+ char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};

+ security_paranoia();
+ set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */
+ current->uid = current->euid = 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, 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;
}
--- /tmp/linux-2.1.95/kernel/sysctl.c Wed Apr 1 16:26:35 1998
+++ linux/kernel/sysctl.c Mon Apr 13 10:33:08 1998
@@ -43,7 +43,6 @@
extern int sysctl_overcommit_memory;
#ifdef CONFIG_KMOD
extern char modprobe_path[];
-extern int kmod_unload_delay;
#endif
#ifdef CONFIG_CHR_DEV_SG
extern int sg_big_buff;
@@ -180,8 +179,6 @@
#ifdef CONFIG_KMOD
{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
0644, NULL, &proc_dostring, &sysctl_string },
- {KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay,
- sizeof(int), 0644, NULL, &proc_dointvec},
#endif
#ifdef CONFIG_CHR_DEV_SG
{KERN_NRFILE, "sg-big-buff", &sg_big_buff, sizeof (int),

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu