Module Name: src
Committed By: pooka
Date: Fri Mar 5 18:35:02 UTC 2010
Modified Files:
src/sys/kern: kern_module.c sys_module.c
src/sys/sys: module.h
Log Message:
Move builtin modules to a list in init and load them from there
instead of using linksets directly. This has two implications:
1) It is now possible to "unload" a builtin module provided it is
not busy. This is useful e.g. to disable a kernel feature as
an immediate workaround to a security problem. To re-initialize
the module, modload -f <name> is required.
2) It is possible to use builtin modules which were linked at
runtime with an external linker (dlopen + rump).
To generate a diff of this commit:
cvs rdiff -u -r1.58 -r1.59 src/sys/kern/kern_module.c
cvs rdiff -u -r1.10 -r1.11 src/sys/kern/sys_module.c
cvs rdiff -u -r1.18 -r1.19 src/sys/sys/module.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/kern_module.c
diff -u src/sys/kern/kern_module.c:1.58 src/sys/kern/kern_module.c:1.59
--- src/sys/kern/kern_module.c:1.58 Wed Mar 3 17:58:36 2010
+++ src/sys/kern/kern_module.c Fri Mar 5 18:35:01 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_module.c,v 1.58 2010/03/03 17:58:36 pooka Exp $ */
+/* $NetBSD: kern_module.c,v 1.59 2010/03/05 18:35:01 pooka Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.58 2010/03/03 17:58:36 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.59 2010/03/05 18:35:01 pooka Exp $");
#define _MODULE_INTERNAL
@@ -63,12 +63,15 @@
struct vm_map *module_map;
char module_base[MODULE_BASE_SIZE];
-struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list);
-struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist);
+struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list);
+struct modlist module_builtins = TAILQ_HEAD_INITIALIZER(module_builtins);
+static struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist);
+
static module_t *module_active;
static int module_verbose_on;
static int module_autoload_on = 1;
u_int module_count;
+u_int module_builtinlist;
kmutex_t module_lock;
u_int module_autotime = 10;
u_int module_gen = 1;
@@ -155,6 +158,141 @@
}
/*
+ * Add modules to the builtin list. This can done at boottime or
+ * at runtime if the module is linked into the kernel with an
+ * external linker. All or none of the input will be handled.
+ * Optionally, the modules can be initialized. If they are not
+ * initialized, module_init_class() or module_load() can be used
+ * later, but these are not guaranteed to give atomic results.
+ */
+int
+module_builtin_add(modinfo_t *const *mip, size_t nmodinfo, bool init)
+{
+ struct module **modp = NULL, *mod_iter;
+ int rv = 0, i, mipskip;
+
+ if (init) {
+ rv = kauth_authorize_system(kauth_cred_get(),
+ KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_LOAD,
+ (void *)(uintptr_t)1, NULL);
+ if (rv) {
+ return rv;
+ }
+ }
+
+ for (i = 0, mipskip = 0; i < nmodinfo; i++) {
+ if (mip[i] == &module_dummy) {
+ KASSERT(nmodinfo > 0);
+ nmodinfo--;
+ }
+ }
+ if (nmodinfo == 0)
+ return 0;
+
+ modp = kmem_zalloc(sizeof(*modp) * nmodinfo, KM_SLEEP);
+ for (i = 0, mipskip = 0; i < nmodinfo; i++) {
+ if (mip[i+mipskip] == &module_dummy) {
+ mipskip++;
+ continue;
+ }
+ modp[i] = kmem_zalloc(sizeof(*modp[i]), KM_SLEEP);
+ modp[i]->mod_info = mip[i+mipskip];
+ modp[i]->mod_source = MODULE_SOURCE_KERNEL;
+ }
+ mutex_enter(&module_lock);
+
+ /* do this in three stages for error recovery and atomicity */
+
+ /* first check for presence */
+ for (i = 0; i < nmodinfo; i++) {
+ TAILQ_FOREACH(mod_iter, &module_builtins, mod_chain) {
+ if (strcmp(mod_iter->mod_info->mi_name,
+ modp[i]->mod_info->mi_name) == 0)
+ break;
+ }
+ if (mod_iter) {
+ rv = EEXIST;
+ goto out;
+ }
+
+ if (module_lookup(modp[i]->mod_info->mi_name) != NULL) {
+ rv = EEXIST;
+ goto out;
+ }
+ }
+
+ /* then add to list */
+ for (i = 0; i < nmodinfo; i++) {
+ TAILQ_INSERT_TAIL(&module_builtins, modp[i], mod_chain);
+ module_builtinlist++;
+ }
+
+ /* finally, init (if required) */
+ if (init) {
+ for (i = 0; i < nmodinfo; i++) {
+ rv = module_do_builtin(modp[i]->mod_info->mi_name,NULL);
+ /* throw in the towel, recovery hard & not worth it */
+ if (rv)
+ panic("builtin module \"%s\" init failed: %d",
+ modp[i]->mod_info->mi_name, rv);
+ }
+ }
+
+ out:
+ mutex_exit(&module_lock);
+ if (rv != 0) {
+ for (i = 0; i < nmodinfo; i++) {
+ if (modp[i])
+ kmem_free(modp[i], sizeof(*modp[i]));
+ }
+ }
+ kmem_free(modp, sizeof(*modp) * nmodinfo);
+ return rv;
+}
+
+/*
+ * Optionally fini and remove builtin module from the kernel.
+ * Note: the module will now be unreachable except via mi && builtin_add.
+ */
+int
+module_builtin_remove(modinfo_t *mi, bool fini)
+{
+ struct module *mod;
+ int rv = 0;
+
+ if (fini) {
+ rv = kauth_authorize_system(kauth_cred_get(),
+ KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_UNLOAD,
+ NULL, NULL);
+ if (rv)
+ return rv;
+
+ mutex_enter(&module_lock);
+ rv = module_do_unload(mi->mi_name);
+ if (rv) {
+ goto out;
+ }
+ } else {
+ mutex_enter(&module_lock);
+ }
+ TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
+ if (strcmp(mod->mod_info->mi_name, mi->mi_name) == 0)
+ break;
+ }
+ if (mod) {
+ TAILQ_REMOVE(&module_builtins, mod, mod_chain);
+ module_builtinlist--;
+ } else {
+ KASSERT(fini == false);
+ rv = ENOENT;
+ }
+
+ out:
+ mutex_exit(&module_lock);
+ return rv;
+}
+
+/*
* module_init:
*
* Initialize the module subsystem.
@@ -162,7 +300,10 @@
void
module_init(void)
{
+ __link_set_decl(modules, modinfo_t);
extern struct vm_map *module_map;
+ modinfo_t *const *mip;
+ int rv;
if (module_map == NULL) {
module_map = kernel_map;
@@ -185,6 +326,12 @@
module_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
module_listener_cb, NULL);
+
+ __link_set_foreach(mip, modules) {
+ if ((rv = module_builtin_add(mip, 1, false) != 0))
+ module_error("builtin %s failed: %d\n",
+ (*mip)->mi_name, rv);
+ }
}
/*
@@ -245,34 +392,32 @@
void
module_init_class(modclass_t class)
{
- __link_set_decl(modules, modinfo_t);
- modinfo_t *const *mip, *mi;
- module_t *mod, *mod_next;
+ module_t *mod;
+ modinfo_t *mi;
mutex_enter(&module_lock);
/*
- * Builtins first. These can't depend on pre-loaded modules.
+ * Builtins first. These will not depend on pre-loaded modules
+ * (because the kernel would not link).
*/
- __link_set_foreach(mip, modules) {
- mi = *mip;
- if (mi == &module_dummy) {
- continue;
- }
- if (class != MODULE_CLASS_ANY && class != mi->mi_class) {
- continue;
+ do {
+ TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
+ mi = mod->mod_info;
+ if (class != MODULE_CLASS_ANY && class != mi->mi_class)
+ continue;
+ (void)module_do_builtin(mi->mi_name, NULL);
+ break;
}
- (void)module_do_builtin(mi->mi_name, NULL);
- }
+ } while (mod != NULL);
+
/*
* Now preloaded modules. These will be pulled off the
* list as we call module_do_load();
*/
do {
- for (mod = TAILQ_FIRST(&module_bootlist); mod; mod = mod_next) {
- mod_next = TAILQ_NEXT(mod, mod_chain);
+ TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
mi = mod->mod_info;
- if (class != MODULE_CLASS_ANY &&
- class != mi->mi_class)
+ if (class != MODULE_CLASS_ANY && class != mi->mi_class)
continue;
module_do_load(mi->mi_name, false, 0, NULL, NULL,
class, false);
@@ -485,63 +630,49 @@
/*
* module_do_builtin:
*
- * Initialize a single module from the list of modules that are
- * built into the kernel (linked into the kernel image).
+ * Initialize a module from the list of modules that are
+ * already linked into the kernel.
*/
static int
module_do_builtin(const char *name, module_t **modp)
{
- __link_set_decl(modules, modinfo_t);
- modinfo_t *const *mip;
const char *p, *s;
char buf[MAXMODNAME];
- modinfo_t *mi;
- module_t *mod, *mod2;
+ modinfo_t *mi = NULL;
+ module_t *mod, *mod2, *mod_loaded;
size_t len;
int error;
KASSERT(mutex_owned(&module_lock));
/*
- * Check to see if already loaded.
+ * Search the list to see if we have a module by this name.
*/
- if ((mod = module_lookup(name)) != NULL) {
- if (modp != NULL) {
- *modp = mod;
+ TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
+ if (strcmp(mod->mod_info->mi_name, name) == 0) {
+ mi = mod->mod_info;
+ break;
}
- return 0;
}
/*
- * Search the list to see if we have a module by this name.
+ * Check to see if already loaded. This might happen if we
+ * were already loaded as a dependency.
*/
- error = ENOENT;
- __link_set_foreach(mip, modules) {
- mi = *mip;
- if (mi == &module_dummy) {
- continue;
- }
- if (strcmp(mi->mi_name, name) == 0) {
- error = 0;
- break;
- }
- }
- if (error != 0) {
- module_error("can't find `%s'", name);
- return error;
+ if ((mod_loaded = module_lookup(name)) != NULL) {
+ KASSERT(mod == NULL);
+ if (modp)
+ *modp = mod_loaded;
+ return 0;
}
+ /* Note! This is from TAILQ, not immediate above */
+ if (mi == NULL)
+ panic("can't find `%s'", name);
+
/*
* Initialize pre-requisites.
*/
- mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
- if (mod == NULL) {
- module_error("out of memory for `%s'", name);
- return ENOMEM;
- }
- if (modp != NULL) {
- *modp = mod;
- }
if (mi->mi_required != NULL) {
for (s = mi->mi_required; *s != '\0'; s = p) {
if (*s == ',')
@@ -555,12 +686,10 @@
break;
if (mod->mod_nrequired == MAXMODDEPS - 1) {
module_error("too many required modules");
- kmem_free(mod, sizeof(*mod));
return EINVAL;
}
error = module_do_builtin(buf, &mod2);
if (error != 0) {
- kmem_free(mod, sizeof(*mod));
return error;
}
mod->mod_required[mod->mod_nrequired++] = mod2;
@@ -577,13 +706,18 @@
if (error != 0) {
module_error("builtin module `%s' "
"failed to init", mi->mi_name);
- kmem_free(mod, sizeof(*mod));
return error;
}
+
+ /* load always succeeds after this point */
+
+ TAILQ_REMOVE(&module_builtins, mod, mod_chain);
+ module_builtinlist--;
+ if (modp != NULL) {
+ *modp = mod;
+ }
if (mi->mi_class == MODULE_CLASS_SECMODEL)
secmodel_register();
- mod->mod_info = mi;
- mod->mod_source = MODULE_SOURCE_KERNEL;
module_enqueue(mod);
return 0;
}
@@ -625,6 +759,27 @@
}
/*
+ * Search the list of disabled builtins first.
+ */
+ TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
+ if (strcmp(mod->mod_info->mi_name, name) == 0) {
+ break;
+ }
+ }
+ if (mod) {
+ if ((flags & MODCTL_LOAD_FORCE) == 0) {
+ module_error("use -f to reinstate "
+ "builtin module \"%s\"", name);
+ depth--;
+ return EPERM;
+ } else {
+ error = module_do_builtin(name, NULL);
+ depth--;
+ return error;
+ }
+ }
+
+ /*
* Load the module and link. Before going to the file system,
* scan the list of modules loaded by the boot loader.
*/
@@ -876,7 +1031,7 @@
module_error("module `%s' not found", name);
return ENOENT;
}
- if (mod->mod_refcnt != 0 || mod->mod_source == MODULE_SOURCE_KERNEL) {
+ if (mod->mod_refcnt != 0) {
module_print("module `%s' busy", name);
return EBUSY;
}
@@ -899,7 +1054,13 @@
if (mod->mod_kobj != NULL) {
kobj_unload(mod->mod_kobj);
}
- kmem_free(mod, sizeof(*mod));
+ if (mod->mod_source == MODULE_SOURCE_KERNEL) {
+ mod->mod_nrequired = 0; /* will be re-parsed */
+ TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain);
+ module_builtinlist++;
+ } else {
+ kmem_free(mod, sizeof(*mod));
+ }
module_gen++;
return 0;
Index: src/sys/kern/sys_module.c
diff -u src/sys/kern/sys_module.c:1.10 src/sys/kern/sys_module.c:1.11
--- src/sys/kern/sys_module.c:1.10 Fri Oct 16 00:27:07 2009
+++ src/sys/kern/sys_module.c Fri Mar 5 18:35:01 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: sys_module.c,v 1.10 2009/10/16 00:27:07 jnemeth Exp $ */
+/* $NetBSD: sys_module.c,v 1.11 2010/03/05 18:35:01 pooka Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.10 2009/10/16 00:27:07 jnemeth Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_module.c,v 1.11 2010/03/05 18:35:01 pooka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -145,7 +145,7 @@
break;
}
mutex_enter(&module_lock);
- mslen = (module_count + 1) * sizeof(modstat_t);
+ mslen = (module_count+module_builtinlist+1) * sizeof(modstat_t);
mso = kmem_zalloc(mslen, KM_SLEEP);
if (mso == NULL) {
mutex_exit(&module_lock);
@@ -169,6 +169,24 @@
ms->ms_source = mod->mod_source;
ms++;
}
+ TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
+ mi = mod->mod_info;
+ strlcpy(ms->ms_name, mi->mi_name, sizeof(ms->ms_name));
+ if (mi->mi_required != NULL) {
+ strlcpy(ms->ms_required, mi->mi_required,
+ sizeof(ms->ms_required));
+ }
+ if (mod->mod_kobj != NULL) {
+ kobj_stat(mod->mod_kobj, &addr, &size);
+ ms->ms_addr = addr;
+ ms->ms_size = size;
+ }
+ ms->ms_class = mi->mi_class;
+ ms->ms_refcnt = -1;
+ KASSERT(mod->mod_source == MODULE_SOURCE_KERNEL);
+ ms->ms_source = mod->mod_source;
+ ms++;
+ }
mutex_exit(&module_lock);
error = copyout(mso, iov.iov_base,
min(mslen - sizeof(modstat_t), iov.iov_len));
Index: src/sys/sys/module.h
diff -u src/sys/sys/module.h:1.18 src/sys/sys/module.h:1.19
--- src/sys/sys/module.h:1.18 Wed Nov 18 17:40:45 2009
+++ src/sys/sys/module.h Fri Mar 5 18:35:01 2010
@@ -1,4 +1,4 @@
-/* $NetBSD: module.h,v 1.18 2009/11/18 17:40:45 pooka Exp $ */
+/* $NetBSD: module.h,v 1.19 2010/03/05 18:35:01 pooka Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -112,7 +112,9 @@
extern struct vm_map *module_map;
extern kmutex_t module_lock;
extern u_int module_count;
+extern u_int module_builtinlist;
extern struct modlist module_list;
+extern struct modlist module_builtins;
extern u_int module_gen;
void module_init(void);
@@ -123,6 +125,8 @@
bool module_compatible(int, int);
int module_load(const char *, int, prop_dictionary_t, modclass_t);
+int module_builtin_add(modinfo_t * const *, size_t, bool);
+int module_builtin_remove(modinfo_t *, bool);
int module_autoload(const char *, modclass_t);
int module_unload(const char *);
int module_hold(const char *);