Commit 3344ea3a authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] MODULE_LICENSE and EXPORT_SYMBOL_GPL support

This implements EXPORT_SYMBOL_GPL and MODULE_LICENSE properly (so
restrictions are enforced).  Also fixes "proprietory" spelling.
parent 0dc68d4a
...@@ -105,7 +105,7 @@ extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in ...@@ -105,7 +105,7 @@ extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in
extern int tainted; extern int tainted;
extern const char *print_tainted(void); extern const char *print_tainted(void);
#define TAINT_PROPRIETORY_MODULE (1<<0) #define TAINT_PROPRIETARY_MODULE (1<<0)
#define TAINT_FORCED_MODULE (1<<1) #define TAINT_FORCED_MODULE (1<<1)
#define TAINT_UNSAFE_SMP (1<<2) #define TAINT_UNSAFE_SMP (1<<2)
#define TAINT_FORCED_RMMOD (1<<3) #define TAINT_FORCED_RMMOD (1<<3)
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <asm/uaccess.h> /* For struct exception_table_entry */ #include <asm/uaccess.h> /* For struct exception_table_entry */
/* Not Yet Implemented */ /* Not Yet Implemented */
#define MODULE_LICENSE(name)
#define MODULE_AUTHOR(name) #define MODULE_AUTHOR(name)
#define MODULE_DESCRIPTION(desc) #define MODULE_DESCRIPTION(desc)
#define MODULE_SUPPORTED_DEVICE(name) #define MODULE_SUPPORTED_DEVICE(name)
...@@ -57,11 +56,41 @@ extern const struct gtype##_id __mod_##gtype##_table \ ...@@ -57,11 +56,41 @@ extern const struct gtype##_id __mod_##gtype##_table \
#define THIS_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 v2" [GNU Public License v2]
* "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(".init.license"))) = license
#else /* !MODULE */ #else /* !MODULE */
#define MODULE_GENERIC_TABLE(gtype,name) #define MODULE_GENERIC_TABLE(gtype,name)
#define THIS_MODULE ((struct module *)0) #define THIS_MODULE ((struct module *)0)
#define MODULE_LICENSE(license)
#endif #endif
#define MODULE_DEVICE_TABLE(type,name) \ #define MODULE_DEVICE_TABLE(type,name) \
...@@ -75,6 +104,9 @@ struct kernel_symbol_group ...@@ -75,6 +104,9 @@ struct kernel_symbol_group
/* Module which owns it (if any) */ /* Module which owns it (if any) */
struct module *owner; struct module *owner;
/* Are we internal use only? */
int gplonly;
unsigned int num_syms; unsigned int num_syms;
const struct kernel_symbol *syms; const struct kernel_symbol *syms;
}; };
...@@ -101,7 +133,11 @@ void *__symbol_get_gpl(const char *symbol); ...@@ -101,7 +133,11 @@ void *__symbol_get_gpl(const char *symbol);
= { (unsigned long)&sym, MODULE_SYMBOL_PREFIX #sym } = { (unsigned long)&sym, MODULE_SYMBOL_PREFIX #sym }
#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) #define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym)
#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 module_ref struct module_ref
{ {
...@@ -128,6 +164,9 @@ struct module ...@@ -128,6 +164,9 @@ struct module
/* Exported symbols */ /* Exported symbols */
struct kernel_symbol_group symbols; struct kernel_symbol_group symbols;
/* GPL-only exported symbols. */
struct kernel_symbol_group gpl_symbols;
/* Exception tables */ /* Exception tables */
struct exception_table extable; struct exception_table extable;
...@@ -149,6 +188,9 @@ struct module ...@@ -149,6 +188,9 @@ struct module
/* Am I unsafe to unload? */ /* Am I unsafe to unload? */
int unsafe; int unsafe;
/* Am I GPL-compatible */
int license_gplok;
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
/* Reference counts */ /* Reference counts */
struct module_ref ref[NR_CPUS]; struct module_ref ref[NR_CPUS];
...@@ -293,6 +335,7 @@ struct module __this_module ...@@ -293,6 +335,7 @@ struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = { __attribute__((section(".gnu.linkonce.this_module"))) = {
.name = __stringify(KBUILD_MODNAME), .name = __stringify(KBUILD_MODNAME),
.symbols = { .owner = &__this_module }, .symbols = { .owner = &__this_module },
.gpl_symbols = { .owner = &__this_module, .gplonly = 1 },
.init = init_module, .init = init_module,
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module, .exit = cleanup_module,
......
...@@ -24,6 +24,8 @@ extern const struct exception_table_entry __start___ex_table[]; ...@@ -24,6 +24,8 @@ extern const struct exception_table_entry __start___ex_table[];
extern const struct exception_table_entry __stop___ex_table[]; extern const struct exception_table_entry __stop___ex_table[];
extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __start___ksymtab[];
extern const struct kernel_symbol __stop___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 */ /* Protects extables and symbol tables */
spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED;
...@@ -34,13 +36,20 @@ LIST_HEAD(symbols); ...@@ -34,13 +36,20 @@ LIST_HEAD(symbols);
static struct exception_table kernel_extable; static struct exception_table kernel_extable;
static struct kernel_symbol_group kernel_symbols; static struct kernel_symbol_group kernel_symbols;
static struct kernel_symbol_group kernel_gpl_symbols;
void __init extable_init(void) void __init extable_init(void)
{ {
/* Add kernel symbols to symbol table */ /* Add kernel symbols to symbol table */
kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab);
kernel_symbols.syms = __start___ksymtab; kernel_symbols.syms = __start___ksymtab;
kernel_symbols.gplonly = 0;
list_add(&kernel_symbols.list, &symbols); 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 */ /* Add kernel exception table to exception tables */
kernel_extable.num_entries = (__stop___ex_table -__start___ex_table); kernel_extable.num_entries = (__stop___ex_table -__start___ex_table);
......
...@@ -72,13 +72,16 @@ EXPORT_SYMBOL(init_module); ...@@ -72,13 +72,16 @@ EXPORT_SYMBOL(init_module);
/* Find a symbol, return value and the symbol group */ /* Find a symbol, return value and the symbol group */
static unsigned long __find_symbol(const char *name, 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; struct kernel_symbol_group *ks;
list_for_each_entry(ks, &symbols, list) { list_for_each_entry(ks, &symbols, list) {
unsigned int i; unsigned int i;
if (ks->gplonly && !gplok)
continue;
for (i = 0; i < ks->num_syms; i++) { for (i = 0; i < ks->num_syms; i++) {
if (strcmp(ks->syms[i].name, name) == 0) { if (strcmp(ks->syms[i].name, name) == 0) {
*group = ks; *group = ks;
...@@ -502,7 +505,7 @@ void __symbol_put(const char *symbol) ...@@ -502,7 +505,7 @@ void __symbol_put(const char *symbol)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&modlist_lock, flags); spin_lock_irqsave(&modlist_lock, flags);
if (!__find_symbol(symbol, &ksg)) if (!__find_symbol(symbol, &ksg, 1))
BUG(); BUG();
module_put(ksg->owner); module_put(ksg->owner);
spin_unlock_irqrestore(&modlist_lock, flags); spin_unlock_irqrestore(&modlist_lock, flags);
...@@ -721,7 +724,7 @@ unsigned long find_symbol_internal(Elf_Shdr *sechdrs, ...@@ -721,7 +724,7 @@ unsigned long find_symbol_internal(Elf_Shdr *sechdrs,
} }
/* Look in other modules... */ /* Look in other modules... */
spin_lock_irq(&modlist_lock); spin_lock_irq(&modlist_lock);
ret = __find_symbol(name, ksg); ret = __find_symbol(name, ksg, mod->license_gplok);
if (ret) { if (ret) {
/* This can fail due to OOM, or module unloading */ /* This can fail due to OOM, or module unloading */
if (!use_module(mod, (*ksg)->owner)) if (!use_module(mod, (*ksg)->owner))
...@@ -738,6 +741,7 @@ static void free_module(struct module *mod) ...@@ -738,6 +741,7 @@ static void free_module(struct module *mod)
list_del(&mod->list); list_del(&mod->list);
spin_lock_irq(&modlist_lock); spin_lock_irq(&modlist_lock);
list_del(&mod->symbols.list); list_del(&mod->symbols.list);
list_del(&mod->gpl_symbols.list);
list_del(&mod->extable.list); list_del(&mod->extable.list);
spin_unlock_irq(&modlist_lock); spin_unlock_irq(&modlist_lock);
...@@ -758,7 +762,7 @@ void *__symbol_get(const char *symbol) ...@@ -758,7 +762,7 @@ void *__symbol_get(const char *symbol)
unsigned long value, flags; unsigned long value, flags;
spin_lock_irqsave(&modlist_lock, flags); spin_lock_irqsave(&modlist_lock, flags);
value = __find_symbol(symbol, &ksg); value = __find_symbol(symbol, &ksg, 1);
if (value && !strong_try_module_get(ksg->owner)) if (value && !strong_try_module_get(ksg->owner))
value = 0; value = 0;
spin_unlock_irqrestore(&modlist_lock, flags); spin_unlock_irqrestore(&modlist_lock, flags);
...@@ -930,7 +934,34 @@ static void layout_sections(struct module *mod, ...@@ -930,7 +934,34 @@ static void layout_sections(struct module *mod,
} }
} }
/* Allocate and load the module */ static inline int license_is_gpl_compatible(const char *license)
{
return (strcmp(license, "GPL") == 0
|| strcmp(license, "GPL v2") == 0
|| strcmp(license, "GPL and additional rights") == 0
|| strcmp(license, "Dual BSD/GPL") == 0
|| strcmp(license, "Dual MPL/GPL") == 0);
}
static void set_license(struct module *mod, Elf_Shdr *sechdrs, int licenseidx)
{
char *license;
if (licenseidx)
license = (char *)sechdrs[licenseidx].sh_addr;
else
license = "unspecified";
mod->license_gplok = license_is_gpl_compatible(license);
if (!mod->license_gplok) {
printk(KERN_WARNING "%s: module license '%s' taints kernel.\n",
mod->name, license);
tainted |= TAINT_PROPRIETARY_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, static struct module *load_module(void *umod,
unsigned long len, unsigned long len,
const char *uargs) const char *uargs)
...@@ -939,7 +970,7 @@ static struct module *load_module(void *umod, ...@@ -939,7 +970,7 @@ static struct module *load_module(void *umod,
Elf_Shdr *sechdrs; Elf_Shdr *sechdrs;
char *secstrings, *args; char *secstrings, *args;
unsigned int i, symindex, exportindex, strindex, setupindex, exindex, unsigned int i, symindex, exportindex, strindex, setupindex, exindex,
modindex, obsparmindex; modindex, obsparmindex, licenseindex, gplindex;
long arglen; long arglen;
struct module *mod; struct module *mod;
long err = 0; long err = 0;
...@@ -975,7 +1006,7 @@ static struct module *load_module(void *umod, ...@@ -975,7 +1006,7 @@ static struct module *load_module(void *umod,
/* May not export symbols, or have setup params, so these may /* May not export symbols, or have setup params, so these may
not exist */ not exist */
exportindex = setupindex = obsparmindex = 0; exportindex = setupindex = obsparmindex = gplindex = licenseindex = 0;
/* And these should exist, but gcc whinges if we don't init them */ /* And these should exist, but gcc whinges if we don't init them */
symindex = strindex = exindex = modindex = 0; symindex = strindex = exindex = modindex = 0;
...@@ -1018,6 +1049,16 @@ static struct module *load_module(void *umod, ...@@ -1018,6 +1049,16 @@ static struct module *load_module(void *umod,
/* Obsolete MODULE_PARM() table */ /* Obsolete MODULE_PARM() table */
DEBUGP("Obsolete param found in section %u\n", i); DEBUGP("Obsolete param found in section %u\n", i);
obsparmindex = i; obsparmindex = i;
} else if (strcmp(secstrings+sechdrs[i].sh_name,".init.license")
== 0) {
/* MODULE_LICENSE() */
DEBUGP("Licence found in section %u\n", i);
licenseindex = i;
} else if (strcmp(secstrings+sechdrs[i].sh_name,
"__gpl_ksymtab") == 0) {
/* EXPORT_SYMBOL_GPL() */
DEBUGP("GPL symbols found in section %u\n", i);
gplindex = i;
} }
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
/* symbol and string tables for decoding later. */ /* symbol and string tables for decoding later. */
...@@ -1113,17 +1154,21 @@ static struct module *load_module(void *umod, ...@@ -1113,17 +1154,21 @@ static struct module *load_module(void *umod,
/* Now we've moved module, initialize linked lists, etc. */ /* Now we've moved module, initialize linked lists, etc. */
module_unload_init(mod); module_unload_init(mod);
/* Set up license info based on contents of section */
set_license(mod, sechdrs, licenseindex);
/* Fix up syms, so that st_value is a pointer to location. */ /* Fix up syms, so that st_value is a pointer to location. */
err = simplify_symbols(sechdrs, symindex, strindex, mod); err = simplify_symbols(sechdrs, symindex, strindex, mod);
if (err < 0) if (err < 0)
goto cleanup; goto cleanup;
/* Set up EXPORTed symbols */ /* Set up EXPORTed & EXPORT_GPLed symbols (section 0 is 0 length) */
if (exportindex) {
mod->symbols.num_syms = (sechdrs[exportindex].sh_size mod->symbols.num_syms = (sechdrs[exportindex].sh_size
/ sizeof(*mod->symbols.syms)); / sizeof(*mod->symbols.syms));
mod->symbols.syms = (void *)sechdrs[exportindex].sh_addr; mod->symbols.syms = (void *)sechdrs[exportindex].sh_addr;
} mod->gpl_symbols.num_syms = (sechdrs[gplindex].sh_size
/ sizeof(*mod->symbols.syms));
mod->gpl_symbols.syms = (void *)sechdrs[gplindex].sh_addr;
/* Set up exception table */ /* Set up exception table */
if (exindex) { if (exindex) {
...@@ -1228,6 +1273,7 @@ sys_init_module(void *umod, ...@@ -1228,6 +1273,7 @@ sys_init_module(void *umod,
spin_lock_irq(&modlist_lock); spin_lock_irq(&modlist_lock);
list_add(&mod->extable.list, &extables); list_add(&mod->extable.list, &extables);
list_add_tail(&mod->symbols.list, &symbols); list_add_tail(&mod->symbols.list, &symbols);
list_add_tail(&mod->gpl_symbols.list, &symbols);
spin_unlock_irq(&modlist_lock); spin_unlock_irq(&modlist_lock);
list_add(&mod->list, &modules); list_add(&mod->list, &modules);
......
...@@ -103,7 +103,7 @@ NORET_TYPE void panic(const char * fmt, ...) ...@@ -103,7 +103,7 @@ NORET_TYPE void panic(const char * fmt, ...)
/** /**
* print_tainted - return a string to represent the kernel taint state. * print_tainted - return a string to represent the kernel taint state.
* *
* 'P' - Proprietory module has been loaded. * 'P' - Proprietary module has been loaded.
* 'F' - Module has been forcibly loaded. * 'F' - Module has been forcibly loaded.
* 'S' - SMP with CPUs not designed for SMP. * 'S' - SMP with CPUs not designed for SMP.
* *
...@@ -115,7 +115,7 @@ const char *print_tainted() ...@@ -115,7 +115,7 @@ const char *print_tainted()
static char buf[20]; static char buf[20];
if (tainted) { if (tainted) {
snprintf(buf, sizeof(buf), "Tainted: %c%c%c", snprintf(buf, sizeof(buf), "Tainted: %c%c%c",
tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G', tainted & TAINT_PROPRIETARY_MODULE ? 'P' : 'G',
tainted & TAINT_FORCED_MODULE ? 'F' : ' ', tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
tainted & TAINT_UNSAFE_SMP ? 'S' : ' '); tainted & TAINT_UNSAFE_SMP ? 'S' : ' ');
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment