final(?) no-daemon kmod diffs for linux-2.1.96

Adam J. Richter (adam@yggdrasil.com)
Wed, 15 Apr 1998 12:32:48 -0700


This is the same "final" daemonsless kmod that I posted yesterday
afternoon, except that the diffs are against 2.1.96 instead of 2.1.95,
and I have updated the documentation to mention that modprobe must be
owned by root and only writable by root.

While the changes to the create_thread call in kernel/kmod.c
that appeared in 2.1.96 cleverly solve the problem of chrooting after
the ramdisk terminates, they do not solve the problem for ramdisks
that do not exit, but rather do "exec chroot /mnt /sbin/init".
Also, a more common problem that would still remain with the stock
2.1.96 kmod is that it will not work for recursive request_module()
calls, such as the parallel port probes described by Greg Zornester,
or a CD writer attached to a scsi controller on another otherwise IDE-only
system (a configuration we happen to use on one machine). There are
probably a number of other situations where this problem occurs, and
as we move toward more modularization and wider use of automatic module
loading, you can bet this will become a more frequent need. The
daemonless kmod solves these problems and allow more flexible
module unloading policies by moving automatic module unloading to
userland.

So, far nobody has reported any problems and a number of people
have reported success. Remember to put a line like the following in
root's crontab if you want automatic unloading of modules:

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

Also, make sure that /sbin/modprobe is owned by root and
not writable by group or other. Otherwise, kmod will refuse to
exec modprobe.

Anyhow, here are the diffs. The only change is that these
are relative to 2.1.96 to make it easier for people who are now running
2.1.96 to try this code. If I do not get any negative reports by
late this evening, I will start the ball rolling on requesting
inclusion of this change in the main kernel distribution.

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.96/Documentation/kmod.txt Tue Mar 10 14:43:13 1998
+++ linux/Documentation/kmod.txt Wed Apr 15 12:29:09 1998
@@ -4,19 +4,20 @@
Kmod is a simple replacement for kerneld. It consists of a
request_module() replacement and a kernel thread called kmod. When the
kernel requests a module, the kmod wakes up and execve()s modprobe,
-passing it the name that was requested. After a configurable period of
-time, kmod will have delete_module() remove any unused modules.
+passing it the name that was requested. For security reasons,
+modprobe must be owned by root and not writable by anyone else.

Kmod is configurable through two entries in /proc/sys/kernel. You can
set the path of modprobe (where the kernel looks for it) by doing:

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.96/Documentation/sysctl/kernel.txt Thu Mar 19 15:04:30 1998
+++ linux/Documentation/sysctl/kernel.txt Wed Apr 15 09:45:45 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.96/include/linux/sysctl.h Sat Apr 11 17:18:15 1998
+++ linux/include/linux/sysctl.h Wed Apr 15 09:48:59 1998
@@ -72,7 +72,6 @@
KERN_STATINODE,
KERN_DENTRY, /* dentry statistics */
KERN_MODPROBE,
- KERN_KMOD_UNLOAD_DELAY
};


--- /tmp/linux-2.1.96/init/main.c Mon Apr 13 18:11:17 1998
+++ linux/init/main.c Wed Apr 15 09:49:04 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.96/kernel/kmod.c Mon Apr 13 18:11:38 1998
+++ linux/kernel/kmod.c Wed Apr 15 09:49:05 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,147 +11,82 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/unistd.h>
+#include <linux/stat.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");
-
- /*
- * 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);
-
+ char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};
+ struct stat statbuf;
+ int stat_result;
+ asmlinkage int sys_newstat(char * filename, struct stat * statbuf);
+
+ security_paranoia();
+ set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */
+ if ((stat_result = sys_newstat(modprobe_path, &statbuf)) < 0) {
+ return stat_result;
+ }
+ if (statbuf.st_uid != 0 || (statbuf.st_mode & (S_IWGRP|S_IWOTH))) {
+ return -EPERM;
+ }
+ 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.96/kernel/sysctl.c Wed Apr 1 16:26:35 1998
+++ linux/kernel/sysctl.c Wed Apr 15 09:49:05 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