From: Andreas Gruenbacher <agruen@suse.de>
Subject: SUSE/external support flag in modules
Patch-mainline: Never, SLES feature
References: bsc#148091 bsc#974406
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
* Jun 26 2019 - msuchanek - refresh for Linux 5.2
* Oct 15 2019 - lpechacek
- squashed module-Inform-user-when-loading-externally-supported.patch
(bsc#974406) into this one
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
Documentation/admin-guide/kernel-parameters.txt | 8 ++
Documentation/admin-guide/sysctl/kernel.rst | 53 +++++++++------
Makefile | 5 +
include/linux/kernel.h | 17 ++++
include/linux/module.h | 3
init/Kconfig.suse | 18 +++++
kernel/ksysfs.c | 27 +++++++
kernel/module.c | 84 ++++++++++++++++++++++++
kernel/panic.c | 3
kernel/sysctl.c | 9 ++
scripts/Makefile.modpost | 6 +
scripts/mod/modpost.c | 80 ++++++++++++++++++++++
12 files changed, 291 insertions(+), 22 deletions(-)
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4987,6 +4987,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/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -1067,30 +1067,43 @@ tainted
Non-zero if the kernel has been tainted. Numeric values, which can be
ORed together. The letters are seen in "Tainted" line of Oops reports.
-====== ===== ==============================================================
- 1 `(P)` proprietary module was loaded
- 2 `(F)` module was force loaded
- 4 `(S)` SMP kernel oops on an officially SMP incapable processor
- 8 `(R)` module was force unloaded
- 16 `(M)` processor reported a Machine Check Exception (MCE)
- 32 `(B)` bad page referenced or some unexpected page flags
- 64 `(U)` taint requested by userspace application
- 128 `(D)` kernel died recently, i.e. there was an OOPS or BUG
- 256 `(A)` an ACPI table was overridden by user
- 512 `(W)` kernel issued warning
- 1024 `(C)` staging driver was loaded
- 2048 `(I)` workaround for bug in platform firmware applied
- 4096 `(O)` externally-built ("out-of-tree") module was loaded
- 8192 `(E)` unsigned module was loaded
- 16384 `(L)` soft lockup occurred
- 32768 `(K)` kernel has been live patched
- 65536 `(X)` Auxiliary taint, defined and used by for distros
-131072 `(T)` The kernel was built with the struct randomization plugin
-====== ===== ==============================================================
+========== ===== ==========================================================
+ 1 `(P)` proprietary module was loaded
+ 2 `(F)` module was force loaded
+ 4 `(S)` SMP kernel oops on an officially SMP incapable processor
+ 8 `(R)` module was force unloaded
+ 16 `(M)` processor reported a Machine Check Exception (MCE)
+ 32 `(B)` bad page referenced or some unexpected page flags
+ 64 `(U)` taint requested by userspace application
+ 128 `(D)` kernel died recently, i.e. there was an OOPS or BUG
+ 256 `(A)` an ACPI table was overridden by user
+ 512 `(W)` kernel issued warning
+ 1024 `(C)` staging driver was loaded
+ 2048 `(I)` workaround for bug in platform firmware applied
+ 4096 `(O)` externally-built ("out-of-tree") module was loaded
+ 8192 `(E)` unsigned module was loaded
+ 16384 `(L)` soft lockup occurred
+ 32768 `(K)` kernel has been live patched
+ 65536 `(X)` A kernel module with external support was loaded
+ 131072 `(T)` The kernel was built with the struct randomization plugin
+2147483648 `(N)` An unsupported kernel module was loaded
+========== ===== ==========================================================
See Documentation/admin-guide/tainted-kernels.rst for more information.
+unsupported:
+============
+
+Allow to load unsupported kernel modules:
+
+ = =========================================================================
+ 0 refuse to load unsupported modules,
+ 1 warn when loading unsupported modules,
+ 2 don't warn.
+ = =========================================================================
+
+
threads-max:
============
--- a/Makefile
+++ b/Makefile
@@ -336,6 +336,11 @@ else
include scripts/Kbuild.include
+# 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
@@ -500,6 +500,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;
@@ -568,6 +571,20 @@ extern enum system_states {
#define TAINT_RANDSTRUCT 17
#define TAINT_FLAGS_COUNT 18
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+/*
+ * Take the upper bits to hopefully allow them
+ * to stay the same for more than one release.
+ */
+# define TAINT_EXTERNAL_SUPPORT TAINT_AUX
+# define TAINT_NO_SUPPORT 31
+# if TAINT_FLAGS_COUNT >= TAINT_NO_SUPPORT
+# error Upstream taint flags overlap with SUSE flags
+# endif
+# undef TAINT_FLAGS_COUNT
+# define TAINT_FLAGS_COUNT 32
+#endif
+
struct taint_flag {
char c_true; /* character printed when tainted */
char c_false; /* character printed when not tainted */
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -535,6 +535,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
@@ -208,6 +208,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,
@@ -229,6 +253,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
@@ -77,6 +77,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),
@@ -1189,6 +1205,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;
}
@@ -1260,6 +1282,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,
@@ -1268,6 +1317,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
@@ -1897,8 +1949,37 @@ 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)) {
+ pr_notice("%s: externally supported module, "
+ "setting X kernel taint flag.\n", mod->name);
+ 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);
+#endif
out_unreg_modinfo_attrs:
module_remove_modinfo_attrs(mod, -1);
out_unreg_param:
@@ -4553,6 +4634,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
@@ -368,6 +368,9 @@ const struct taint_flag taint_flags[TAIN
[ TAINT_LIVEPATCH ] = { 'K', ' ', true },
[ TAINT_AUX ] = { 'X', ' ', true },
[ TAINT_RANDSTRUCT ] = { 'T', ' ', true },
+#ifdef CONFIG_SUSE_KERNEL_SUPPORTED
+ [ TAINT_NO_SUPPORT ] = { 'N', ' ', true },
+#endif
};
/**
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -830,6 +830,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
@@ -57,7 +57,11 @@ MODPOST = scripts/mod/modpost \
$(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
$(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-M) \
- $(if $(KBUILD_MODPOST_WARN),-w)
+ $(if $(KBUILD_MODPOST_WARN),-w) \
+ $(if $(CONFIG_SUSE_KERNEL_SUPPORTED), \
+ -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \
+ $(objtree)/Module.supported \
+ $(srctree)/Module.supported /dev/null)))
ifdef MODPOST_VMLINUX
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -19,6 +19,7 @@
#include <stdbool.h>
#include <errno.h>
#include "modpost.h"
+#include "../../include/generated/autoconf.h"
#include "../../include/linux/license.h"
/* Are we using CONFIG_MODVERSIONS? */
@@ -2000,6 +2001,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(const char *modname)
{
const char *symname;
@@ -2285,6 +2330,15 @@ static void add_staging_flag(struct buff
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
}
+#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
**/
@@ -2421,6 +2475,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>namespace
**/
@@ -2549,12 +2612,15 @@ int main(int argc, char **argv)
char *kernel_read = NULL, *module_read = NULL;
char *missing_namespace_deps = 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:mnsT:o:awEMd:")) != -1) {
+ while ((opt = getopt(argc, argv, "i:I:e:mnsT:o:awEMd:N:")) != -1) {
switch (opt) {
case 'i':
kernel_read = optarg;
@@ -2601,11 +2667,20 @@ int main(int argc, char **argv)
case 'd':
missing_namespace_deps = optarg;
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)
@@ -2640,6 +2715,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);
add_moddevtable(&buf, mod);