sil2100//vx developer log

Welcome to my personal web page

In-kernel module unloading and the usermode helper

It seems safely unloading a Linux kernel module in kernel space is not a very straightforward task, but once you see how it's done, it seems awfully trivial - and strange. Those of you that had some kernel programming experience before probably know about the request_module() and its non blocking version request_module_nowait() functions declared in include/linux/kmod.h. Just provide the name of the module you want to load as the parameter and the function takes care of the rest. Sadly, there is no such function for loaded module removal.



There are many tools that could be used for this purpose in the Linux kernel, but none of them are explicitly exported for us to use.

But if we look into the __request_module function internals (the base for both the request_module functions) in kernel/kmod.c, we can see that it actually doesn't do anything with the module at all. All it does is run modprobe (yes, the one from userspace) to do the module loading instead - at least in versions 2.6.28 and .31. It does this using a usermode helper, with the call_usermodehelper() function. Its syntax is similar to the execve() user mode function - requiring the binary path, a NULL-terminated string array of arguments, a NULL-terminated string array of environment variables and a flag indicating the wait policy. The wait policy defines whether the caller should wait for the process to finish (UMH_WAIT_PROC), just wait for the exec call to finish (UMH_WAIT_EXEC) or not to wait at all (UMH_NO_WAIT).

Now that we know how request_module() works, we can do the same thing when we need to remove a module by name. We can either do a modprobe -r or a rmmod call. This way, we're not unloading modules entirely by force, and it's safe for the operating system. This can be done, for instance, like this:

static char *argv[] = { "/sbin/rmmod", "my_module", NULL }, 
            *env[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
if (call_usermodehelper(argv[0], argv, env, UMH_WAIT_PROC))
    PRINTD(KERN_WARNING "Failed unloading the module\n");

Just remember that with just that we cannot actually tell if the module has been unloaded or not. This requires additional checking done by ourselves later on.
You can of course use usermode helpers to call any other user mode applications when needed. Just use it with care. It's best if the kernel doesn't ask for help from user space. Kernel is strong.