]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
module: expose imported namespaces via sysfs
authorNicholas Sielicki <linux@opensource.nslick.com>
Sat, 7 Mar 2026 09:00:09 +0000 (03:00 -0600)
committerSami Tolvanen <samitolvanen@google.com>
Thu, 19 Mar 2026 22:24:55 +0000 (22:24 +0000)
Add /sys/module/*/import_ns to expose imported namespaces for
currently loaded modules. The file contains one namespace per line and
only exists for modules that import at least one namespace.

Previously, the only way for userspace to inspect the symbol
namespaces a module imports is to locate the .ko on disk and invoke
modinfo(8) to decompress/parse the metadata. The kernel validated
namespaces at load time, but it was otherwise discarded.

Exposing this data via sysfs provides a runtime mechanism to verify
which namespaces are being used by modules. For example, this allows
userspace to audit driver API access in Android GKI, which uses symbol
namespaces to restrict vendor drivers from using specific kernel
interfaces (e.g., direct filesystem access).

Signed-off-by: Nicholas Sielicki <linux@opensource.nslick.com>
[Sami: Updated the commit message to explain motivation.]
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Documentation/ABI/testing/sysfs-module
include/linux/module.h
kernel/module/main.c

index 6bc9af6229f0013f23acd4bb6f8d7c8e52637899..d5b7d19bd3103dff763f6f300a83f7a6c7224f1c 100644 (file)
@@ -48,6 +48,15 @@ Contact:     Kay Sievers <kay.sievers@vrfy.org>
 Description:   Show the initialization state(live, coming, going) of
                the module.
 
+What:          /sys/module/*/import_ns
+Date:          January 2026
+KernelVersion: 7.1
+Contact:       linux-modules@vger.kernel.org
+Description:   List of symbol namespaces imported by this module via
+               MODULE_IMPORT_NS(). Each namespace appears on a separate line.
+               This file only exists for modules that import at least one
+               namespace.
+
 What:          /sys/module/*/taint
 Date:          Jan 2012
 KernelVersion: 3.3
index 14f391b186c6d92677c33d28698e3f449aef830c..60ed1c3e0ed9582e672afd06179f224b7b69be14 100644 (file)
@@ -413,6 +413,7 @@ struct module {
        struct module_attribute *modinfo_attrs;
        const char *version;
        const char *srcversion;
+       const char *imported_namespaces;
        struct kobject *holders_dir;
 
        /* Exported symbols */
index ef2e2130972fe26399c1f1f52e403fa9dc116c3d..fc033137863dacc63699b3318424c29d0b051d59 100644 (file)
@@ -607,6 +607,36 @@ static const struct module_attribute modinfo_##field = {              \
 MODINFO_ATTR(version);
 MODINFO_ATTR(srcversion);
 
+static void setup_modinfo_import_ns(struct module *mod, const char *s)
+{
+       mod->imported_namespaces = NULL;
+}
+
+static ssize_t show_modinfo_import_ns(const struct module_attribute *mattr,
+                                     struct module_kobject *mk, char *buffer)
+{
+       return sysfs_emit(buffer, "%s\n", mk->mod->imported_namespaces);
+}
+
+static int modinfo_import_ns_exists(struct module *mod)
+{
+       return mod->imported_namespaces != NULL;
+}
+
+static void free_modinfo_import_ns(struct module *mod)
+{
+       kfree(mod->imported_namespaces);
+       mod->imported_namespaces = NULL;
+}
+
+static const struct module_attribute modinfo_import_ns = {
+       .attr = { .name = "import_ns", .mode = 0444 },
+       .show = show_modinfo_import_ns,
+       .setup = setup_modinfo_import_ns,
+       .test = modinfo_import_ns_exists,
+       .free = free_modinfo_import_ns,
+};
+
 static struct {
        char name[MODULE_NAME_LEN];
        char taints[MODULE_FLAGS_BUF_SIZE];
@@ -1058,6 +1088,7 @@ const struct module_attribute *const modinfo_attrs[] = {
        &module_uevent,
        &modinfo_version,
        &modinfo_srcversion,
+       &modinfo_import_ns,
        &modinfo_initstate,
        &modinfo_coresize,
 #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC
@@ -1760,11 +1791,43 @@ static void module_license_taint_check(struct module *mod, const char *license)
        }
 }
 
+static int copy_modinfo_import_ns(struct module *mod, struct load_info *info)
+{
+       char *ns;
+       size_t len, total_len = 0;
+       char *buf, *p;
+
+       for_each_modinfo_entry(ns, info, "import_ns")
+               total_len += strlen(ns) + 1;
+
+       if (!total_len) {
+               mod->imported_namespaces = NULL;
+               return 0;
+       }
+
+       buf = kmalloc(total_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       p = buf;
+       for_each_modinfo_entry(ns, info, "import_ns") {
+               len = strlen(ns);
+               memcpy(p, ns, len);
+               p += len;
+               *p++ = '\n';
+       }
+       /* Replace trailing newline with null terminator. */
+       *(p - 1) = '\0';
+
+       mod->imported_namespaces = buf;
+       return 0;
+}
+
 static int setup_modinfo(struct module *mod, struct load_info *info)
 {
        const struct module_attribute *attr;
        char *imported_namespace;
-       int i;
+       int i, err;
 
        for (i = 0; (attr = modinfo_attrs[i]); i++) {
                if (attr->setup)
@@ -1783,6 +1846,10 @@ static int setup_modinfo(struct module *mod, struct load_info *info)
                }
        }
 
+       err = copy_modinfo_import_ns(mod, info);
+       if (err)
+               return err;
+
        return 0;
 }