From: Andreas Gruenbacher Subject: Novell/external support flag in modules Patch-mainline: Never, SLES feature Upon module load, check if a module is supported, and set the N (TAINT_NO_SUPPORT) or X (TAINT_EXTERNAL_SUPPORT) tail flags for unsupported or externally suported modules. Changes: * Feb 21 2008 - jeffm - 2.6.25 claimed -S and bumped the flags up a bit, modpost now uses -N Signed-off-by: Andreas Gruenbacher --- Documentation/kernel-parameters.txt | 5 ++ Documentation/sysctl/kernel.txt | 12 ++++++ Makefile | 5 ++ include/linux/kernel.h | 9 ++++ include/linux/module.h | 1 kernel/ksysfs.c | 23 ++++++++++++ kernel/module.c | 65 ++++++++++++++++++++++++++++++++++++ kernel/panic.c | 9 ++++ kernel/sysctl.c | 9 ++++ scripts/Makefile.modpost | 4 +- scripts/mod/modpost.c | 65 +++++++++++++++++++++++++++++++++++- 11 files changed, 205 insertions(+), 2 deletions(-) --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2504,6 +2504,11 @@ and is between 256 and 4096 characters. [X86] Set unknown_nmi_panic=1 early on boot. + unsupported Allow loading of unsupported kernel modules: + 0 = only allow supported modules, + 1 = warn when loading unsupported modules, + 2 = don't warn. + usbcore.autosuspend= [USB] The autosuspend time delay (in seconds) used for newly-detected USB devices (default 2). This --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -491,6 +491,18 @@ can be ORed together: instead of using the one provided by the hardware. 512 - A kernel warning has occurred. 1024 - A module from drivers/staging was loaded. + 0x40000000 - An unsupported kernel module was loaded. + 0x80000000 - An kernel module with external support was loaded. + +============================================================== + +unsupported: + +Allow to load unsupported kernel modules: + + 0 - refuse to load unsupported modules, + 1 - warn when loading unsupported modules, + 2 - don't warn. ============================================================== --- a/Makefile +++ b/Makefile @@ -362,6 +362,11 @@ KBUILD_AFLAGS_MODULE := -DMODULE KBUILD_CFLAGS_MODULE := -DMODULE KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds +# Warn about unsupported modules in kernels built inside Autobuild +ifneq ($(wildcard /.buildenv),) +CFLAGS += -DUNSUPPORTED_MODULES=2 +endif + # Read KERNELRELEASE from include/config/kernel.release (if it exists) KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -222,8 +222,10 @@ extern int panic_timeout; extern int panic_on_oops; extern int panic_on_unrecovered_nmi; extern int panic_on_io_nmi; +extern int unsupported; extern const char *print_tainted(void); extern void add_taint(unsigned flag); +extern void add_nonfatal_taint(unsigned flag); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); extern int root_mountflags; @@ -251,6 +253,13 @@ extern enum system_states { #define TAINT_CRAP 10 #define TAINT_FIRMWARE_WORKAROUND 11 +/* + * Take the upper bits to hopefully allow them + * to stay the same for more than one release. + */ +#define TAINT_NO_SUPPORT 30 +#define TAINT_EXTERNAL_SUPPORT 31 + extern const char hex_asc[]; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] --- a/include/linux/module.h +++ b/include/linux/module.h @@ -410,6 +410,7 @@ struct module *__module_address(unsigned bool is_module_address(unsigned long addr); bool is_module_percpu_address(unsigned long addr); bool is_module_text_address(unsigned long addr); +const char *supported_printable(int taint); static inline int within_module_core(unsigned long addr, struct module *mod) { --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -157,6 +157,28 @@ static struct bin_attribute notes_attr = struct kobject *kernel_kobj; EXPORT_SYMBOL_GPL(kernel_kobj); +const char *supported_printable(int taint) +{ + int mask = TAINT_PROPRIETARY_MODULE|TAINT_NO_SUPPORT; + if ((taint & mask) == mask) + return "No, Proprietary and Unsupported modules are loaded"; + else if (taint & TAINT_PROPRIETARY_MODULE) + return "No, Proprietary modules are loaded"; + else if (taint & TAINT_NO_SUPPORT) + return "No, Unsupported modules are loaded"; + else if (taint & TAINT_EXTERNAL_SUPPORT) + return "Yes, External"; + else + return "Yes"; +} + +static ssize_t supported_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", supported_printable(get_taint())); +} +KERNEL_ATTR_RO(supported); + static struct attribute * kernel_attrs[] = { #if defined(CONFIG_HOTPLUG) &uevent_seqnum_attr.attr, @@ -171,6 +193,7 @@ static struct attribute * kernel_attrs[] &kexec_crash_size_attr.attr, &vmcoreinfo_attr.attr, #endif + &supported_attr.attr, NULL }; --- a/kernel/module.c +++ b/kernel/module.c @@ -73,6 +73,20 @@ /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) +/* Allow unsupported modules switch. */ +#ifdef UNSUPPORTED_MODULES +int unsupported = UNSUPPORTED_MODULES; +#else +int unsupported = 2; /* don't warn when loading unsupported modules. */ +#endif + +static int __init unsupported_setup(char *str) +{ + get_option(&str, &unsupported); + return 1; +} +__setup("unsupported=", unsupported_setup); + /* * Mutex protects: * 1) List of modules (also safely readable with preempt_disable), @@ -931,10 +945,36 @@ static struct module_attribute initstate .show = show_initstate, }; +static void setup_modinfo_supported(struct module *mod, const char *s) +{ + if (!s) { + mod->taints |= (1 << TAINT_NO_SUPPORT); + return; + } + + if (strcmp(s, "external") == 0) + mod->taints |= (1 << TAINT_EXTERNAL_SUPPORT); + else if (strcmp(s, "yes")) + mod->taints |= (1 << TAINT_NO_SUPPORT); +} + +static ssize_t show_modinfo_supported(struct module_attribute *mattr, + struct module *mod, char *buffer) +{ + return sprintf(buffer, "%s\n", supported_printable(mod->taints)); +} + +static struct module_attribute modinfo_supported = { + .attr = { .name = "supported", .mode = 0444 }, + .show = show_modinfo_supported, + .setup = setup_modinfo_supported, +}; + static struct module_attribute *modinfo_attrs[] = { &modinfo_version, &modinfo_srcversion, &initstate, + &modinfo_supported, #ifdef CONFIG_MODULE_UNLOAD &refcnt, #endif @@ -1476,6 +1516,26 @@ static int mod_sysfs_setup(struct module add_sect_attrs(mod, info); add_notes_attrs(mod, info); + /* We don't use add_taint() here because it also disables lockdep. */ + if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) + add_nonfatal_taint(TAINT_EXTERNAL_SUPPORT); + else if (mod->taints == (1 << TAINT_NO_SUPPORT)) { + if (unsupported == 0) { + printk(KERN_WARNING "%s: module not supported by " + "Novell, refusing to load. To override, echo " + "1 > /proc/sys/kernel/unsupported\n", mod->name); + err = -ENOEXEC; + goto free_hdr; + } + add_nonfatal_taint(TAINT_NO_SUPPORT); + if (unsupported == 1) { + printk(KERN_WARNING "%s: module is not supported by " + "Novell. Novell Technical Services may decline " + "your support request if it involves a kernel " + "fault.\n", mod->name); + } + } + kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD); return 0; @@ -3005,6 +3065,10 @@ static char *module_flags(struct module buf[bx++] = 'F'; if (mod->taints & (1 << TAINT_CRAP)) buf[bx++] = 'C'; + if (mod->taints & (1 << TAINT_NO_SUPPORT)) + buf[bx++] = 'N'; + if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT)) + buf[bx++] = 'X'; /* * TAINT_FORCED_RMMOD: could be added. * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't @@ -3217,6 +3281,7 @@ void print_modules(void) if (last_unloaded_module[0]) printk(" [last unloaded: %s]", last_unloaded_module); printk("\n"); + printk("Supported: %s\n", supported_printable(get_taint())); } #ifdef CONFIG_MODVERSIONS --- a/kernel/panic.c +++ b/kernel/panic.c @@ -174,6 +174,8 @@ static const struct tnt tnts[] = { { TAINT_WARN, 'W', ' ' }, { TAINT_CRAP, 'C', ' ' }, { TAINT_FIRMWARE_WORKAROUND, 'I', ' ' }, + { TAINT_NO_SUPPORT, 'N', ' ' }, + { TAINT_EXTERNAL_SUPPORT, 'X', ' ' }, }; /** @@ -191,6 +193,8 @@ static const struct tnt tnts[] = { * 'W' - Taint on warning. * 'C' - modules from drivers/staging are loaded. * 'I' - Working around severe firmware bug. + * 'N' - Unsuported modules loaded. + * 'X' - Modules with external support loaded. * * The string is overwritten by the next call to print_tainted(). */ @@ -226,6 +230,11 @@ unsigned long get_taint(void) return tainted_mask; } +void add_nonfatal_taint(unsigned flag) +{ + set_bit(flag, &tainted_mask); +} + void add_taint(unsigned flag) { /* --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -664,6 +664,15 @@ static struct ctl_table kern_table[] = { .extra1 = &pid_max_min, .extra2 = &pid_max_max, }, +#ifdef CONFIG_MODULES + { + .procname = "unsupported", + .data = &unsupported, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, +#endif { .procname = "panic_on_oops", .data = &panic_on_oops, --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -81,7 +81,9 @@ modpost = scripts/mod/modpost $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ - $(if $(cross_build),-c) + $(if $(cross_build),-c) \ + -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \ + $(objtree)/Module.supported /dev/null)) quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules cmd_modpost = $(modpost) -s --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1639,6 +1639,48 @@ static void check_sec_ref(struct module } } +void *supported_file; +unsigned long supported_size; + +static const char *supported(struct module *mod) +{ + unsigned long pos = 0; + char *line; + + /* In a first shot, do a simple linear scan. */ + while ((line = get_next_line(&pos, supported_file, + supported_size))) { + const char *basename, *how = "yes"; + char *l = line; + + /* optional type-of-support flag */ + for (l = line; *l != '\0'; l++) { + if (*l == ' ' || *l == '\t') { + *l = '\0'; + how = l + 1; + break; + } + } + + /* skip directory components */ + if ((l = strrchr(line, '/'))) + line = l + 1; + /* strip .ko extension */ + l = line + strlen(line); + if (l - line > 3 && !strcmp(l-3, ".ko")) + *(l-3) = '\0'; + + /* skip directory components */ + if ((basename = strrchr(mod->name, '/'))) + basename++; + else + basename = mod->name; + if (!strcmp(basename, line)) + return how; + } + return NULL; +} + static void read_symbols(char *modname) { const char *symname; @@ -1826,6 +1868,13 @@ static void add_staging_flag(struct buff buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } +static void add_supported_flag(struct buffer *b, struct module *mod) +{ + const char *how = supported(mod); + if (how) + buf_printf(b, "\nMODULE_INFO(supported, \"%s\");\n", how); +} + /** * Record CRCs for unresolved symbols **/ @@ -1966,6 +2015,13 @@ static void write_if_changed(struct buff fclose(file); } +static void read_supported(const char *fname) +{ + supported_file = grab_file(fname, &supported_size); + if (!supported_file) + ; /* ignore error */ +} + /* parse Module.symvers file. line format: * 0x12345678symbolmodule[[export]something] **/ @@ -2059,12 +2115,13 @@ int main(int argc, char **argv) struct buffer buf = { }; char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL; + const char *supported = NULL; int opt; int err; struct ext_sym_list *extsym_iter; struct ext_sym_list *extsym_start = NULL; - while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) { + while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:N:")) != -1) { switch (opt) { case 'i': kernel_read = optarg; @@ -2102,11 +2159,16 @@ int main(int argc, char **argv) case 'w': warn_unresolved = 1; break; + case 'N': + supported = optarg; + break; default: exit(1); } } + if (supported) + read_supported(supported); if (kernel_read) read_dump(kernel_read, 1); if (module_read) @@ -2139,6 +2201,7 @@ int main(int argc, char **argv) add_header(&buf, mod); add_staging_flag(&buf, mod->name); + add_supported_flag(&buf, mod); err |= add_versions(&buf, mod); add_depends(&buf, mod, modules); add_moddevtable(&buf, mod);