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 *);