patch-2.4.0-test11 linux/kernel/module.c

Next file: linux/kernel/ptrace.c
Previous file: linux/kernel/ksyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test10/linux/kernel/module.c linux/kernel/module.c
@@ -1,11 +1,14 @@
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <asm/module.h>
 #include <asm/uaccess.h>
 #include <linux/vmalloc.h>
 #include <linux/smp_lock.h>
 #include <asm/pgalloc.h>
 #include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
 
 /*
  * Originally by Anonymous (as far as I know...)
@@ -14,11 +17,17 @@
  * Heavily modified by Bjorn Ekwall <bj0rn@blox.se> May 1994 (C)
  * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996
  * Add MOD_INITIALIZING Keith Owens <kaos@ocs.com.au> Nov 1999
+ * Add kallsyms support, Keith Owens <kaos@ocs.com.au> Apr 2000
+ * Add asm/module support, IA64 has special requirements.  Keith Owens <kaos@ocs.com.au> Sep 2000
+ * Fix assorted bugs in module verification.  Keith Owens <kaos@ocs.com.au> Sep 2000
+ * Fix sys_init_module race, Andrew Morton <andrewm@uow.edu.au> Oct 2000
+ *     http://www.uwsg.iu.edu/hypermail/linux/kernel/0008.3/0379.html
+ * Replace xxx_module_symbol with inter_module_xxx.  Keith Owens <kaos@ocs.com.au> Oct 2000
  *
  * This source is covered by the GNU GPL, the same as all kernel sources.
  */
 
-#ifdef CONFIG_MODULES		/* a *big* #ifdef block... */
+#if defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS)
 
 extern struct module_symbol __start___ksymtab[];
 extern struct module_symbol __stop___ksymtab[];
@@ -26,6 +35,9 @@
 extern const struct exception_table_entry __start___ex_table[];
 extern const struct exception_table_entry __stop___ex_table[];
 
+extern const char __start___kallsyms[] __attribute__ ((weak));
+extern const char __stop___kallsyms[] __attribute__ ((weak));
+
 static struct module kernel_module =
 {
 	size_of_struct:		sizeof(struct module),
@@ -33,16 +45,182 @@
 	uc:	 		{ATOMIC_INIT(1)},
 	flags:			MOD_RUNNING,
 	syms:			__start___ksymtab,
-	ex_table_start:		__start___ex_table,	
-	ex_table_end:		__stop___ex_table
+	ex_table_start:		__start___ex_table,
+	ex_table_end:		__stop___ex_table,
+	kallsyms_start:		__start___kallsyms,
+	kallsyms_end:		__stop___kallsyms,
 };
 
 struct module *module_list = &kernel_module;
 
+#endif	/* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */
+
+/* inter_module functions are always available, even when the kernel is
+ * compiled without modules.  Consumers of inter_module_xxx routines
+ * will always work, even when both are built into the kernel, this
+ * approach removes lots of #ifdefs in mainline code.
+ */
+
+static struct list_head ime_list = LIST_HEAD_INIT(ime_list);
+static spinlock_t ime_lock = SPIN_LOCK_UNLOCKED;
+static int kmalloc_failed;
+
+/**
+ * inter_module_register - register a new set of inter module data.
+ * @im_name: an arbitrary string to identify the data, must be unique
+ * @owner: module that is registering the data, always use THIS_MODULE
+ * @userdata: pointer to arbitrary userdata to be registered
+ *
+ * Description: Check that the im_name has not already been registered,
+ * complain if it has.  For new data, add it to the inter_module_entry
+ * list.
+ */
+void inter_module_register(const char *im_name, struct module *owner, const void *userdata)
+{
+	struct list_head *tmp;
+	struct inter_module_entry *ime, *ime_new;
+
+	if (!(ime_new = kmalloc(sizeof(*ime), GFP_KERNEL))) {
+		/* Overloaded kernel, not fatal */
+		printk(KERN_ERR
+			"Aiee, inter_module_register: cannot kmalloc entry for '%s'\n",
+			im_name);
+		kmalloc_failed = 1;
+		return;
+	}
+	memset(ime_new, 0, sizeof(*ime_new));
+	ime_new->im_name = im_name;
+	ime_new->owner = owner;
+	ime_new->userdata = userdata;
+
+	spin_lock(&ime_lock);
+	list_for_each(tmp, &ime_list) {
+		ime = list_entry(tmp, struct inter_module_entry, list);
+		if (strcmp(ime->im_name, im_name) == 0) {
+			spin_unlock(&ime_lock);
+			kfree(ime_new);
+			/* Program logic error, fatal */
+			panic("inter_module_register: duplicate im_name '%s'", im_name);
+		}
+	}
+	list_add(&(ime_new->list), &ime_list);
+	spin_unlock(&ime_lock);
+}
+
+/**
+ * inter_module_unregister - unregister a set of inter module data.
+ * @im_name: an arbitrary string to identify the data, must be unique
+ *
+ * Description: Check that the im_name has been registered, complain if
+ * it has not.  For existing data, remove it from the
+ * inter_module_entry list.
+ */
+void inter_module_unregister(const char *im_name)
+{
+	struct list_head *tmp;
+	struct inter_module_entry *ime;
+
+	spin_lock(&ime_lock);
+	list_for_each(tmp, &ime_list) {
+		ime = list_entry(tmp, struct inter_module_entry, list);
+		if (strcmp(ime->im_name, im_name) == 0) {
+			list_del(&(ime->list));
+			spin_unlock(&ime_lock);
+			kfree(ime);
+			return;
+		}
+	}
+	spin_unlock(&ime_lock);
+	if (kmalloc_failed) {
+		printk(KERN_ERR
+			"inter_module_unregister: no entry for '%s', "
+			"probably caused by previous kmalloc failure\n",
+			im_name);
+		return;
+	}
+	else {
+		/* Program logic error, fatal */
+		panic("inter_module_unregister: no entry for '%s'", im_name);
+	}
+}
+
+/**
+ * inter_module_get - return arbitrary userdata from another module.
+ * @im_name: an arbitrary string to identify the data, must be unique
+ *
+ * Description: If the im_name has not been registered, return NULL.
+ * Try to increment the use count on the owning module, if that fails
+ * then return NULL.  Otherwise return the userdata.
+ */
+const void *inter_module_get(const char *im_name)
+{
+	struct list_head *tmp;
+	struct inter_module_entry *ime;
+	const void *result = NULL;
+
+	spin_lock(&ime_lock);
+	list_for_each(tmp, &ime_list) {
+		ime = list_entry(tmp, struct inter_module_entry, list);
+		if (strcmp(ime->im_name, im_name) == 0) {
+			if (try_inc_mod_count(ime->owner))
+				result = ime->userdata;
+			break;
+		}
+	}
+	spin_unlock(&ime_lock);
+	return(result);
+}
+
+/**
+ * inter_module_get_request - im get with automatic request_module.
+ * @im_name: an arbitrary string to identify the data, must be unique
+ * @modname: module that is expected to register im_name
+ *
+ * Description: If inter_module_get fails, do request_module then retry.
+ */
+const void *inter_module_get_request(const char *im_name, const char *modname)
+{
+	const void *result = inter_module_get(im_name);
+	if (!result) {
+		request_module(modname);
+		result = inter_module_get(im_name);
+	}
+	return(result);
+}
+
+/**
+ * inter_module_put - release use of data from another module.
+ * @im_name: an arbitrary string to identify the data, must be unique
+ *
+ * Description: If the im_name has not been registered, complain,
+ * otherwise decrement the use count on the owning module.
+ */
+void inter_module_put(const char *im_name)
+{
+	struct list_head *tmp;
+	struct inter_module_entry *ime;
+
+	spin_lock(&ime_lock);
+	list_for_each(tmp, &ime_list) {
+		ime = list_entry(tmp, struct inter_module_entry, list);
+		if (strcmp(ime->im_name, im_name) == 0) {
+			if (ime->owner)
+				__MOD_DEC_USE_COUNT(ime->owner);
+			spin_unlock(&ime_lock);
+			return;
+		}
+	}
+	spin_unlock(&ime_lock);
+	panic("inter_module_put: no entry for '%s'", im_name);
+}
+
+
+#if defined(CONFIG_MODULES)	/* The rest of the source */
+
 static long get_mod_name(const char *user_name, char **buf);
 static void put_mod_name(char *buf);
-static struct module *find_module(const char *name);
-static void free_module(struct module *, int tag_freed);
+struct module *find_module(const char *name);
+void free_module(struct module *, int tag_freed);
 
 
 /*
@@ -151,7 +329,7 @@
 sys_init_module(const char *name_user, struct module *mod_user)
 {
 	struct module mod_tmp, *mod;
-	char *name, *n_name;
+	char *name, *n_name, *name_tmp = NULL;
 	long namelen, n_namelen, i, error;
 	unsigned long mod_user_size;
 	struct module_ref *dep;
@@ -168,7 +346,7 @@
 		goto err1;
 	}
 
-	/* Check module header size.  We allow a bit of slop over the 
+	/* Check module header size.  We allow a bit of slop over the
 	   size we are familiar with to cope with a version of insmod
 	   for a newer kernel.  But don't over do it. */
 	if ((error = get_user(mod_user_size, &mod_user->size_of_struct)) != 0)
@@ -185,8 +363,14 @@
 	/* Hold the current contents while we play with the user's idea
 	   of righteousness.  */
 	mod_tmp = *mod;
+	name_tmp = kmalloc(strlen(mod->name) + 1, GFP_KERNEL);	/* Where's kstrdup()? */
+	if (name_tmp == NULL) {
+		error = -ENOMEM;
+		goto err1;
+	}
+	strcpy(name_tmp, mod->name);
 
-	error = copy_from_user(mod, mod_user, sizeof(struct module));
+	error = copy_from_user(mod, mod_user, mod_user_size);
 	if (error) {
 		error = -EFAULT;
 		goto err2;
@@ -203,32 +387,29 @@
 
 	/* Make sure all interesting pointers are sane.  */
 
-#define bound(p, n, m)  ((unsigned long)(p) >= (unsigned long)(m+1) &&  \
-	         (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size)
-
-	if (!bound(mod->name, namelen, mod)) {
+	if (!mod_bound(mod->name, namelen, mod)) {
 		printk(KERN_ERR "init_module: mod->name out of bounds.\n");
 		goto err2;
 	}
-	if (mod->nsyms && !bound(mod->syms, mod->nsyms, mod)) {
+	if (mod->nsyms && !mod_bound(mod->syms, mod->nsyms, mod)) {
 		printk(KERN_ERR "init_module: mod->syms out of bounds.\n");
 		goto err2;
 	}
-	if (mod->ndeps && !bound(mod->deps, mod->ndeps, mod)) {
+	if (mod->ndeps && !mod_bound(mod->deps, mod->ndeps, mod)) {
 		printk(KERN_ERR "init_module: mod->deps out of bounds.\n");
 		goto err2;
 	}
-	if (mod->init && !bound(mod->init, 0, mod)) {
+	if (mod->init && !mod_bound(mod->init, 0, mod)) {
 		printk(KERN_ERR "init_module: mod->init out of bounds.\n");
 		goto err2;
 	}
-	if (mod->cleanup && !bound(mod->cleanup, 0, mod)) {
+	if (mod->cleanup && !mod_bound(mod->cleanup, 0, mod)) {
 		printk(KERN_ERR "init_module: mod->cleanup out of bounds.\n");
 		goto err2;
 	}
 	if (mod->ex_table_start > mod->ex_table_end
 	    || (mod->ex_table_start &&
-		!((unsigned long)mod->ex_table_start >= (unsigned long)(mod+1)
+		!((unsigned long)mod->ex_table_start >= ((unsigned long)mod + mod->size_of_struct)
 		  && ((unsigned long)mod->ex_table_end
 		      < (unsigned long)mod + mod->size)))
 	    || (((unsigned long)mod->ex_table_start
@@ -242,24 +423,51 @@
 		goto err2;
 	}
 #ifdef __alpha__
-	if (!bound(mod->gp - 0x8000, 0, mod)) {
+	if (!mod_bound(mod->gp - 0x8000, 0, mod)) {
 		printk(KERN_ERR "init_module: mod->gp out of bounds.\n");
 		goto err2;
 	}
 #endif
 	if (mod_member_present(mod, can_unload)
-	    && mod->can_unload && !bound(mod->can_unload, 0, mod)) {
+	    && mod->can_unload && !mod_bound(mod->can_unload, 0, mod)) {
 		printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n");
 		goto err2;
 	}
-
-#undef bound
+	if (mod_member_present(mod, kallsyms_end)) {
+	    if (mod->kallsyms_end &&
+		(!mod_bound(mod->kallsyms_start, 0, mod) ||
+		 !mod_bound(mod->kallsyms_end, 0, mod))) {
+		printk(KERN_ERR "init_module: mod->kallsyms out of bounds.\n");
+		goto err2;
+	    }
+	    if (mod->kallsyms_start > mod->kallsyms_end) {
+		printk(KERN_ERR "init_module: mod->kallsyms invalid.\n");
+		goto err2;
+	    }
+	}
+	if (mod_member_present(mod, archdata_end)) {
+	    if (mod->archdata_end &&
+		(!mod_bound(mod->archdata_start, 0, mod) ||
+		 !mod_bound(mod->archdata_end, 0, mod))) {
+		printk(KERN_ERR "init_module: mod->archdata out of bounds.\n");
+		goto err2;
+	    }
+	    if (mod->archdata_start > mod->archdata_end) {
+		printk(KERN_ERR "init_module: mod->archdata invalid.\n");
+		goto err2;
+	    }
+	}
+	if (mod_member_present(mod, kernel_data) && mod->kernel_data) {
+	    printk(KERN_ERR "init_module: mod->kernel_data must be zero.\n");
+	    goto err2;
+	}
 
 	/* Check that the user isn't doing something silly with the name.  */
 
 	if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod
 				      + (unsigned long)mod_user,
 				      &n_name)) < 0) {
+		printk(KERN_ERR "init_module: get_mod_name failure.\n");
 		error = n_namelen;
 		goto err2;
 	}
@@ -277,13 +485,17 @@
 		goto err3;
 	}
 
+	if (module_arch_init(mod))
+		goto err3;
+
 	/* On some machines it is necessary to do something here
 	   to make the I and D caches consistent.  */
 	flush_icache_range((unsigned long)mod, (unsigned long)mod + mod->size);
 
-	/* Update module references.  */
 	mod->next = mod_tmp.next;
 	mod->refs = NULL;
+
+	/* Sanity check the module's dependents */
 	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
 		struct module *o, *d = dep->dep;
 
@@ -294,18 +506,25 @@
 			goto err3;
 		}
 
-		for (o = module_list; o != &kernel_module; o = o->next)
-			if (o == d) goto found_dep;
+		/* Scan the current modules for this dependency */
+		for (o = module_list; o != &kernel_module && o != d; o = o->next)
+			;
 
-		printk(KERN_ERR "init_module: found dependency that is "
+		if (o != d) {
+			printk(KERN_ERR "init_module: found dependency that is "
 				"(no longer?) a module.\n");
-		goto err3;
-		
-	found_dep:
+			goto err3;
+		}
+	}
+
+	/* Update module references.  */
+	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
+		struct module *d = dep->dep;
+
 		dep->ref = mod;
 		dep->next_ref = d->refs;
 		d->refs = dep;
-		/* Being referenced by a dependent module counts as a 
+		/* Being referenced by a dependent module counts as a
 		   use as far as kmod is concerned.  */
 		d->flags |= MOD_USED_ONCE;
 	}
@@ -335,10 +554,12 @@
 	put_mod_name(n_name);
 err2:
 	*mod = mod_tmp;
+	strcpy((char *)mod->name, name_tmp);	/* We know there is room for this */
 err1:
 	put_mod_name(name);
 err0:
 	unlock_kernel();
+	kfree(name_tmp);
 	return error;
 }
 
@@ -384,11 +605,11 @@
 		}
 		put_mod_name(name);
 		error = -EBUSY;
- 		if (mod->refs != NULL)
+		if (mod->refs != NULL)
 			goto out;
 
 		spin_lock(&unload_lock);
- 		if (!__MOD_IN_USE(mod)) {
+		if (!__MOD_IN_USE(mod)) {
 			mod->flags |= MOD_DELETED;
 			spin_unlock(&unload_lock);
 			free_module(mod, 0);
@@ -768,7 +989,7 @@
  * Look for a module by name, ignoring modules marked for deletion.
  */
 
-static struct module *
+struct module *
 find_module(const char *name)
 {
 	struct module *mod;
@@ -787,7 +1008,7 @@
  * Free the given module.
  */
 
-static void
+void
 free_module(struct module *mod, int tag_freed)
 {
 	struct module_ref *dep;
@@ -795,7 +1016,7 @@
 
 	/* Let the module clean up.  */
 
-	if (mod->flags & MOD_RUNNING) 
+	if (mod->flags & MOD_RUNNING)
 	{
 		if(mod->cleanup)
 			mod->cleanup();
@@ -852,7 +1073,7 @@
 		} while (0)
 #define safe_copy_cstr(str)	safe_copy_str(str, sizeof(str)-1)
 
-        	len = strlen(mod->name);
+		len = strlen(mod->name);
 		safe_copy_str(mod->name, len);
 
 		if ((len = 20 - len) > 0) {
@@ -959,58 +1180,6 @@
 	if (len > length)
 		len = length;
 	return len;
-}
-
-/*
- * Gets the address for a symbol in the given module.  If modname is
- * NULL, it looks for the name in any registered symbol table.  If the
- * modname is an empty string, it looks for the symbol in kernel exported
- * symbol tables. Increase the usage count of the module in which the
- * symbol was found - it's the only way we can guarantee that it's still
- * there by the time our caller actually uses it.
- */
-unsigned long
-get_module_symbol(char *modname, char *symname)
-{
-	struct module *mp;
-	struct module_symbol *sym;
-	int i;
-
-	spin_lock(&unload_lock);
-	for (mp = module_list; mp; mp = mp->next) {
-		if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) &&
-			MOD_CAN_QUERY(mp) &&
-			(mp->nsyms > 0)) {
-			for (i = mp->nsyms, sym = mp->syms;
-				i > 0; --i, ++sym) {
-
-				if (strcmp(sym->name, symname) == 0) {
-					__MOD_INC_USE_COUNT(mp);
-					spin_unlock(&unload_lock);
-					return sym->value;
-				}
-			}
-		}
-	}
-	spin_unlock(&unload_lock);
-	return 0;
-}
-
-/* Decrease the use count of the module containing a symbol with the 
- * address passed.
- */
-void put_module_symbol(unsigned long addr)
-{
-	struct module *mp;
-
-	for (mp = module_list; mp; mp = mp->next) {
-		if (MOD_CAN_QUERY(mp) &&
-		    addr >= (unsigned long)mp &&
-		    addr < (unsigned long)mp + mp->size) {
-			__MOD_DEC_USE_COUNT(mp);
-			return;
-		}
-	}
 }
 
 #else		/* CONFIG_MODULES */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)