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

Reply via email to