Messages in this thread Patch in this message | | | From | Rusty Russell <> | Subject | [PATCH] Module rewrite 14/20: Module License Support | Date | Wed, 25 Sep 2002 13:05:25 +1000 |
| |
Name: MODULE_LICENSE support for modules Author: Rusty Russell Status: Untested Depends: Module/modversions.patch.gz
D: This implements EXPORT_SYMBOL_GPL and MODULE_LICENSE properly (so D: restrictions are enforced). It also sets up a table for finding D: the sections in the ELF object, since there are now enough D: variables to make it sensible.
diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .14496-linux-2.5.38/arch/i386/vmlinux.lds.S .14496-linux-2.5.38.updated/arch/i386/vmlinux.lds.S --- .14496-linux-2.5.38/arch/i386/vmlinux.lds.S 2002-09-25 10:34:57.000000000 +1000 +++ .14496-linux-2.5.38.updated/arch/i386/vmlinux.lds.S 2002-09-25 10:35:23.000000000 +1000 @@ -29,6 +29,10 @@ SECTIONS __start___ksymtab = .; /* Kernel symbol table */ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + . = ALIGN(64); + __start_gpl_ksymtab = .; /* Kernel symbol table (GPL only) */ + __gpl.ksymtab : { *(__gpl.ksymtab) } + __stop_gpl_ksymtab = .; .data : { /* Data */ *(.data) diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .14496-linux-2.5.38/include/linux/module.h .14496-linux-2.5.38.updated/include/linux/module.h --- .14496-linux-2.5.38/include/linux/module.h 2002-09-25 10:35:00.000000000 +1000 +++ .14496-linux-2.5.38.updated/include/linux/module.h 2002-09-25 10:35:23.000000000 +1000 @@ -19,7 +19,6 @@ #include <asm/uaccess.h> /* For struct exception_table_entry */ /* Not Yet Implemented */ -#define MODULE_LICENSE(name) #define MODULE_AUTHOR(name) #define MODULE_DESCRIPTION(desc) #define MODULE_SUPPORTED_DEVICE(name) @@ -44,8 +43,36 @@ struct modversion_info { a constant so it works in initializers. */ extern struct module __this_module; #define THIS_MODULE (&__this_module) + +/* + * The following license idents are currently accepted as indicating free + * software modules + * + * "GPL" [GNU Public License v2 or later] + * "GPL and additional rights" [GNU Public License v2 rights and more] + * "Dual BSD/GPL" [GNU Public License v2 or BSD license choice] + * "Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice] + * + * The following other idents are available + * + * "Proprietary" [Non free products] + * + * There are dual licensed components, but when running with Linux it is the + * GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL + * is a GPL combined work. + * + * This exists for several reasons + * 1. So modinfo can show license info for users wanting to vet their setup + * is free + * 2. So the community can ignore bug reports including proprietary modules + * 3. So vendors can do likewise based on their own policies + */ +#define MODULE_LICENSE(license) \ + static const char __module_license[] \ + __attribute__((section(".license"))) = license #else #define THIS_MODULE ((struct module *)0) +#define MODULE_LICENSE(license) #endif #ifdef CONFIG_MODULES @@ -73,8 +100,10 @@ extern struct modversion_info modversion NEED_MODVERSION(sym) \ EXPORT_SYMBOL_NOVERS(sym) -/* FIXME: Enforce this. */ -#define EXPORT_SYMBOL_GPL(sym) EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) \ + const struct kernel_symbol __ksymtab_##sym \ + __attribute__((section("__gpl.ksymtab"))) \ + = { (unsigned long)&sym, #sym } struct kernel_symbol_group { @@ -84,6 +113,9 @@ struct kernel_symbol_group /* Module which owns it (if any) */ struct module *owner; + /* Are we internal use only? */ + int gplonly; + unsigned int num_syms; const struct kernel_symbol *syms; }; @@ -106,6 +138,9 @@ struct module /* Exported symbols */ struct kernel_symbol_group symbols; + /* GPL-only exported symbols. */ + struct kernel_symbol_group gpl_symbols; + /* Exception tables */ struct exception_table extable; @@ -130,6 +165,9 @@ struct module /* Am I unsafe to unload? */ int unsafe_to_unload; + /* My license */ + char *license; + #ifdef CONFIG_MODULE_UNLOAD /* Usage count. */ struct bigref use; diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .14496-linux-2.5.38/kernel/module.c .14496-linux-2.5.38.updated/kernel/module.c --- .14496-linux-2.5.38/kernel/module.c 2002-09-25 10:35:01.000000000 +1000 +++ .14496-linux-2.5.38.updated/kernel/module.c 2002-09-25 10:36:50.000000000 +1000 @@ -35,10 +35,13 @@ #define DEBUGP(fmt , ...) #endif +/* Created by linker magic */ extern const struct exception_table_entry __start___ex_table[]; extern const struct exception_table_entry __stop___ex_table[]; extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; +extern const struct kernel_symbol __start_gpl_ksymtab[]; +extern const struct kernel_symbol __stop_gpl_ksymtab[]; /* Protects extables and symbol tables */ spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; @@ -49,6 +52,7 @@ static LIST_HEAD(symbols); static struct exception_table kernel_extable; static struct kernel_symbol_group kernel_symbols; +static struct kernel_symbol_group kernel_gpl_symbols; /* List of modules, protected by module_mutex */ static DECLARE_MUTEX(module_mutex); @@ -63,13 +67,16 @@ struct sizes /* Find a symbol, return value and the symbol group */ static unsigned long __find_symbol(const char *name, - struct kernel_symbol_group **group) + struct kernel_symbol_group **group, + int gplok) { struct kernel_symbol_group *ks; list_for_each_entry(ks, &symbols, list) { unsigned int i; + if (ks->gplonly && !gplok) + continue; for (i = 0; i < ks->num_syms; i++) { if (strcmp(ks->syms[i].name, name) == 0) { *group = ks; @@ -220,6 +227,7 @@ sys_delete_module(const char *name_user, /* Remove symbols so module count can't increase from symbol_get() */ spin_lock_irq(&modlist_lock); list_del_init(&mod->symbols.list); + list_del_init(&mod->gpl_symbols.list); spin_unlock_irq(&modlist_lock); /* Mark it as non-live. */ @@ -240,6 +248,7 @@ sys_delete_module(const char *name_user, try_module_get(mod); /* Can't fail */ spin_lock_irq(&modlist_lock); list_add(&mod->symbols.list, &kernel_symbols.list); + list_add(&mod->gpl_symbols.list, &kernel_symbols.list); spin_unlock_irq(&modlist_lock); ret = -EWOULDBLOCK; goto out; @@ -298,7 +307,7 @@ void __symbol_put(const char *symbol) unsigned long flags; spin_lock_irqsave(&modlist_lock, flags); - if (!__find_symbol(symbol, &ksg)) + if (!__find_symbol(symbol, &ksg, 1)) BUG(); module_put(ksg->owner); spin_unlock_irqrestore(&modlist_lock, flags); @@ -501,6 +510,14 @@ static inline int check_version(const ch } #endif /* CONFIG_MODVERSIONING */ +static int module_is_gpl(struct module *mod) +{ + return (strcmp(mod->license, "GPL") == 0 + || strcmp(mod->license, "GPL and additional rights") == 0 + || strcmp(mod->license, "Dual BSD/GPL") == 0 + || strcmp(mod->license, "Dual MPL/GPL") == 0); +} + /* Find an symbol for this module (ie. resolve internals first). It we find one, record usage. Must be holding module_mutex. */ unsigned long find_symbol_internal(Elf_Shdr *sechdrs, @@ -519,7 +536,7 @@ unsigned long find_symbol_internal(Elf_S } /* Look in other modules... */ spin_lock_irq(&modlist_lock); - ret = __find_symbol(name, ksg); + ret = __find_symbol(name, ksg, module_is_gpl(mod)); if (ret) { /* This can fail due to OOM, or module unloading */ if (!use_module(mod, (*ksg)->owner)) @@ -536,6 +553,7 @@ static void free_module(struct module *m list_del(&mod->list); spin_lock_irq(&modlist_lock); list_del(&mod->symbols.list); + list_del(&mod->gpl_symbols.list); list_del(&mod->extable.list); spin_unlock_irq(&modlist_lock); @@ -556,7 +574,7 @@ void *__symbol_get(const char *symbol) unsigned long value, flags; spin_lock_irqsave(&modlist_lock, flags); - value = __find_symbol(symbol, &ksg); + value = __find_symbol(symbol, &ksg, 1); if (value && !try_module_get(ksg->owner)) value = 0; spin_unlock_irqrestore(&modlist_lock, flags); @@ -792,7 +810,62 @@ static struct sizes get_sizes(const Elf_ return ret; } -/* Allocate and load the module */ +struct section_locate +{ + const char *name; + unsigned int *position; + int compulsory; +}; + +static int find_secs(Elf_Shdr *sechdrs, + const char *secstrings, + unsigned int numsections, + struct section_locate locate[], + unsigned int num) +{ + unsigned int i, j; + + for (j = 0; j < num; j++) + *locate[j].position = 0; + + for (i = 0; i < numsections; i++) { + const char *secname = secstrings + sechdrs[i].sh_name; + for (j = 0; j < num; j++) { + if (strcmp(locate[j].name, secname) == 0) + *locate[j].position = i; + } +#ifndef CONFIG_MODULE_UNLOAD + /* Don't load .exit sections */ + if (strstr(secname, ".exit")) + sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC; +#endif + } + for (j = 0; j < num; j++) { + if (!*locate[j].position && locate[j].compulsory) { + printk(KERN_ERR "module has no %s\n", locate[j].name); + return -ENOEXEC; + } + } + return 0; +} + +static void set_license(struct module *mod, Elf_Shdr *sechdrs, int licenseidx) +{ + if (licenseidx) + mod->license = (char *)sechdrs[licenseidx].sh_offset; + else + mod->license = "unspecified"; + + if (module_is_gpl(mod)) + return; + + printk(KERN_WARNING "%s: module license `%s' taints kernel.\n", + mod->name, mod->license); + tainted |= TAINT_PROPRIETORY_MODULE; +} + +/* Allocate and load the module: note that size of section 0 is always + zero, and we rely on this for optional sections. */ static struct module *load_module(void *umod, unsigned long len, const char *uargs) @@ -800,14 +873,20 @@ static struct module *load_module(void * Elf_Ehdr *hdr; Elf_Shdr *sechdrs; char *secstrings; - unsigned int i, symindex, exportindex, strindex, setupindex, exindex, - modnameindex, obsparmindex, versindex; + unsigned int i, symidx, exportidx, stridx, setupidx, exidx, + modnameidx, obsparmidx, versionidx, licenseidx, gplidx; long arglen; unsigned long common_length; struct sizes sizes, used; struct module *mod; int err = 0; void *ptr = NULL; /* Stops spurious gcc uninitialized warning */ + struct section_locate loc[] + = { { ".symtab",&symidx,1 }, { ".modulename",&modnameidx,1 }, + { "__ksymtab",&exportidx,0 }, {"__gpl.ksymtab",&gplidx,0 }, + { ".setup.init",&setupidx,0 }, { "__ex_table",&exidx,0 }, + { ".obsparm",&obsparmidx,0}, { ".license",&licenseidx,0 }, + { ".versions",&versionidx,0}, { ".strtab",&stridx,1 } }; DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); @@ -837,67 +916,10 @@ static struct module *load_module(void * sechdrs = (void *)hdr + hdr->e_shoff; secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; - /* May not export symbols, or have setup params, so these may - not exist */ - exportindex = setupindex = obsparmindex = versindex = 0; - - /* And these should exist, but gcc whinges if we don't init them */ - symindex = strindex = exindex = modnameindex = 0; - /* Find where important sections are */ - for (i = 1; i < hdr->e_shnum; i++) { - if (sechdrs[i].sh_type == SHT_SYMTAB) { - /* Internal symbols */ - DEBUGP("Symbol table in section %u\n", i); - symindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, ".modulename") - == 0) { - /* This module's name */ - DEBUGP("Module name in section %u\n", i); - modnameindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, "__ksymtab") - == 0) { - /* Exported symbols. */ - DEBUGP("EXPORT table in section %u\n", i); - exportindex = i; - } else if (strcmp(secstrings + sechdrs[i].sh_name, ".strtab") - == 0) { - /* Strings */ - DEBUGP("String table found in section %u\n", i); - strindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, ".setup.init") - == 0) { - /* Setup parameter info */ - DEBUGP("Setup table found in section %u\n", i); - setupindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, "__ex_table") - == 0) { - /* Exception table */ - DEBUGP("Exception table found in section %u\n", i); - exindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, ".obsparm") - == 0) { - /* Obsolete MODULE_PARM() table */ - DEBUGP("Obsolete param found in section %u\n", i); - obsparmindex = i; - } else if (strcmp(secstrings+sechdrs[i].sh_name, ".versions") - == 0) { - /* Module versioning table. */ - DEBUGP("Symbol versions found in section %u\n", i); - versindex = i; - } -#ifndef CONFIG_MODULE_UNLOAD - /* Don't load .exit sections */ - if (strstr(secstrings+sechdrs[i].sh_name, ".exit")) - sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC; -#endif - } - - if (!modnameindex) { - DEBUGP("Module has no name!\n"); - err = -ENOEXEC; + err = find_secs(sechdrs, secstrings, hdr->e_shnum,loc,ARRAY_SIZE(loc)); + if (err < 0) goto free_hdr; - } /* Now allocate space for the module proper, and copy name and args. */ err = strlen_user(uargs); @@ -915,7 +937,7 @@ static struct module *load_module(void * err = -EFAULT; goto free_mod; } - strncpy(mod->name, (char *)hdr + sechdrs[modnameindex].sh_offset, + strncpy(mod->name, (char *)hdr + sechdrs[modnameidx].sh_offset, sizeof(mod->name)-1); if (find_module(mod->name)) { @@ -927,12 +949,14 @@ static struct module *load_module(void * INIT_LIST_HEAD(&mod->extable.list); INIT_LIST_HEAD(&mod->list); INIT_LIST_HEAD(&mod->symbols.list); - mod->symbols.owner = mod; + INIT_LIST_HEAD(&mod->gpl_symbols.list); + mod->symbols.owner = mod->gpl_symbols.owner = mod; + mod->gpl_symbols.gplonly = 1; module_unload_init(mod); /* How much space will we need? (Common area in core) */ sizes = get_sizes(hdr, sechdrs, secstrings); - common_length = read_commons(hdr, &sechdrs[symindex]); + common_length = read_commons(hdr, &sechdrs[symidx]); sizes.core_size += common_length; /* Set these up: arch's can add to them */ @@ -943,7 +967,6 @@ static struct module *load_module(void * ptr = module_core_alloc(hdr, sechdrs, secstrings, mod); if (IS_ERR(ptr)) goto free_mod; - mod->module_core = ptr; ptr = module_init_alloc(hdr, sechdrs, secstrings, mod); @@ -970,40 +993,40 @@ static struct module *load_module(void * if (used.init_size > mod->init_size || used.core_size > mod->core_size) BUG(); + /* Set up license info based on contents of section */ + set_license(mod, sechdrs, licenseidx); + /* Fix up syms, so that st_value is a pointer to location. */ - err = simplify_symbols(sechdrs, symindex, strindex, versindex, + err = simplify_symbols(sechdrs, symidx, stridx, versionidx, mod->module_core, mod); if (err < 0) goto cleanup; /* Always do SMP check for modversions. */ err = check_version(mod->name, "__SMP__", - (void *)sechdrs[versindex].sh_offset, - sechdrs[versindex].sh_size + (void *)sechdrs[versionidx].sh_offset, + sechdrs[versionidx].sh_size / sizeof(struct modversion_info)); if (err < 0) goto cleanup; - /* Set up EXPORTed symbols */ - if (exportindex) { - mod->symbols.num_syms = (sechdrs[exportindex].sh_size - / sizeof(*mod->symbols.syms)); - mod->symbols.syms = (void *)sechdrs[exportindex].sh_offset; - } + /* Set up EXPORTed & EXPORT_GPLed symbols */ + mod->symbols.num_syms = (sechdrs[exportidx].sh_size + / sizeof(*mod->symbols.syms)); + mod->symbols.syms = (void *)sechdrs[exportidx].sh_offset; + mod->gpl_symbols.num_syms = (sechdrs[gplidx].sh_size + / sizeof(*mod->gpl_symbols.syms)); + mod->gpl_symbols.syms = (void *)sechdrs[gplidx].sh_offset; - /* Set up exception table */ - if (exindex) { - /* FIXME: Sort exception table. */ - mod->extable.num_entries = (sechdrs[exindex].sh_size - / sizeof(struct - exception_table_entry)); - mod->extable.entry = (void *)sechdrs[exindex].sh_offset; - } + /* Set up exception table. FIXME: Sort exception table. */ + mod->extable.num_entries = (sechdrs[exidx].sh_size + / sizeof(struct exception_table_entry)); + mod->extable.entry = (void *)sechdrs[exidx].sh_offset; /* Now handle each section. */ for (i = 1; i < hdr->e_shnum; i++) { err = handle_section(secstrings + sechdrs[i].sh_name, - sechdrs, strindex, symindex, i, mod); + sechdrs, stridx, symidx, i, mod); if (err < 0) goto cleanup; } @@ -1012,7 +1035,7 @@ static struct module *load_module(void * put a dummy function in, because otherwise this can be a very confusing silent failure */ if (!mod->init) { - printk("%s: No init function.\n", mod->name); + printk(KERN_ERR "%s: No init function.\n", mod->name); err = -ENOEXEC; goto cleanup; } @@ -1021,20 +1044,20 @@ static struct module *load_module(void * if (err < 0) goto cleanup; - if (obsparmindex) { + if (obsparmidx) { err = obsolete_params(mod->name, mod->args, (struct obsolete_modparm *) - sechdrs[obsparmindex].sh_offset, - sechdrs[obsparmindex].sh_size + sechdrs[obsparmidx].sh_offset, + sechdrs[obsparmidx].sh_size / sizeof(struct obsolete_modparm), - sechdrs, symindex, - (char *)sechdrs[strindex].sh_offset); + sechdrs, symidx, + (char *)sechdrs[stridx].sh_offset); } else { /* Size of section 0 is 0, so this works well if no params */ err = parse_args(mod->name, mod->args, (struct kernel_param *) - sechdrs[setupindex].sh_offset, - sechdrs[setupindex].sh_size + sechdrs[setupidx].sh_offset, + sechdrs[setupidx].sh_size / sizeof(struct kernel_param), NULL); } @@ -1115,6 +1138,7 @@ sys_init_module(void *umod, /* Now it's a first class citizen! */ spin_lock_irq(&modlist_lock); list_add(&mod->symbols.list, &kernel_symbols.list); + list_add(&mod->gpl_symbols.list, &kernel_symbols.list); spin_unlock_irq(&modlist_lock); list_add(&mod->list, &modules); @@ -1179,7 +1203,12 @@ static int __init init(void) /* Add kernel symbols to symbol table */ kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); kernel_symbols.syms = __start___ksymtab; + kernel_symbols.gplonly = 0; list_add(&kernel_symbols.list, &symbols); + kernel_gpl_symbols.num_syms = (__stop_gpl_ksymtab-__start_gpl_ksymtab); + kernel_gpl_symbols.syms = __start_gpl_ksymtab; + kernel_gpl_symbols.gplonly = 1; + list_add(&kernel_gpl_symbols.list, &symbols); /* Add kernel exception table to exception tables */ kernel_extable.num_entries = (__stop___ex_table -__start___ex_table); -- Anyone who quotes me in their sig is an idiot. -- Rusty Russell. - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |