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).
Showing the changes to kernel/module (1 of 3 files). Show all
Unified · Split
| @@ -607,6 +607,36 @@ static const struct module_attribute modinfo_##field = { \ · 606 unmodified lines | ||
| 607 | 607 | MODINFO_ATTR(version); |
| 608 | 608 | MODINFO_ATTR(srcversion); |
| 609 | 609 | |
| 610 | +static void setup_modinfo_import_ns(struct module *mod, const char *s) | |
| 611 | +{ | |
| 612 | + mod->imported_namespaces = NULL; | |
| 613 | +} | |
| 614 | + | |
| 615 | +static ssize_t show_modinfo_import_ns(const struct module_attribute *mattr, | |
| 616 | + struct module_kobject *mk, char *buffer) | |
| 617 | +{ | |
| 618 | + return sysfs_emit(buffer, "%s\n", mk->mod->imported_namespaces); | |
| 619 | +} | |
| 620 | + | |
| 621 | +static int modinfo_import_ns_exists(struct module *mod) | |
| 622 | +{ | |
| 623 | + return mod->imported_namespaces != NULL; | |
| 624 | +} | |
| 625 | + | |
| 626 | +static void free_modinfo_import_ns(struct module *mod) | |
| 627 | +{ | |
| 628 | + kfree(mod->imported_namespaces); | |
| 629 | + mod->imported_namespaces = NULL; | |
| 630 | +} | |
| 631 | + | |
| 632 | +static const struct module_attribute modinfo_import_ns = { | |
| 633 | + .attr = { .name = "import_ns", .mode = 0444 }, | |
| 634 | + .show = show_modinfo_import_ns, | |
| 635 | + .setup = setup_modinfo_import_ns, | |
| 636 | + .test = modinfo_import_ns_exists, | |
| 637 | + .free = free_modinfo_import_ns, | |
| 638 | +}; | |
| 639 | + | |
| 610 | 640 | static struct { |
| 611 | 641 | char name[MODULE_NAME_LEN]; |
| 612 | 642 | char taints[MODULE_FLAGS_BUF_SIZE]; |
| @@ -1058,6 +1088,7 @@ const struct module_attribute *const modinfo_attrs[] = { · 445 unmodified lines | ||
| 1058 | 1088 | &module_uevent, |
| 1059 | 1089 | &modinfo_version, |
| 1060 | 1090 | &modinfo_srcversion, |
| 1091 | + &modinfo_import_ns, | |
| 1061 | 1092 | &modinfo_initstate, |
| 1062 | 1093 | &modinfo_coresize, |
| 1063 | 1094 | #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC |
| @@ -1760,11 +1791,43 @@ static void module_license_taint_check(struct module *mod, const char *license) · 696 unmodified lines | ||
| 1760 | 1791 | } |
| 1761 | 1792 | } |
| 1762 | 1793 | |
| 1794 | +static int copy_modinfo_import_ns(struct module *mod, struct load_info *info) | |
| 1795 | +{ | |
| 1796 | + char *ns; | |
| 1797 | + size_t len, total_len = 0; | |
| 1798 | + char *buf, *p; | |
| 1799 | + | |
| 1800 | + for_each_modinfo_entry(ns, info, "import_ns") | |
| 1801 | + total_len += strlen(ns) + 1; | |
| 1802 | + | |
| 1803 | + if (!total_len) { | |
| 1804 | + mod->imported_namespaces = NULL; | |
| 1805 | + return 0; | |
| 1806 | + } | |
| 1807 | + | |
| 1808 | + buf = kmalloc(total_len, GFP_KERNEL); | |
| 1809 | + if (!buf) | |
| 1810 | + return -ENOMEM; | |
| 1811 | + | |
| 1812 | + p = buf; | |
| 1813 | + for_each_modinfo_entry(ns, info, "import_ns") { | |
| 1814 | + len = strlen(ns); | |
| 1815 | + memcpy(p, ns, len); | |
| 1816 | + p += len; | |
| 1817 | + *p++ = '\n'; | |
| 1818 | + } | |
| 1819 | + /* Replace trailing newline with null terminator. */ | |
| 1820 | + *(p - 1) = '\0'; | |
| 1821 | + | |
| 1822 | + mod->imported_namespaces = buf; | |
| 1823 | + return 0; | |
| 1824 | +} | |
| 1825 | + | |
| 1763 | 1826 | static int setup_modinfo(struct module *mod, struct load_info *info) |
| 1764 | 1827 | { |
| 1765 | 1828 | const struct module_attribute *attr; |
| 1766 | 1829 | char *imported_namespace; |
| 1767 | − int i; | |
| 1830 | + int i, err; | |
| 1768 | 1831 | |
| 1769 | 1832 | for (i = 0; (attr = modinfo_attrs[i]); i++) { |
| 1770 | 1833 | if (attr->setup) |
| @@ -1783,6 +1846,10 @@ static int setup_modinfo(struct module *mod, struct load_info *info) · 12 unmodified lines | ||
| 1783 | 1846 | } |
| 1784 | 1847 | } |
| 1785 | 1848 | |
| 1849 | + err = copy_modinfo_import_ns(mod, info); | |
| 1850 | + if (err) | |
| 1851 | + return err; | |
| 1852 | + | |
| 1786 | 1853 | return 0; |
| 1787 | 1854 | } |
| 1788 | 1855 | |