diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-17 15:54:28 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-17 15:54:28 -0700 |
| commit | 0cbf730433bdc3932b71111130ccb67addf1a61a (patch) | |
| tree | 55a68f2f3071e2aa38abd3a87d00e9970e5da2f9 | |
| parent | fb3ec8201ae0dda3f124561d4d0a917022f6efbe (diff) | |
| download | patches-0cbf730433bdc3932b71111130ccb67addf1a61a.tar.gz | |
namespace patch
| -rw-r--r-- | namespace | 468 | ||||
| -rw-r--r-- | series | 1 |
2 files changed, 469 insertions, 0 deletions
diff --git a/namespace b/namespace new file mode 100644 index 00000000000000..f01fb3f284695e --- /dev/null +++ b/namespace @@ -0,0 +1,468 @@ +Core module symbol namespaces code and intro. + +There seems to be rough consensus that the kernel currently has too many +exported symbols. A lot of these exports are generally usable utility +functions or important driver interfaces; but another large part are +functions intended by only one or two very specific modules for a very +specific purpose. One example is the TCP code. It has most of its +internals exported, but only for use by tcp_ipv6.c (and now a few more +by the TCP/IP congestion modules) But it doesn't make sense to include +these exported for a specific module functions into a broader "kernel +interface". External modules assume they can use these functions, but +they were never intended for that. + +This patch allows to export symbols only for specific modules by +introducing symbol name spaces. A module name space has a white list of +modules that are allowed to import symbols for it; all others can't use +the symbols. + +It adds two new macros: + +MODULE_NAMESPACE_ALLOW(namespace, module); + +Allow module to import symbols from namespace. module is the module name +without .ko as displayed by lsmod. Must be in the same module as the +export (and be duplicated if there are multiple modules exporting +symbols to a namespace). Multiple allows for the same name space are +allowed. + +EXPORT_SYMBOL_NS(namespace, symbol); + +Export symbol into namespace. Only modules allowed for the namespace +will be able to use them. EXPORT_SYMBOL_NS implies GPL only because it +is only for "internal" interfaces. + +The name spaces only work for module loading. I didn't find a nice way +to make them work inside the main kernel binary. This means the name +space is not enforced for modules that are built in. + +The biggest amount of work is of course still open: to go over all the +existing exports and figure for which ones it makes sense to define a +namespace. I did it for TCP and UDP so far, but the kernel right now +has nearly 10k exports (with some dups) that would need to be checked +and turned into name spaces. I would expect any symbol that is only used +by one or two other modules is a strong candidate for a namespace; in +some cases even more with modules that are tightly coupled. + +I am optimistic that in the end we will have a much more manageable +kernel interface. + +Caveats: + +Exports need one long word more memory. + +I had to add some alignment magic to the existing EXPORT_SYMBOLs to get +the sections right. Tested on i386/x86-64, but I hope it also still +works on architectures with stricter alignment requirements like ARM. +Any testers for that? + +--- + arch/arm/kernel/armksyms.c | 2 + include/asm-generic/vmlinux.lds.h | 7 ++ + include/linux/module.h | 72 ++++++++++++++++----- + kernel/module.c | 129 ++++++++++++++++++++++++++++++++------ + 4 files changed, 175 insertions(+), 35 deletions(-) + +--- a/arch/arm/kernel/armksyms.c ++++ b/arch/arm/kernel/armksyms.c +@@ -52,7 +52,7 @@ extern void fp_enter(void); + * This has a special calling convention; it doesn't + * modify any of the usual registers, except for LR. + */ +-#define EXPORT_CRC_ALIAS(sym) __CRC_SYMBOL(sym, "") ++#define EXPORT_CRC_ALIAS(sym) __CRC_SYMBOL(sym, "", "", "") + + #define EXPORT_SYMBOL_ALIAS(sym,orig) \ + EXPORT_CRC_ALIAS(sym) \ +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -163,6 +163,13 @@ + VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .; \ + } \ + \ ++ /* Kernel symbol table: namespaces */ \ ++ __knamespace : AT(ADDR(__knamespace) - LOAD_OFFSET) { \ ++ VMLINUX_SYMBOL(__start___knamespace) = .; \ ++ *(__knamespace) \ ++ VMLINUX_SYMBOL(__stop___knamespace) = .; \ ++ } \ ++ \ + /* Kernel symbol table: strings */ \ + __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ + *(__ksymtab_strings) \ +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -34,6 +34,7 @@ struct kernel_symbol + { + unsigned long value; + const char *name; ++ const char *namespace; + }; + + struct modversion_info +@@ -167,49 +168,81 @@ struct notifier_block; + #ifdef CONFIG_MODULES + + /* Get/put a kernel symbol (calls must be symmetric) */ +-void *__symbol_get(const char *symbol); +-void *__symbol_get_gpl(const char *symbol); ++extern void *do_symbol_get(const char *symbol, struct module *caller); ++#define __symbol_get(sym) do_symbol_get(sym, THIS_MODULE) + #define symbol_get(x) ((typeof(&x))(__symbol_get(MODULE_SYMBOL_PREFIX #x))) + ++struct module_ns { ++ char *name; ++ char *allowed; ++}; ++ ++#define NS_SEPARATOR "." ++ ++/* ++ * Allow module MODULE to reference namespace NS. ++ * MODULE is just the base module name with suffix or path. ++ * This must be declared in the module (or main kernel) as where the ++ * symbols are defined. When multiple modules export symbols from ++ * a single namespace all modules need to contain a full set ++ * of MODULE_NAMESPACE_ALLOWs. ++ */ ++#define MODULE_NAMESPACE_ALLOW(ns, module) \ ++ static const struct module_ns __knamespace_##module##_##_##ns \ ++ asm("__knamespace_" #module NS_SEPARATOR #ns) \ ++ __used__ \ ++ __attribute__((section("__knamespace"), unused)) \ ++ = { #ns, #module } ++ + #ifndef __GENKSYMS__ + #ifdef CONFIG_MODVERSIONS + /* Mark the CRC weak since genksyms apparently decides not to + * generate a checksums for some symbols */ +-#define __CRC_SYMBOL(sym, sec) \ ++#define __CRC_SYMBOL(sym, sec, post, post2) \ + extern void *__crc_##sym __attribute__((weak)); \ +- static const unsigned long __kcrctab_##sym \ ++ static const unsigned long __kcrctab_##sym##post \ ++ asm("__kcrctab_" #sym post2) \ + __used \ + __attribute__((section("__kcrctab" sec), unused)) \ + = (unsigned long) &__crc_##sym; + #else +-#define __CRC_SYMBOL(sym, sec) ++#define __CRC_SYMBOL(sym, sec, post, post2) + #endif + + /* For every exported symbol, place a struct in the __ksymtab section */ +-#define __EXPORT_SYMBOL(sym, sec) \ ++#define __EXPORT_SYMBOL(sym, sec, post, post2, ns) \ + extern typeof(sym) sym; \ +- __CRC_SYMBOL(sym, sec) \ +- static const char __kstrtab_##sym[] \ ++ __CRC_SYMBOL(sym, sec, post, post2) \ ++ static const char __kstrtab_##sym##post[] \ ++ asm("__kstrtab_" #sym post2) \ + __attribute__((section("__ksymtab_strings"), aligned(1))) \ + = MODULE_SYMBOL_PREFIX #sym; \ +- static const struct kernel_symbol __ksymtab_##sym \ ++ static const struct kernel_symbol __ksymtab_##sym##post \ ++ asm("__ksymtab_" #sym post2) \ + __used \ + __attribute__((section("__ksymtab" sec), unused)) \ +- = { (unsigned long)&sym, __kstrtab_##sym } ++ __attribute__((aligned(sizeof(void *)))) \ ++ = { (unsigned long)&sym, __kstrtab_##sym##post, ns } + + #define EXPORT_SYMBOL(sym) \ +- __EXPORT_SYMBOL(sym, "") ++ __EXPORT_SYMBOL(sym, "",,, NULL) + + #define EXPORT_SYMBOL_GPL(sym) \ +- __EXPORT_SYMBOL(sym, "_gpl") ++ __EXPORT_SYMBOL(sym, "_gpl",,, NULL) + + #define EXPORT_SYMBOL_GPL_FUTURE(sym) \ +- __EXPORT_SYMBOL(sym, "_gpl_future") ++ __EXPORT_SYMBOL(sym, "_gpl_future",,, NULL) ++ + ++/* Export symbol into namespace ns ++ * No _GPL variants because namespaces imply GPL only ++ */ ++#define EXPORT_SYMBOL_NS(ns, sym) \ ++ __EXPORT_SYMBOL(sym, "_gpl",__##ns, NS_SEPARATOR #ns, #ns) + + #ifdef CONFIG_UNUSED_SYMBOLS +-#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") +-#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl") ++#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused",,,NULL) ++#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl",,,NULL) + #else + #define EXPORT_UNUSED_SYMBOL(sym) + #define EXPORT_UNUSED_SYMBOL_GPL(sym) +@@ -271,6 +304,10 @@ struct module + unsigned int num_gpl_future_syms; + const unsigned long *gpl_future_crcs; + ++ /* Namespaces */ ++ struct module_ns *knamespace; ++ unsigned num_knamespaces; ++ + /* Exception table */ + unsigned int num_exentries; + const struct exception_table_entry *extable; +@@ -374,7 +411,8 @@ extern void __module_put_and_exit(struct + + #ifdef CONFIG_MODULE_UNLOAD + unsigned int module_refcount(struct module *mod); +-void __symbol_put(const char *symbol); ++extern void do_symbol_put(const char *symbol, struct module *caller); ++#define __symbol_put(symbol) do_symbol_put(symbol, THIS_MODULE) + #define symbol_put(x) __symbol_put(MODULE_SYMBOL_PREFIX #x) + void symbol_put_addr(void *addr); + +@@ -456,6 +494,8 @@ extern void module_update_markers(void); + #define EXPORT_SYMBOL_GPL_FUTURE(sym) + #define EXPORT_UNUSED_SYMBOL(sym) + #define EXPORT_UNUSED_SYMBOL_GPL(sym) ++#define EXPORT_SYMBOL_NS(sym, ns) ++#define MODULE_NAMESPACE_ALLOW(ns, allow) + + /* Given an address, look for it in the exception tables. */ + static inline const struct exception_table_entry * +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -145,6 +145,8 @@ extern const unsigned long __start___kcr + extern const unsigned long __start___kcrctab_gpl_future[]; + extern const unsigned long __start___kcrctab_unused[]; + extern const unsigned long __start___kcrctab_unused_gpl[]; ++extern const struct module_ns __start___knamespace[]; ++extern const struct module_ns __stop___knamespace[]; + + #ifndef CONFIG_MODVERSIONS + #define symversion(base, idx) NULL +@@ -242,11 +244,10 @@ static const struct kernel_symbol *searc + + /* Find a symbol, return value, (optional) crc and (optional) module + * which owns it */ +-static unsigned long find_symbol(const char *name, +- struct module **owner, +- const unsigned long **crc, +- bool gplok, +- bool warn) ++static const struct kernel_symbol *do_find_symbol(const char *name, ++ struct module **owner, ++ const unsigned long **crc, ++ bool gplok, bool warn) + { + struct module *mod; + const struct kernel_symbol *ks; +@@ -268,7 +269,7 @@ static unsigned long find_symbol(const c + if (ks) { + if (owner) + *owner = NULL; +- return ks->value; ++ return ks; + } + + /* Now try modules. */ +@@ -294,12 +295,95 @@ static unsigned long find_symbol(const c + if (ks) { + if (owner) + *owner = mod; +- return ks->value; ++ return ks; + } + } + + DEBUGP("Failed to find symbol %s\n", name); +- return -ENOENT; ++ return NULL; ++} ++ ++/* Find namespace in a single namespace table */ ++static const struct module_ns *lookup_namespace(const struct module_ns *start, ++ const struct module_ns *stop, ++ const char *name, ++ const char *reqname, int *n) ++{ ++ const struct module_ns *ns; ++ for (ns = start; ns < stop; ns++) ++ if (!strcmp(name, ns->name)) { ++ (*n)++; ++ if (!strcmp(reqname, ns->allowed)) ++ return ns; ++ } ++ return NULL; ++} ++ ++/* Search all namespace tables for a namespace */ ++static const struct module_ns *find_namespace(const char *name, ++ const char *reqname, int *n, ++ const struct module **modp) ++{ ++ const struct module_ns *ns; ++ struct module *mod; ++ *modp = NULL; ++ ns = lookup_namespace(__start___knamespace, __stop___knamespace, ++ name, reqname, n); ++ if (ns) ++ return ns; ++ list_for_each_entry(mod, &modules, list) { ++ ns = lookup_namespace(mod->knamespace, ++ mod->knamespace + mod->num_knamespaces, ++ name, reqname, n); ++ if (ns) { ++ *modp = mod; ++ return ns; ++ } ++ } ++ return NULL; ++} ++ ++/* Look up a symbol and check namespace */ ++static unsigned long find_symbol(const char *name, ++ struct module **owner, ++ const unsigned long **crc, ++ bool gplok, bool warn, ++ struct module *requester) ++{ ++ const struct kernel_symbol *ks; ++ const struct module_ns *ns; ++ const struct module *mod; ++ int n = 0; ++ ++ ks = do_find_symbol(name, owner, crc, gplok, warn); ++ if (!ks) ++ return 0; ++ ++ /* When the symbol has a name space check if the requesting module ++ is white listed as allowed. */ ++ if (ks->namespace) { ++ ns = find_namespace(ks->namespace, requester->name, &n, &mod); ++ if (!ns) { ++ if (n > 0) ++ printk(KERN_WARNING "module %s not allowed " ++ "to reference namespace `%s' for %s\n", ++ requester->name, ks->namespace, name); ++ else ++ printk(KERN_WARNING "%s referencing " ++ "undeclared namespace `%s' for %s\n", ++ requester->name, ks->namespace, name); ++ return 0; ++ } ++ /* Only allow name space declarations in the symbol's own ++ module. */ ++ if (mod != *owner) { ++ printk(KERN_WARNING "namespace `%s' for symbol `%s' " ++ "defined in wrong module `%s'\n", ++ name, name, mod->name); ++ return 0; ++ } ++ } ++ return ks->value; + } + + /* Search for module by name: must hold module_mutex. */ +@@ -784,17 +868,17 @@ static void print_unload_info(struct seq + seq_printf(m, "-"); + } + +-void __symbol_put(const char *symbol) ++void do_symbol_put(const char *symbol, struct module *caller) + { + struct module *owner; + + preempt_disable(); +- if (IS_ERR_VALUE(find_symbol(symbol, &owner, NULL, true, false))) ++ if (!find_symbol(symbol, &owner, NULL, true, false, caller)) + BUG(); + module_put(owner); + preempt_enable(); + } +-EXPORT_SYMBOL(__symbol_put); ++EXPORT_SYMBOL(do_symbol_put); + + void symbol_put_addr(void *addr) + { +@@ -952,7 +1036,8 @@ static inline int check_modstruct_versio + { + const unsigned long *crc; + +- if (IS_ERR_VALUE(find_symbol("struct_module", NULL, &crc, true, false))) ++ if (IS_ERR_VALUE(find_symbol("struct_module", NULL, &crc, ++ true, false, mod))) + BUG(); + return check_version(sechdrs, versindex, "struct_module", mod, crc); + } +@@ -1003,7 +1088,7 @@ static unsigned long resolve_symbol(Elf_ + const unsigned long *crc; + + ret = find_symbol(name, &owner, &crc, +- !(mod->taints & TAINT_PROPRIETARY_MODULE), true); ++ !(mod->taints & TAINT_PROPRIETARY_MODULE), true, mod); + if (!IS_ERR_VALUE(ret)) { + /* use_module can fail due to OOM, + or module initialization or unloading */ +@@ -1412,13 +1497,13 @@ static void free_module(struct module *m + module_free(mod, mod->module_core); + } + +-void *__symbol_get(const char *symbol) ++void *do_symbol_get(const char *symbol, struct module *caller) + { + struct module *owner; + unsigned long value; + + preempt_disable(); +- value = find_symbol(symbol, &owner, NULL, true, true); ++ value = find_symbol(symbol, &owner, NULL, true, true, caller); + if (IS_ERR_VALUE(value)) + value = 0; + else if (strong_try_module_get(owner)) +@@ -1427,7 +1512,7 @@ void *__symbol_get(const char *symbol) + + return (void *)value; + } +-EXPORT_SYMBOL_GPL(__symbol_get); ++EXPORT_SYMBOL_GPL(do_symbol_get); + + /* + * Ensure that an exported symbol [global namespace] does not already exist +@@ -1451,8 +1536,8 @@ static int verify_export_symbols(struct + + for (i = 0; i < ARRAY_SIZE(arr); i++) { + for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { +- if (!IS_ERR_VALUE(find_symbol(s->name, &owner, +- NULL, true, false))) { ++ if (!IS_ERR_VALUE(find_symbol(s->name, &owner, NULL, ++ true, false, mod))) { + printk(KERN_ERR + "%s: exports duplicate symbol %s" + " (owned by %s)\n", +@@ -1770,6 +1855,7 @@ static struct module *load_module(void _ + unsigned int unusedgplcrcindex; + unsigned int markersindex; + unsigned int markersstringsindex; ++ unsigned int namespaceindex; + struct module *mod; + long err = 0; + void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ +@@ -1866,6 +1952,7 @@ static struct module *load_module(void _ + #ifdef ARCH_UNWIND_SECTION_NAME + unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME); + #endif ++ namespaceindex = find_sec(hdr, sechdrs, secstrings, "__knamespace"); + + /* Don't keep modinfo and version sections. */ + sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; +@@ -2034,6 +2121,12 @@ static struct module *load_module(void _ + mod->unused_gpl_crcs + = (void *)sechdrs[unusedgplcrcindex].sh_addr; + ++ if (namespaceindex) { ++ mod->knamespace = (void *)sechdrs[namespaceindex].sh_addr; ++ mod->num_knamespaces = sechdrs[namespaceindex].sh_size / ++ sizeof(struct module_ns); ++ } ++ + #ifdef CONFIG_MODVERSIONS + if ((mod->num_syms && !crcindex) || + (mod->num_gpl_syms && !gplcrcindex) || @@ -264,3 +264,4 @@ usb/usb-gotemp.patch +namespace |
