Blob Blame History Raw
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Novell/external support flag in modules
Patch-mainline: Never, SLES feature
References: bsc#148091

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 <agruen@suse.de>
---
 Documentation/admin-guide/kernel-parameters.txt |    8 ++
 Documentation/sysctl/kernel.txt                 |   12 +++
 Makefile                                        |    5 +
 include/linux/kernel.h                          |   15 ++++
 include/linux/module.h                          |    3 
 init/Kconfig.suse                               |   18 +++++
 kernel/ksysfs.c                                 |   27 +++++++
 kernel/module.c                                 |   83 ++++++++++++++++++++++++
 kernel/panic.c                                  |    6 +
 kernel/sysctl.c                                 |    9 ++
 scripts/Makefile.modpost                        |    6 +
 scripts/mod/modpost.c                           |   79 ++++++++++++++++++++++
 12 files changed, 269 insertions(+), 2 deletions(-)

--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4766,6 +4766,14 @@
 	unknown_nmi_panic
 			[X86] Cause panic on unknown NMI.
 
+	unsupported	Allow loading of unsupported kernel modules:
+			0 = only allow supported modules,
+			1 = warn when loading unsupported modules,
+			2 = don't warn.
+
+			CONFIG_SUSE_KERNEL_SUPPORTED must be enabled for this
+			to have any effect.
+
 	usbcore.authorized_default=
 			[USB] Default USB device authorization:
 			(default -1 = authorized except for wireless USB,
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -974,6 +974,18 @@ can be ORed together:
        signature.
 16384 - A soft lockup has previously occurred on the system.
 32768 - The kernel has been live patched.
+ 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
@@ -412,6 +412,11 @@ KBUILD_CFLAGS_MODULE  := -DMODULE
 KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
 LDFLAGS :=
 
+# 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)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -461,6 +461,9 @@ extern int panic_on_unrecovered_nmi;
 extern int panic_on_io_nmi;
 extern int panic_on_warn;
 extern int sysctl_panic_on_rcu_stall;
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+extern int suse_unsupported;
+#endif
 extern int sysctl_panic_on_stackoverflow;
 
 extern bool crash_kexec_post_notifiers;
@@ -519,7 +522,19 @@ extern enum system_states {
 #define TAINT_UNSIGNED_MODULE		13
 #define TAINT_SOFTLOCKUP		14
 #define TAINT_LIVEPATCH			15
+/* !!! Keep TAINT_FLAGS_COUNT in sync !!! */
+
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+/*
+ * 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
+#define TAINT_FLAGS_COUNT		32
+#else
 #define TAINT_FLAGS_COUNT		16
+#endif
 
 struct taint_flag {
 	char c_true;	/* character printed when tainted */
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -496,6 +496,9 @@ bool is_module_address(unsigned long add
 bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr);
 bool is_module_percpu_address(unsigned long addr);
 bool is_module_text_address(unsigned long addr);
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+const char *supported_printable(int taint);
+#endif
 
 static inline bool within_module_core(unsigned long addr,
 				      const struct module *mod)
--- a/init/Kconfig.suse
+++ b/init/Kconfig.suse
@@ -1,2 +1,20 @@
 config SUSE_KERNEL
 	def_bool y
+
+config SUSE_KERNEL_SUPPORTED
+	bool "Enable enterprise support facility"
+	depends on SUSE_KERNEL
+	help
+	  This feature enables the handling of the "supported" module flag.
+	  This flag can be used to report unsupported module loads or even
+	  refuse them entirely. It is useful when ensuring that the kernel
+	  remains in a state that SUSE, or its technical partners, is
+	  prepared to support.
+
+	  Modules in the list of supported modules will be marked supported
+	  on build. The default enforcement mode is to report, but not
+	  deny, loading of unsupported modules.
+
+	  If you aren't building a kernel for an enterprise distribution,
+	  say n.
+
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -215,6 +215,30 @@ static struct bin_attribute notes_attr _
 struct kobject *kernel_kobj;
 EXPORT_SYMBOL_GPL(kernel_kobj);
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+const char *supported_printable(int taint)
+{
+	int mask = (1 << TAINT_PROPRIETARY_MODULE) | (1 << TAINT_NO_SUPPORT);
+	if ((taint & mask) == mask)
+		return "No, Proprietary and Unsupported modules are loaded";
+	else if (taint & (1 << TAINT_PROPRIETARY_MODULE))
+		return "No, Proprietary modules are loaded";
+	else if (taint & (1 << TAINT_NO_SUPPORT))
+		return "No, Unsupported modules are loaded";
+	else if (taint & (1 << 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);
+#endif
+
 static struct attribute * kernel_attrs[] = {
 	&fscaps_attr.attr,
 	&uevent_seqnum_attr.attr,
@@ -236,6 +260,9 @@ static struct attribute * kernel_attrs[]
 	&rcu_expedited_attr.attr,
 	&rcu_normal_attr.attr,
 #endif
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	&supported_attr.attr,
+#endif
 	NULL
 };
 
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -90,6 +90,22 @@
 /* If this is set, the section belongs in the init part of the module */
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+/* Allow unsupported modules switch. */
+#ifdef UNSUPPORTED_MODULES
+int suse_unsupported = UNSUPPORTED_MODULES;
+#else
+int suse_unsupported = 2;  /* don't warn when loading unsupported modules. */
+#endif
+
+static int __init unsupported_setup(char *str)
+{
+	get_option(&str, &suse_unsupported);
+	return 1;
+}
+__setup("unsupported=", unsupported_setup);
+#endif
+
 /*
  * Mutex protects:
  * 1) List of modules (also safely readable with preempt_disable),
@@ -1156,6 +1172,12 @@ static size_t module_flags_taint(struct
 			buf[l++] = taint_flags[i].c_true;
 	}
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	if (mod->taints & (1 << TAINT_NO_SUPPORT))
+		buf[l++] = 'N';
+	if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT))
+		buf[l++] = 'X';
+#endif
 	return l;
 }
 
@@ -1228,6 +1250,33 @@ static ssize_t show_taint(struct module_
 static struct module_attribute modinfo_taint =
 	__ATTR(taint, 0444, show_taint, NULL);
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+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_kobject *mk, char *buffer)
+{
+	return sprintf(buffer, "%s\n", supported_printable(mk->mod->taints));
+}
+
+static struct module_attribute modinfo_supported = {
+	.attr = { .name = "supported", .mode = 0444 },
+	.show = show_modinfo_supported,
+	.setup = setup_modinfo_supported,
+};
+#endif
+
 static struct module_attribute *modinfo_attrs[] = {
 	&module_uevent,
 	&modinfo_version,
@@ -1236,6 +1285,9 @@ static struct module_attribute *modinfo_
 	&modinfo_coresize,
 	&modinfo_initsize,
 	&modinfo_taint,
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	&modinfo_supported,
+#endif
 #ifdef CONFIG_MODULE_UNLOAD
 	&modinfo_refcnt,
 #endif
@@ -1790,8 +1842,36 @@ static int mod_sysfs_setup(struct module
 	add_sect_attrs(mod, info);
 	add_notes_attrs(mod, info);
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT))
+		add_taint(TAINT_EXTERNAL_SUPPORT, LOCKDEP_STILL_OK);
+	else if (mod->taints & (1 << TAINT_NO_SUPPORT)) {
+		if (suse_unsupported == 0) {
+			printk(KERN_WARNING "%s: module not supported by "
+			       "SUSE, refusing to load. To override, echo "
+			       "1 > /proc/sys/kernel/unsupported\n", mod->name);
+			err = -ENOEXEC;
+			goto out_remove_attrs;
+		}
+		add_taint(TAINT_NO_SUPPORT, LOCKDEP_STILL_OK);
+		if (suse_unsupported == 1) {
+			printk(KERN_WARNING "%s: module is not supported by "
+			       "SUSE. Our support organization may not be "
+			       "able to address your support request if it "
+			       "involves a kernel fault.\n", mod->name);
+		}
+	}
+#endif
+
 	return 0;
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+out_remove_attrs:
+	remove_notes_attrs(mod);
+	remove_sect_attrs(mod);
+	del_usage_links(mod);
+	module_remove_modinfo_attrs(mod);
+#endif
 out_unreg_param:
 	module_param_sysfs_remove(mod);
 out_unreg_holders:
@@ -4492,6 +4572,9 @@ void print_modules(void)
 	if (last_unloaded_module[0])
 		pr_cont(" [last unloaded: %s]", last_unloaded_module);
 	pr_cont("\n");
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	printk("Supported: %s\n", supported_printable(get_taint()));
+#endif
 }
 
 #ifdef CONFIG_MODVERSIONS
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -325,6 +325,10 @@ const struct taint_flag taint_flags[TAIN
 	{ 'E', ' ', true },	/* TAINT_UNSIGNED_MODULE */
 	{ 'L', ' ', false },	/* TAINT_SOFTLOCKUP */
 	{ 'K', ' ', true },	/* TAINT_LIVEPATCH */
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	{ 'N', ' ', true },	/* TAINT_NO_SUPPORT */
+	{ 'X', ' ', true },	/* TAINT_EXTERNAL_SUPPORT */
+#endif
 };
 
 /**
@@ -346,6 +350,8 @@ const struct taint_flag taint_flags[TAIN
  *  'E' - Unsigned module has been loaded.
  *  'L' - A soft lockup has previously occurred.
  *  'K' - Kernel has been live patched.
+ *  'N' - Unsuported modules loaded.
+ *  'X' - Modules with external support loaded.
  *
  *	The string is overwritten by the next call to print_tainted().
  */
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -825,6 +825,15 @@ static struct ctl_table kern_table[] = {
 		.extra1		= &pid_max_min,
 		.extra2		= &pid_max_max,
 	},
+#if defined(CONFIG_MODULES) && defined(CONFIG_SUSE_KERNEL_SUPPORTED)
+	{
+		.procname	= "unsupported",
+		.data		= &suse_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
@@ -78,7 +78,11 @@ modpost = scripts/mod/modpost
  $(if $(KBUILD_EXTMOD),-o $(modulesymfile))      \
  $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S)      \
  $(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E)  \
- $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w)
+ $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \
+ $(if $(CONFIG_SUSE_KERNEL_SUPPORTED),              \
+      -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \
+				$(objtree)/Module.supported \
+				$(srctree)/Module.supported /dev/null)))
 
 MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS)))
 
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1940,6 +1940,50 @@ static char *remove_dot(char *s)
 	return s;
 }
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+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;
+}
+#endif
+
 static void read_symbols(char *modname)
 {
 	const char *symname;
@@ -2167,6 +2211,15 @@ static void add_staging_flag(struct buff
  */
 #define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+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);
+}
+#endif
+
 /**
  * Record CRCs for unresolved symbols
  **/
@@ -2314,6 +2367,15 @@ static void write_if_changed(struct buff
 	fclose(file);
 }
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+static void read_supported(const char *fname)
+{
+	supported_file = grab_file(fname, &supported_size);
+	if (!supported_file)
+		; /* ignore error */
+}
+#endif
+
 /* parse Module.symvers file. line format:
  * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
  **/
@@ -2410,12 +2472,15 @@ int main(int argc, char **argv)
 	struct buffer buf = { };
 	char *kernel_read = NULL, *module_read = NULL;
 	char *dump_write = NULL, *files_source = NULL;
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	const char *supported = NULL;
+#endif
 	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:mnsST:o:awM:K:E")) != -1) {
+	while ((opt = getopt(argc, argv, "i:I:e:mnsST:o:awM:K:EN:")) != -1) {
 		switch (opt) {
 		case 'i':
 			kernel_read = optarg;
@@ -2459,11 +2524,20 @@ int main(int argc, char **argv)
 		case 'E':
 			sec_mismatch_fatal = 1;
 			break;
+		case 'N':
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+			supported = optarg;
+#endif
+			break;
 		default:
 			exit(1);
 		}
 	}
 
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+	if (supported)
+		read_supported(supported);
+#endif
 	if (kernel_read)
 		read_dump(kernel_read, 1);
 	if (module_read)
@@ -2501,6 +2575,9 @@ int main(int argc, char **argv)
 		add_intree_flag(&buf, !external_module);
 		add_retpoline(&buf);
 		add_staging_flag(&buf, mod->name);
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+		add_supported_flag(&buf, mod);
+#endif
 		err |= add_versions(&buf, mod);
 		add_depends(&buf, mod, modules);
 		add_moddevtable(&buf, mod);