Message ID | 20221128201332.3482092-1-allenwebb@google.com |
---|---|
State | Superseded |
Headers | show |
Series | modules: add modalias file to sysfs for modules. | expand |
Le 28/11/2022 à 21:13, Allen Webb a écrit : > USB devices support the authorized attribute which can be used by > user-space to implement trust-based systems for enabling USB devices. It > would be helpful when building these systems to be able to know in > advance which kernel drivers (or modules) are reachable from a > particular USB device. > > This information is readily available for external modules in > modules.alias. However, builtin kernel modules are not covered. This > patch adds a sys-fs attribute to both builtin and loaded modules > exposing the matching rules in the modalias format for integration > with tools like USBGuard. > > Signed-off-by: Allen Webb <allenwebb@google.com> > --- You already sent a patch with the same title on Nov 11th. Why are you resending ? The line count is different from previous patch. Is that just a rebase ? The commit message is different, so is it a new version ? If so it should be marked [PATCH v2] in the title. Also, there should be some explanation of the resend here below the ---, so that everyone knows the reason you are resending or sending a new version. > drivers/base/Makefile | 2 +- > drivers/base/base.h | 8 ++ > drivers/base/bus.c | 42 ++++++ > drivers/base/mod_devicetable.c | 247 +++++++++++++++++++++++++++++++++ > drivers/usb/core/driver.c | 2 + > include/linux/device/bus.h | 8 ++ > include/linux/module.h | 1 + > kernel/module/internal.h | 2 + > kernel/module/sysfs.c | 100 +++++++++++++ > kernel/params.c | 2 + > 10 files changed, 413 insertions(+), 1 deletion(-) > create mode 100644 drivers/base/mod_devicetable.c > > diff --git a/drivers/base/Makefile b/drivers/base/Makefile > index 83217d243c25b..924d46ae987f4 100644 > --- a/drivers/base/Makefile > +++ b/drivers/base/Makefile > @@ -15,7 +15,7 @@ obj-y += firmware_loader/ > obj-$(CONFIG_NUMA) += node.o > obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o > ifeq ($(CONFIG_SYSFS),y) > -obj-$(CONFIG_MODULES) += module.o > +obj-$(CONFIG_MODULES) += mod_devicetable.o module.o > endif > obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o > obj-$(CONFIG_REGMAP) += regmap/ > diff --git a/drivers/base/base.h b/drivers/base/base.h > index b902d1ecc247f..beaa252c04388 100644 > --- a/drivers/base/base.h > +++ b/drivers/base/base.h > @@ -173,6 +173,14 @@ static inline void module_add_driver(struct module *mod, > static inline void module_remove_driver(struct device_driver *drv) { } > #endif > > +#if defined(CONFIG_SYSFS) > +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, > + size_t count); > +#else > +static inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, > + size_t count) { return -EINVAL; } > +#endif > + > #ifdef CONFIG_DEVTMPFS > extern int devtmpfs_init(void); > #else > diff --git a/drivers/base/bus.c b/drivers/base/bus.c > index 7ca47e5b3c1f4..4e0c5925545e5 100644 > --- a/drivers/base/bus.c > +++ b/drivers/base/bus.c > @@ -178,6 +178,48 @@ static const struct kset_uevent_ops bus_uevent_ops = { > > static struct kset *bus_kset; > > +/** > + * bus_for_each - bus iterator. > + * @start: bus to start iterating from. > + * @data: data for the callback. > + * @fn: function to be called for each device. > + * > + * Iterate over list of buses, and call @fn for each, > + * passing it @data. If @start is not NULL, we use that bus to > + * begin iterating from. > + * > + * We check the return of @fn each time. If it returns anything > + * other than 0, we break out and return that value. > + * > + * NOTE: The bus that returns a non-zero value is not retained > + * in any way, nor is its refcount incremented. If the caller needs > + * to retain this data, it should do so, and increment the reference > + * count in the supplied callback. > + */ > +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *)) > +{ > + int error = 0; > + struct bus_type *bus; > + struct subsys_private *bus_prv; > + struct kset *subsys; > + struct kobject *k; > + > + spin_lock(&bus_kset->list_lock); > + > + list_for_each_entry(k, &bus_kset->list, entry) { > + subsys = container_of(k, struct kset, kobj); > + bus_prv = container_of(subsys, struct subsys_private, subsys); > + bus = bus_prv->bus; > + error = fn(bus, data); > + if (error) > + break; > + } > + > + spin_unlock(&bus_kset->list_lock); > + return error; > +} > +EXPORT_SYMBOL_GPL(bus_for_each); > + > /* Manually detach a device from its associated driver. */ > static ssize_t unbind_store(struct device_driver *drv, const char *buf, > size_t count) > diff --git a/drivers/base/mod_devicetable.c b/drivers/base/mod_devicetable.c > new file mode 100644 > index 0000000000000..b04442207668c > --- /dev/null > +++ b/drivers/base/mod_devicetable.c > @@ -0,0 +1,247 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * mod_devicetable.c - helpers for displaying modaliases through sysfs. > + * > + * This borrows a lot from file2alias.c > + */ > + > +#include <linux/device/bus.h> > +#include <linux/device.h> > +#include <linux/usb.h> > + > +#include "base.h" > +#include "../usb/core/usb.h" > + > +#define ADD(buf, count, len, sep, cond, field) \ > +do { \ > + if (cond) \ > + (len) += scnprintf(&(buf)[len], \ > + (count) - (len), \ > + sizeof(field) == 1 ? (sep "%02X") : \ > + sizeof(field) == 2 ? (sep "%04X") : \ > + sizeof(field) == 4 ? (sep "%08X") : "", \ > + (field)); \ > + else \ > + (len) += scnprintf(&(buf)[len], (count) - (len), (sep "*")); \ > +} while (0) > + > +#ifdef CONFIG_USB > +/* USB related modaliases can be split because of device number matching, so > + * this function handles individual modaliases for one segment of the range. > + * > + * > + */ > +static ssize_t usb_id_to_modalias(const struct usb_device_id *id, > + unsigned int bcdDevice_initial, > + int bcdDevice_initial_digits, > + unsigned char range_lo, > + unsigned char range_hi, > + unsigned char max, const char *mod_name, > + char *buf, size_t count) > +{ > + ssize_t len = 0; > + > + ADD(buf, count, len, "alias usb:v", > + id->match_flags & USB_DEVICE_ID_MATCH_VENDOR, id->idVendor); > + ADD(buf, count, len, "p", id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT, > + id->idProduct); > + > + len += scnprintf(&buf[len], count - len, "d"); > + if (bcdDevice_initial_digits) > + len += scnprintf(&buf[len], count - len, "%0*X", > + bcdDevice_initial_digits, bcdDevice_initial); > + if (range_lo == range_hi) { > + len += scnprintf(&buf[len], count - len, "%X", range_lo); > + } else if (range_lo > 0 || range_hi < max) { > + if (range_lo > 0x9 || range_hi < 0xA) { > + len += scnprintf(&buf[len], count - len, "[%X-%X]", > + range_lo, range_hi); > + } else { > + len += scnprintf(&buf[len], count - len, > + range_lo < 0x9 ? "[%X-9" : "[%X", > + range_lo); > + len += scnprintf(&buf[len], count - len, > + range_hi > 0xA ? "A-%X]" : "%X]", > + range_hi); > + } > + } > + if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1)) > + len += scnprintf(&buf[len], count - len, "*"); > + > + ADD(buf, count, len, "dc", > + id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS, id->bDeviceClass); > + ADD(buf, count, len, "dsc", > + id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS, > + id->bDeviceSubClass); > + ADD(buf, count, len, "dp", > + id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL, > + id->bDeviceProtocol); > + ADD(buf, count, len, "ic", > + id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS, > + id->bInterfaceClass); > + ADD(buf, count, len, "isc", > + id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS, > + id->bInterfaceSubClass); > + ADD(buf, count, len, "ip", > + id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL, > + id->bInterfaceProtocol); > + ADD(buf, count, len, "in", > + id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER, > + id->bInterfaceNumber); > + > + len += scnprintf(&buf[len], count - len, " %s\n", mod_name); > + return len; > +} > + > +/* Handles increment/decrement of BCD formatted integers */ > +/* Returns the previous value, so it works like i++ or i-- */ > +static unsigned int incbcd(unsigned int *bcd, > + int inc, > + unsigned char max, > + size_t chars) > +{ > + unsigned int init = *bcd, i, j; > + unsigned long long c, dec = 0; > + > + /* If bcd is not in BCD format, just increment */ > + if (max > 0x9) { > + *bcd += inc; > + return init; > + } > + > + /* Convert BCD to Decimal */ > + for (i = 0 ; i < chars ; i++) { > + c = (*bcd >> (i << 2)) & 0xf; > + c = c > 9 ? 9 : c; /* force to bcd just in case */ > + for (j = 0 ; j < i ; j++) > + c = c * 10; > + dec += c; > + } > + > + /* Do our increment/decrement */ > + dec += inc; > + *bcd = 0; > + > + /* Convert back to BCD */ > + for (i = 0 ; i < chars ; i++) { > + for (c = 1, j = 0 ; j < i ; j++) > + c = c * 10; > + c = (dec / c) % 10; > + *bcd += c << (i << 2); > + } > + return init; > +} > + > +/* Print the modaliases for the specified struct usb_device_id. > + */ > +static ssize_t usb_id_to_modalias_multi(const struct usb_device_id *id, > + const char *mod_name, char *buf, > + size_t count) > +{ > + ssize_t len = 0; > + unsigned int devlo, devhi; > + unsigned char chi, clo, max; > + int ndigits; > + > + devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ? > + id->bcdDevice_lo : 0x0U; > + devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ? > + id->bcdDevice_hi : ~0x0U; > + > + /* Figure out if this entry is in bcd or hex format */ > + max = 0x9; /* Default to decimal format */ > + for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) { > + clo = (devlo >> (ndigits << 2)) & 0xf; > + chi = ((devhi > 0x9999 ? 0x9999 : devhi) >> > + (ndigits << 2)) & 0xf; > + if (clo > max || chi > max) { > + max = 0xf; > + break; > + } > + } > + > + /* > + * Some modules (visor) have empty slots as placeholder for > + * run-time specification that results in catch-all alias > + */ > + if (!(id->idVendor || id->idProduct || id->bDeviceClass || > + id->bInterfaceClass)) > + return len; > + > + /* Convert numeric bcdDevice range into fnmatch-able pattern(s) */ > + for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; > + ndigits--) { > + clo = devlo & 0xf; > + chi = devhi & 0xf; > + /* If we are in bcd mode, truncate if necessary */ > + if (chi > max) > + chi = max; > + devlo >>= 4; > + devhi >>= 4; > + > + if (devlo == devhi || !ndigits) { > + len += usb_id_to_modalias(id, devlo, ndigits, clo, chi, > + max, mod_name, buf + len, > + count - len); > + break; > + } > + > + if (clo > 0x0) > + len += usb_id_to_modalias(id, > + incbcd(&devlo, 1, max, > + sizeof(id->bcdDevice_lo) * 2), > + ndigits, clo, max, max, mod_name, buf + len, > + count - len); > + > + if (chi < max) > + len += usb_id_to_modalias(id, > + incbcd(&devhi, -1, max, > + sizeof(id->bcdDevice_lo) * 2), > + ndigits, 0x0, chi, max, mod_name, buf + len, > + count - len); > + } > + return len; > +} > + > +/* Print the modaliases for the given driver assumed to be an usb_driver or > + * usb_device_driver. > + * > + * "alias" is prepended and the module name is appended to each modalias to > + * match the format in modules.aliases. > + * > + * The modaliases will be written out to @buf with @count being the maximum > + * bytes to write. The return value is a negative errno on error or the number > + * of bytes written to @buf on success. > + */ > +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, > + size_t count) > +{ > + ssize_t len = 0; > + const struct usb_device_id *id; > + const char *mod_name; > + > + if (drv->bus != &usb_bus_type) > + return -EINVAL; > + > + if (drv->owner) > + mod_name = drv->owner->name; > + else > + mod_name = drv->mod_name; > + > + if (is_usb_device_driver(drv)) > + id = to_usb_device_driver(drv)->id_table; > + else > + id = to_usb_driver(drv)->id_table; > + if (!id) > + return len; > + > + for (; id->match_flags; id++) { > + len += usb_id_to_modalias_multi(id, mod_name, buf + len, > + count - len); > + } > + return len; > +} > +#else > +inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, > + size_t count){ return 0; } > +#endif > diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c > index 7e7e119c253fb..fdbc197b64c9c 100644 > --- a/drivers/usb/core/driver.c > +++ b/drivers/usb/core/driver.c > @@ -32,6 +32,7 @@ > #include <linux/usb/quirks.h> > #include <linux/usb/hcd.h> > > +#include "../../base/base.h" > #include "usb.h" > > > @@ -2030,4 +2031,5 @@ struct bus_type usb_bus_type = { > .match = usb_device_match, > .uevent = usb_uevent, > .need_parent_lock = true, > + .drv_to_modalias = usb_drv_to_modalias, > }; > diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h > index d8b29ccd07e56..cce0bedec63d9 100644 > --- a/include/linux/device/bus.h > +++ b/include/linux/device/bus.h > @@ -61,6 +61,10 @@ struct fwnode_handle; > * this bus. > * @dma_cleanup: Called to cleanup DMA configuration on a device on > * this bus. > + * @drv_to_modalias: Called to convert the matching IDs in a > + * struct device_driver to their corresponding modaliases. > + * Note that the struct device_driver is expected to belong > + * to this bus. > * @pm: Power management operations of this bus, callback the specific > * device driver's pm-ops. > * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU > @@ -107,6 +111,9 @@ struct bus_type { > int (*dma_configure)(struct device *dev); > void (*dma_cleanup)(struct device *dev); > > + ssize_t (*drv_to_modalias)(struct device_driver *drv, char *buf, > + size_t count); > + > const struct dev_pm_ops *pm; > > const struct iommu_ops *iommu_ops; > @@ -161,6 +168,7 @@ void subsys_dev_iter_init(struct subsys_dev_iter *iter, > struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter); > void subsys_dev_iter_exit(struct subsys_dev_iter *iter); > > +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *)); > int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, > int (*fn)(struct device *dev, void *data)); > struct device *bus_find_device(struct bus_type *bus, struct device *start, > diff --git a/include/linux/module.h b/include/linux/module.h > index ec61fb53979a9..0bfa859a21566 100644 > --- a/include/linux/module.h > +++ b/include/linux/module.h > @@ -47,6 +47,7 @@ struct module_kobject { > struct kobject *drivers_dir; > struct module_param_attrs *mp; > struct completion *kobj_completion; > + struct bin_attribute modalias_attr; > } __randomize_layout; > > struct module_attribute { > diff --git a/kernel/module/internal.h b/kernel/module/internal.h > index 2e2bf236f5582..8d7ae37584868 100644 > --- a/kernel/module/internal.h > +++ b/kernel/module/internal.h > @@ -259,11 +259,13 @@ static inline void add_kallsyms(struct module *mod, const struct load_info *info > #endif /* CONFIG_KALLSYMS */ > > #ifdef CONFIG_SYSFS > +void add_modalias_attr(struct module_kobject *mk); > int mod_sysfs_setup(struct module *mod, const struct load_info *info, > struct kernel_param *kparam, unsigned int num_params); > void mod_sysfs_teardown(struct module *mod); > void init_param_lock(struct module *mod); > #else /* !CONFIG_SYSFS */ > +static inline void add_modalias_attr(struct module_kobject *mk) {} > static inline int mod_sysfs_setup(struct module *mod, > const struct load_info *info, > struct kernel_param *kparam, > diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c > index ce68f821dcd12..e80bfa4639765 100644 > --- a/kernel/module/sysfs.c > +++ b/kernel/module/sysfs.c > @@ -5,6 +5,8 @@ > * Copyright (C) 2008 Rusty Russell > */ > > +#include <linux/device/bus.h> > +#include <linux/device/driver.h> > #include <linux/module.h> > #include <linux/kernel.h> > #include <linux/fs.h> > @@ -240,6 +242,102 @@ static inline void add_notes_attrs(struct module *mod, const struct load_info *i > static inline void remove_notes_attrs(struct module *mod) { } > #endif /* CONFIG_KALLSYMS */ > > +/* Track of the buffer and module identity in callbacks when walking the list of > + * drivers for each bus. > + */ > +struct modalias_bus_print_state { > + struct module_kobject *mk; > + char *buf; > + size_t count; > + ssize_t len; > +}; > + > +static int print_modalias_for_drv(struct device_driver *drv, void *p) > +{ > + struct modalias_bus_print_state *s = p; > + struct module_kobject *mk = s->mk; > + ssize_t len; > + /* Skip drivers that do not match this module. */ > + if (mk->mod) { > + if (mk->mod != drv->owner) > + return 0; > + } else if (!mk->kobj.name || !drv->mod_name || > + strcmp(mk->kobj.name, drv->mod_name)) > + return 0; > + > + if (drv->bus && drv->bus->drv_to_modalias) { > + len = drv->bus->drv_to_modalias(drv, s->buf + s->len, > + s->count - s->len); > + if (len < 0) > + return len; > + s->len += len; > + } > + > + s->len += scnprintf(&s->buf[s->len], s->count - s->len, "driver %s\n", > + drv->name); > + return 0; > +} > + > +static int print_modalias_for_bus(struct bus_type *type, void *p) > +{ > + return bus_for_each_drv(type, NULL, p, print_modalias_for_drv); > +} > + > +static ssize_t module_modalias_read(struct file *filp, struct kobject *kobj, > + struct bin_attribute *bin_attr, > + char *buf, loff_t pos, size_t count) > +{ > + struct module_kobject *mk = container_of(kobj, struct module_kobject, > + kobj); > + struct modalias_bus_print_state state = {mk, buf, count, 0}; > + int error = 0; > + > + if (pos != 0) > + return -EINVAL; > + > + error = bus_for_each(&state, print_modalias_for_bus); > + if (error) > + return error; > + > + if (mk->mod) > + state.len += scnprintf(&buf[state.len], count - state.len, > + "modalias %s %s\n", kobject_name(kobj), > + mk->mod->name); > + else > + state.len += scnprintf(&buf[state.len], count - state.len, > + "modalias %s NULL\n", > + kobject_name(kobj)); > + > + /* > + * The caller checked the pos and count against our size. > + */ > + return state.len; > +} > + > +/* Used in kernel/params.c for builtin modules. > + * > + * `struct module_kobject` is used instead of `struct module` because for > + * builtin modules, the `struct module` is not available when this is called. > + */ > +void add_modalias_attr(struct module_kobject *mk) > +{ > + sysfs_bin_attr_init(&mk->modalias_attr); > + mk->modalias_attr.attr.name = "modalias"; > + mk->modalias_attr.attr.mode = 0444; > + mk->modalias_attr.read = module_modalias_read; > + if (sysfs_create_bin_file(&mk->kobj, &mk->modalias_attr)) { > + /* We shouldn't ignore the return type, but there is nothing to > + * do. > + */ > + return; > + } > +} > + > +static void remove_modalias_attr(struct module_kobject *mk) > +{ > + sysfs_remove_bin_file(&mk->kobj, &mk->modalias_attr); > +} > + > static void del_usage_links(struct module *mod) > { > #ifdef CONFIG_MODULE_UNLOAD > @@ -398,6 +496,7 @@ int mod_sysfs_setup(struct module *mod, > > add_sect_attrs(mod, info); > add_notes_attrs(mod, info); > + add_modalias_attr(&mod->mkobj); > > return 0; > > @@ -415,6 +514,7 @@ int mod_sysfs_setup(struct module *mod, > > static void mod_sysfs_fini(struct module *mod) > { > + remove_modalias_attr(&mod->mkobj); > remove_notes_attrs(mod); > remove_sect_attrs(mod); > mod_kobject_put(mod); > diff --git a/kernel/params.c b/kernel/params.c > index 5b92310425c50..111024196361a 100644 > --- a/kernel/params.c > +++ b/kernel/params.c > @@ -13,6 +13,7 @@ > #include <linux/slab.h> > #include <linux/ctype.h> > #include <linux/security.h> > +#include "module/internal.h" > > #ifdef CONFIG_SYSFS > /* Protects all built-in parameters, modules use their own param_lock */ > @@ -815,6 +816,7 @@ static void __init kernel_add_sysfs_param(const char *name, > BUG_ON(err); > kobject_uevent(&mk->kobj, KOBJ_ADD); > kobject_put(&mk->kobj); > + add_modalias_attr(mk); > } > > /*
Hi Allen, Thank you for the patch! Yet something to improve: [auto build test ERROR on driver-core/driver-core-testing] [also build test ERROR on driver-core/driver-core-next driver-core/driver-core-linus mcgrof/modules-next usb/usb-testing usb/usb-next usb/usb-linus linus/master v6.1-rc7 next-20221129] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Allen-Webb/modules-add-modalias-file-to-sysfs-for-modules/20221129-041511 patch link: https://lore.kernel.org/r/20221128201332.3482092-1-allenwebb%40google.com patch subject: [PATCH] modules: add modalias file to sysfs for modules. config: arm-buildonly-randconfig-r003-20221128 compiler: arm-linux-gnueabi-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/5f45459f50fc0240781c397065e1b7bafcc79e08 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Allen-Webb/modules-add-modalias-file-to-sysfs-for-modules/20221129-041511 git checkout 5f45459f50fc0240781c397065e1b7bafcc79e08 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All errors (new ones prefixed by >>): In file included from include/linux/container_of.h:5, from include/linux/kernel.h:21, from kernel/params.c:6: kernel/module/internal.h: In function 'mod_find': >> include/linux/container_of.h:20:54: error: invalid use of undefined type 'struct module' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu' 391 | pos = list_entry_rcu((head)->next, typeof(*pos), member); \ | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:298:27: error: expression in static assertion is not an integer 298 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu' 391 | pos = list_entry_rcu((head)->next, typeof(*pos), member); \ | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ In file included from include/uapi/linux/posix_types.h:5, from include/uapi/linux/types.h:14, from include/linux/types.h:6, from include/linux/limits.h:6, from include/linux/kernel.h:16: >> include/linux/stddef.h:16:33: error: invalid use of undefined type 'struct module' 16 | #define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) | ^~~~~~~~~~~~~~~~~~ include/linux/container_of.h:23:28: note: in expansion of macro 'offsetof' 23 | ((type *)(__mptr - offsetof(type, member))); }) | ^~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu' 391 | pos = list_entry_rcu((head)->next, typeof(*pos), member); \ | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ In file included from include/linux/pid.h:5, from include/linux/sched.h:14, from include/linux/sched/mm.h:7, from include/linux/xarray.h:19, from include/linux/radix-tree.h:21, from include/linux/idr.h:15, from include/linux/kernfs.h:12, from include/linux/sysfs.h:16, from include/linux/kobject.h:20, from include/linux/module.h:21, from kernel/params.c:9: >> include/linux/rculist.h:392:21: error: invalid use of undefined type 'struct module' 392 | &pos->member != (head); \ | ^~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/container_of.h:19:33: note: in definition of macro 'container_of' 19 | void *__mptr = (void *)(ptr); \ | ^~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/container_of.h:19:33: note: in definition of macro 'container_of' 19 | void *__mptr = (void *)(ptr); \ | ^~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/container_of.h:19:33: note: in definition of macro 'container_of' 19 | void *__mptr = (void *)(ptr); \ | ^~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/container_of.h:19:33: note: in definition of macro 'container_of' 19 | void *__mptr = (void *)(ptr); \ -- | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/asm-generic/rwonce.h:44:43: note: in expansion of macro '__unqual_scalar_typeof' 44 | #define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x)) | ^~~~~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:50:9: note: in expansion of macro '__READ_ONCE' 50 | __READ_ONCE(x); \ | ^~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/asm-generic/rwonce.h:50:9: note: in expansion of macro '__READ_ONCE' 50 | __READ_ONCE(x); \ | ^~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ >> include/linux/container_of.h:20:54: error: invalid use of undefined type 'struct module' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:20:23: note: in expansion of macro '__same_type' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:21:23: note: in expansion of macro '__same_type' 21 | __same_type(*(ptr), void), \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~ include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert' 78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg) | ^~~~ include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert' 20 | static_assert(__same_type(*(ptr), ((type *)0)->member) || \ | ^~~~~~~~~~~~~ include/linux/container_of.h:21:23: note: in expansion of macro '__same_type' 21 | __same_type(*(ptr), void), \ | ^~~~~~~~~~~ include/linux/rculist.h:307:9: note: in expansion of macro 'container_of' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~~~~ include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert' 345 | __compiletime_assert(condition, msg, prefix, suffix) | ^~~~~~~~~~~~~~~~~~~~ include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert' 357 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__) | ^~~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~~~~~~ include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word' 36 | compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \ | ^~~~~~~~~~~~~ include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type' 49 | compiletime_assert_rwonce_type(x); \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE' 307 | container_of(READ_ONCE(ptr), type, member) | ^~~~~~~~~ include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) | ^~~~~~~~~~~~~~ kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu' 212 | list_for_each_entry_rcu(mod, &modules, list, | ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module' 393 | pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) .. vim +20 include/linux/container_of.h d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 9 d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 10 /** d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 11 * container_of - cast a member of a structure out to the containing structure d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 12 * @ptr: the pointer to the member. d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 13 * @type: the type of the container struct this is embedded in. d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 14 * @member: the name of the member within the struct. d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 15 * 7376e561fd2e01 Sakari Ailus 2022-10-24 16 * WARNING: any const qualifier of @ptr is lost. d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 17 */ d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 18 #define container_of(ptr, type, member) ({ \ d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 19 void *__mptr = (void *)(ptr); \ e1edc277e6f6df Rasmus Villemoes 2021-11-08 @20 static_assert(__same_type(*(ptr), ((type *)0)->member) || \ e1edc277e6f6df Rasmus Villemoes 2021-11-08 21 __same_type(*(ptr), void), \ d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 22 "pointer type mismatch in container_of()"); \ d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 23 ((type *)(__mptr - offsetof(type, member))); }) d2a8ebbf8192b8 Andy Shevchenko 2021-11-08 24
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 83217d243c25b..924d46ae987f4 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -15,7 +15,7 @@ obj-y += firmware_loader/ obj-$(CONFIG_NUMA) += node.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o ifeq ($(CONFIG_SYSFS),y) -obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_MODULES) += mod_devicetable.o module.o endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ diff --git a/drivers/base/base.h b/drivers/base/base.h index b902d1ecc247f..beaa252c04388 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -173,6 +173,14 @@ static inline void module_add_driver(struct module *mod, static inline void module_remove_driver(struct device_driver *drv) { } #endif +#if defined(CONFIG_SYSFS) +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, + size_t count); +#else +static inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, + size_t count) { return -EINVAL; } +#endif + #ifdef CONFIG_DEVTMPFS extern int devtmpfs_init(void); #else diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7ca47e5b3c1f4..4e0c5925545e5 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -178,6 +178,48 @@ static const struct kset_uevent_ops bus_uevent_ops = { static struct kset *bus_kset; +/** + * bus_for_each - bus iterator. + * @start: bus to start iterating from. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over list of buses, and call @fn for each, + * passing it @data. If @start is not NULL, we use that bus to + * begin iterating from. + * + * We check the return of @fn each time. If it returns anything + * other than 0, we break out and return that value. + * + * NOTE: The bus that returns a non-zero value is not retained + * in any way, nor is its refcount incremented. If the caller needs + * to retain this data, it should do so, and increment the reference + * count in the supplied callback. + */ +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *)) +{ + int error = 0; + struct bus_type *bus; + struct subsys_private *bus_prv; + struct kset *subsys; + struct kobject *k; + + spin_lock(&bus_kset->list_lock); + + list_for_each_entry(k, &bus_kset->list, entry) { + subsys = container_of(k, struct kset, kobj); + bus_prv = container_of(subsys, struct subsys_private, subsys); + bus = bus_prv->bus; + error = fn(bus, data); + if (error) + break; + } + + spin_unlock(&bus_kset->list_lock); + return error; +} +EXPORT_SYMBOL_GPL(bus_for_each); + /* Manually detach a device from its associated driver. */ static ssize_t unbind_store(struct device_driver *drv, const char *buf, size_t count) diff --git a/drivers/base/mod_devicetable.c b/drivers/base/mod_devicetable.c new file mode 100644 index 0000000000000..b04442207668c --- /dev/null +++ b/drivers/base/mod_devicetable.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mod_devicetable.c - helpers for displaying modaliases through sysfs. + * + * This borrows a lot from file2alias.c + */ + +#include <linux/device/bus.h> +#include <linux/device.h> +#include <linux/usb.h> + +#include "base.h" +#include "../usb/core/usb.h" + +#define ADD(buf, count, len, sep, cond, field) \ +do { \ + if (cond) \ + (len) += scnprintf(&(buf)[len], \ + (count) - (len), \ + sizeof(field) == 1 ? (sep "%02X") : \ + sizeof(field) == 2 ? (sep "%04X") : \ + sizeof(field) == 4 ? (sep "%08X") : "", \ + (field)); \ + else \ + (len) += scnprintf(&(buf)[len], (count) - (len), (sep "*")); \ +} while (0) + +#ifdef CONFIG_USB +/* USB related modaliases can be split because of device number matching, so + * this function handles individual modaliases for one segment of the range. + * + * + */ +static ssize_t usb_id_to_modalias(const struct usb_device_id *id, + unsigned int bcdDevice_initial, + int bcdDevice_initial_digits, + unsigned char range_lo, + unsigned char range_hi, + unsigned char max, const char *mod_name, + char *buf, size_t count) +{ + ssize_t len = 0; + + ADD(buf, count, len, "alias usb:v", + id->match_flags & USB_DEVICE_ID_MATCH_VENDOR, id->idVendor); + ADD(buf, count, len, "p", id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT, + id->idProduct); + + len += scnprintf(&buf[len], count - len, "d"); + if (bcdDevice_initial_digits) + len += scnprintf(&buf[len], count - len, "%0*X", + bcdDevice_initial_digits, bcdDevice_initial); + if (range_lo == range_hi) { + len += scnprintf(&buf[len], count - len, "%X", range_lo); + } else if (range_lo > 0 || range_hi < max) { + if (range_lo > 0x9 || range_hi < 0xA) { + len += scnprintf(&buf[len], count - len, "[%X-%X]", + range_lo, range_hi); + } else { + len += scnprintf(&buf[len], count - len, + range_lo < 0x9 ? "[%X-9" : "[%X", + range_lo); + len += scnprintf(&buf[len], count - len, + range_hi > 0xA ? "A-%X]" : "%X]", + range_hi); + } + } + if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1)) + len += scnprintf(&buf[len], count - len, "*"); + + ADD(buf, count, len, "dc", + id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS, id->bDeviceClass); + ADD(buf, count, len, "dsc", + id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS, + id->bDeviceSubClass); + ADD(buf, count, len, "dp", + id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL, + id->bDeviceProtocol); + ADD(buf, count, len, "ic", + id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS, + id->bInterfaceClass); + ADD(buf, count, len, "isc", + id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS, + id->bInterfaceSubClass); + ADD(buf, count, len, "ip", + id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL, + id->bInterfaceProtocol); + ADD(buf, count, len, "in", + id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER, + id->bInterfaceNumber); + + len += scnprintf(&buf[len], count - len, " %s\n", mod_name); + return len; +} + +/* Handles increment/decrement of BCD formatted integers */ +/* Returns the previous value, so it works like i++ or i-- */ +static unsigned int incbcd(unsigned int *bcd, + int inc, + unsigned char max, + size_t chars) +{ + unsigned int init = *bcd, i, j; + unsigned long long c, dec = 0; + + /* If bcd is not in BCD format, just increment */ + if (max > 0x9) { + *bcd += inc; + return init; + } + + /* Convert BCD to Decimal */ + for (i = 0 ; i < chars ; i++) { + c = (*bcd >> (i << 2)) & 0xf; + c = c > 9 ? 9 : c; /* force to bcd just in case */ + for (j = 0 ; j < i ; j++) + c = c * 10; + dec += c; + } + + /* Do our increment/decrement */ + dec += inc; + *bcd = 0; + + /* Convert back to BCD */ + for (i = 0 ; i < chars ; i++) { + for (c = 1, j = 0 ; j < i ; j++) + c = c * 10; + c = (dec / c) % 10; + *bcd += c << (i << 2); + } + return init; +} + +/* Print the modaliases for the specified struct usb_device_id. + */ +static ssize_t usb_id_to_modalias_multi(const struct usb_device_id *id, + const char *mod_name, char *buf, + size_t count) +{ + ssize_t len = 0; + unsigned int devlo, devhi; + unsigned char chi, clo, max; + int ndigits; + + devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ? + id->bcdDevice_lo : 0x0U; + devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ? + id->bcdDevice_hi : ~0x0U; + + /* Figure out if this entry is in bcd or hex format */ + max = 0x9; /* Default to decimal format */ + for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) { + clo = (devlo >> (ndigits << 2)) & 0xf; + chi = ((devhi > 0x9999 ? 0x9999 : devhi) >> + (ndigits << 2)) & 0xf; + if (clo > max || chi > max) { + max = 0xf; + break; + } + } + + /* + * Some modules (visor) have empty slots as placeholder for + * run-time specification that results in catch-all alias + */ + if (!(id->idVendor || id->idProduct || id->bDeviceClass || + id->bInterfaceClass)) + return len; + + /* Convert numeric bcdDevice range into fnmatch-able pattern(s) */ + for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; + ndigits--) { + clo = devlo & 0xf; + chi = devhi & 0xf; + /* If we are in bcd mode, truncate if necessary */ + if (chi > max) + chi = max; + devlo >>= 4; + devhi >>= 4; + + if (devlo == devhi || !ndigits) { + len += usb_id_to_modalias(id, devlo, ndigits, clo, chi, + max, mod_name, buf + len, + count - len); + break; + } + + if (clo > 0x0) + len += usb_id_to_modalias(id, + incbcd(&devlo, 1, max, + sizeof(id->bcdDevice_lo) * 2), + ndigits, clo, max, max, mod_name, buf + len, + count - len); + + if (chi < max) + len += usb_id_to_modalias(id, + incbcd(&devhi, -1, max, + sizeof(id->bcdDevice_lo) * 2), + ndigits, 0x0, chi, max, mod_name, buf + len, + count - len); + } + return len; +} + +/* Print the modaliases for the given driver assumed to be an usb_driver or + * usb_device_driver. + * + * "alias" is prepended and the module name is appended to each modalias to + * match the format in modules.aliases. + * + * The modaliases will be written out to @buf with @count being the maximum + * bytes to write. The return value is a negative errno on error or the number + * of bytes written to @buf on success. + */ +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, + size_t count) +{ + ssize_t len = 0; + const struct usb_device_id *id; + const char *mod_name; + + if (drv->bus != &usb_bus_type) + return -EINVAL; + + if (drv->owner) + mod_name = drv->owner->name; + else + mod_name = drv->mod_name; + + if (is_usb_device_driver(drv)) + id = to_usb_device_driver(drv)->id_table; + else + id = to_usb_driver(drv)->id_table; + if (!id) + return len; + + for (; id->match_flags; id++) { + len += usb_id_to_modalias_multi(id, mod_name, buf + len, + count - len); + } + return len; +} +#else +inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf, + size_t count){ return 0; } +#endif diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 7e7e119c253fb..fdbc197b64c9c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -32,6 +32,7 @@ #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> +#include "../../base/base.h" #include "usb.h" @@ -2030,4 +2031,5 @@ struct bus_type usb_bus_type = { .match = usb_device_match, .uevent = usb_uevent, .need_parent_lock = true, + .drv_to_modalias = usb_drv_to_modalias, }; diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index d8b29ccd07e56..cce0bedec63d9 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -61,6 +61,10 @@ struct fwnode_handle; * this bus. * @dma_cleanup: Called to cleanup DMA configuration on a device on * this bus. + * @drv_to_modalias: Called to convert the matching IDs in a + * struct device_driver to their corresponding modaliases. + * Note that the struct device_driver is expected to belong + * to this bus. * @pm: Power management operations of this bus, callback the specific * device driver's pm-ops. * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU @@ -107,6 +111,9 @@ struct bus_type { int (*dma_configure)(struct device *dev); void (*dma_cleanup)(struct device *dev); + ssize_t (*drv_to_modalias)(struct device_driver *drv, char *buf, + size_t count); + const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; @@ -161,6 +168,7 @@ void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter); void subsys_dev_iter_exit(struct subsys_dev_iter *iter); +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *)); int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); struct device *bus_find_device(struct bus_type *bus, struct device *start, diff --git a/include/linux/module.h b/include/linux/module.h index ec61fb53979a9..0bfa859a21566 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -47,6 +47,7 @@ struct module_kobject { struct kobject *drivers_dir; struct module_param_attrs *mp; struct completion *kobj_completion; + struct bin_attribute modalias_attr; } __randomize_layout; struct module_attribute { diff --git a/kernel/module/internal.h b/kernel/module/internal.h index 2e2bf236f5582..8d7ae37584868 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -259,11 +259,13 @@ static inline void add_kallsyms(struct module *mod, const struct load_info *info #endif /* CONFIG_KALLSYMS */ #ifdef CONFIG_SYSFS +void add_modalias_attr(struct module_kobject *mk); int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params); void mod_sysfs_teardown(struct module *mod); void init_param_lock(struct module *mod); #else /* !CONFIG_SYSFS */ +static inline void add_modalias_attr(struct module_kobject *mk) {} static inline int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c index ce68f821dcd12..e80bfa4639765 100644 --- a/kernel/module/sysfs.c +++ b/kernel/module/sysfs.c @@ -5,6 +5,8 @@ * Copyright (C) 2008 Rusty Russell */ +#include <linux/device/bus.h> +#include <linux/device/driver.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> @@ -240,6 +242,102 @@ static inline void add_notes_attrs(struct module *mod, const struct load_info *i static inline void remove_notes_attrs(struct module *mod) { } #endif /* CONFIG_KALLSYMS */ +/* Track of the buffer and module identity in callbacks when walking the list of + * drivers for each bus. + */ +struct modalias_bus_print_state { + struct module_kobject *mk; + char *buf; + size_t count; + ssize_t len; +}; + +static int print_modalias_for_drv(struct device_driver *drv, void *p) +{ + struct modalias_bus_print_state *s = p; + struct module_kobject *mk = s->mk; + ssize_t len; + /* Skip drivers that do not match this module. */ + if (mk->mod) { + if (mk->mod != drv->owner) + return 0; + } else if (!mk->kobj.name || !drv->mod_name || + strcmp(mk->kobj.name, drv->mod_name)) + return 0; + + if (drv->bus && drv->bus->drv_to_modalias) { + len = drv->bus->drv_to_modalias(drv, s->buf + s->len, + s->count - s->len); + if (len < 0) + return len; + s->len += len; + } + + s->len += scnprintf(&s->buf[s->len], s->count - s->len, "driver %s\n", + drv->name); + return 0; +} + +static int print_modalias_for_bus(struct bus_type *type, void *p) +{ + return bus_for_each_drv(type, NULL, p, print_modalias_for_drv); +} + +static ssize_t module_modalias_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + struct module_kobject *mk = container_of(kobj, struct module_kobject, + kobj); + struct modalias_bus_print_state state = {mk, buf, count, 0}; + int error = 0; + + if (pos != 0) + return -EINVAL; + + error = bus_for_each(&state, print_modalias_for_bus); + if (error) + return error; + + if (mk->mod) + state.len += scnprintf(&buf[state.len], count - state.len, + "modalias %s %s\n", kobject_name(kobj), + mk->mod->name); + else + state.len += scnprintf(&buf[state.len], count - state.len, + "modalias %s NULL\n", + kobject_name(kobj)); + + /* + * The caller checked the pos and count against our size. + */ + return state.len; +} + +/* Used in kernel/params.c for builtin modules. + * + * `struct module_kobject` is used instead of `struct module` because for + * builtin modules, the `struct module` is not available when this is called. + */ +void add_modalias_attr(struct module_kobject *mk) +{ + sysfs_bin_attr_init(&mk->modalias_attr); + mk->modalias_attr.attr.name = "modalias"; + mk->modalias_attr.attr.mode = 0444; + mk->modalias_attr.read = module_modalias_read; + if (sysfs_create_bin_file(&mk->kobj, &mk->modalias_attr)) { + /* We shouldn't ignore the return type, but there is nothing to + * do. + */ + return; + } +} + +static void remove_modalias_attr(struct module_kobject *mk) +{ + sysfs_remove_bin_file(&mk->kobj, &mk->modalias_attr); +} + static void del_usage_links(struct module *mod) { #ifdef CONFIG_MODULE_UNLOAD @@ -398,6 +496,7 @@ int mod_sysfs_setup(struct module *mod, add_sect_attrs(mod, info); add_notes_attrs(mod, info); + add_modalias_attr(&mod->mkobj); return 0; @@ -415,6 +514,7 @@ int mod_sysfs_setup(struct module *mod, static void mod_sysfs_fini(struct module *mod) { + remove_modalias_attr(&mod->mkobj); remove_notes_attrs(mod); remove_sect_attrs(mod); mod_kobject_put(mod); diff --git a/kernel/params.c b/kernel/params.c index 5b92310425c50..111024196361a 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/ctype.h> #include <linux/security.h> +#include "module/internal.h" #ifdef CONFIG_SYSFS /* Protects all built-in parameters, modules use their own param_lock */ @@ -815,6 +816,7 @@ static void __init kernel_add_sysfs_param(const char *name, BUG_ON(err); kobject_uevent(&mk->kobj, KOBJ_ADD); kobject_put(&mk->kobj); + add_modalias_attr(mk); } /*
USB devices support the authorized attribute which can be used by user-space to implement trust-based systems for enabling USB devices. It would be helpful when building these systems to be able to know in advance which kernel drivers (or modules) are reachable from a particular USB device. This information is readily available for external modules in modules.alias. However, builtin kernel modules are not covered. This patch adds a sys-fs attribute to both builtin and loaded modules exposing the matching rules in the modalias format for integration with tools like USBGuard. Signed-off-by: Allen Webb <allenwebb@google.com> --- drivers/base/Makefile | 2 +- drivers/base/base.h | 8 ++ drivers/base/bus.c | 42 ++++++ drivers/base/mod_devicetable.c | 247 +++++++++++++++++++++++++++++++++ drivers/usb/core/driver.c | 2 + include/linux/device/bus.h | 8 ++ include/linux/module.h | 1 + kernel/module/internal.h | 2 + kernel/module/sysfs.c | 100 +++++++++++++ kernel/params.c | 2 + 10 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 drivers/base/mod_devicetable.c