Modules updates for v4.13
Summary of modules changes for the 4.13 merge window: - Minor code cleanups - Avoid accessing mod struct prior to checking module struct version, from Kees - Fix racy atomic inc/dec logic of kmod_concurrent_max in kmod, from Luis Signed-off-by: Jessica Yu <jeyu@kernel.org> -----BEGIN PGP SIGNATURE----- iQIcBAABCgAGBQJZZp4WAAoJEMBFfjjOO8Fy5JkQAIYujpi6ZS7pGpNCXnGa8pnQ E62oLWAM3UndSgzkL6KJ8HXUzc26Wvm56hoF+k/bvQ7fq0qUmMF71yQ7mArzTZEW QW4t7Fu6zTUh4l5hGenoz1ShJbi+rB/pQT8l6AgdCSEZjpcCoWv+sdb93qoT3YO8 /5pugAR2Uid1yb6EVDzItB/tz5w9Vyojp/fePkcz7M0sAI3NCa/0zeWtYgJbXpTW atieqPM8icfP8LNBYaXmA1SowMkW9cIh8AGhBIbvUYP35wTZVP2jJA0GxK6vB/+c pnDRw/zZO+BUYSpv/NMpJsQ2SKX+t2h5uvBqveq3Q5PljcZAvb6L0wt3PSUp4kvz iRPAIb90FtQqBCLfFnDyIMvzVyCXfHq+eVsFYcvlVOWfdkLaeNEhLyn25whkFXr7 ricd/yXKdS8T1WHatR1HqzIk7pog7PsPewVrjl78TBx3nyIMxEhtCpV9MrnditfP IE1/8hQ2rSriSkFeAi5SYxQ5iNwzQKtKOqMiv7lefIuJiCde+0no4XzMrPz/MaU6 UGyTRRNiQXSlfZQaMI4Ru1itVdAugRRVScATz69ggFqRyfCVuByM78RaygfcrPEC H6tHbeJxyEBytlS2qB2cmVXPvIKOdJ3mU9bGdBy9IuXCj8reJMbzQMfIt4lSow+h axggDNhbL2urY9Ymn1wX =tYuD -----END PGP SIGNATURE----- Merge tag 'modules-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux Pull modules updates from Jessica Yu: "Summary of modules changes for the 4.13 merge window: - Minor code cleanups - Avoid accessing mod struct prior to checking module struct version, from Kees - Fix racy atomic inc/dec logic of kmod_concurrent_max in kmod, from Luis" * tag 'modules-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux: module: make the modinfo name const kmod: reduce atomic operations on kmod_concurrent and simplify module: use list_for_each_entry_rcu() on find_module_all() kernel/module.c: suppress warning about unused nowarn variable module: Add module name to modinfo module: Pass struct load_info into symbol checks
This commit is contained in:
commit
3a75ad1457
@ -45,8 +45,6 @@
|
|||||||
|
|
||||||
#include <trace/events/module.h>
|
#include <trace/events/module.h>
|
||||||
|
|
||||||
extern int max_threads;
|
|
||||||
|
|
||||||
#define CAP_BSET (void *)1
|
#define CAP_BSET (void *)1
|
||||||
#define CAP_PI (void *)2
|
#define CAP_PI (void *)2
|
||||||
|
|
||||||
@ -56,6 +54,20 @@ static DEFINE_SPINLOCK(umh_sysctl_lock);
|
|||||||
static DECLARE_RWSEM(umhelper_sem);
|
static DECLARE_RWSEM(umhelper_sem);
|
||||||
|
|
||||||
#ifdef CONFIG_MODULES
|
#ifdef CONFIG_MODULES
|
||||||
|
/*
|
||||||
|
* Assuming:
|
||||||
|
*
|
||||||
|
* threads = div64_u64((u64) totalram_pages * (u64) PAGE_SIZE,
|
||||||
|
* (u64) THREAD_SIZE * 8UL);
|
||||||
|
*
|
||||||
|
* If you need less than 50 threads would mean we're dealing with systems
|
||||||
|
* smaller than 3200 pages. This assuems you are capable of having ~13M memory,
|
||||||
|
* and this would only be an be an upper limit, after which the OOM killer
|
||||||
|
* would take effect. Systems like these are very unlikely if modules are
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
#define MAX_KMOD_CONCURRENT 50
|
||||||
|
static atomic_t kmod_concurrent_max = ATOMIC_INIT(MAX_KMOD_CONCURRENT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
modprobe_path is set via /proc/sys.
|
modprobe_path is set via /proc/sys.
|
||||||
@ -127,10 +139,7 @@ int __request_module(bool wait, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
char module_name[MODULE_NAME_LEN];
|
char module_name[MODULE_NAME_LEN];
|
||||||
unsigned int max_modprobes;
|
|
||||||
int ret;
|
int ret;
|
||||||
static atomic_t kmod_concurrent = ATOMIC_INIT(0);
|
|
||||||
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
|
|
||||||
static int kmod_loop_msg;
|
static int kmod_loop_msg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -154,21 +163,7 @@ int __request_module(bool wait, const char *fmt, ...)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* If modprobe needs a service that is in a module, we get a recursive
|
if (atomic_dec_if_positive(&kmod_concurrent_max) < 0) {
|
||||||
* loop. Limit the number of running kmod threads to max_threads/2 or
|
|
||||||
* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method
|
|
||||||
* would be to run the parents of this process, counting how many times
|
|
||||||
* kmod was invoked. That would mean accessing the internals of the
|
|
||||||
* process tables to get the command line, proc_pid_cmdline is static
|
|
||||||
* and it is not worth changing the proc code just to handle this case.
|
|
||||||
* KAO.
|
|
||||||
*
|
|
||||||
* "trace the ppid" is simple, but will fail if someone's
|
|
||||||
* parent exits. I think this is as good as it gets. --RR
|
|
||||||
*/
|
|
||||||
max_modprobes = min(max_threads/2, MAX_KMOD_CONCURRENT);
|
|
||||||
atomic_inc(&kmod_concurrent);
|
|
||||||
if (atomic_read(&kmod_concurrent) > max_modprobes) {
|
|
||||||
/* We may be blaming an innocent here, but unlikely */
|
/* We may be blaming an innocent here, but unlikely */
|
||||||
if (kmod_loop_msg < 5) {
|
if (kmod_loop_msg < 5) {
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
@ -176,7 +171,6 @@ int __request_module(bool wait, const char *fmt, ...)
|
|||||||
module_name);
|
module_name);
|
||||||
kmod_loop_msg++;
|
kmod_loop_msg++;
|
||||||
}
|
}
|
||||||
atomic_dec(&kmod_concurrent);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +178,12 @@ int __request_module(bool wait, const char *fmt, ...)
|
|||||||
|
|
||||||
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
|
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
|
||||||
|
|
||||||
atomic_dec(&kmod_concurrent);
|
atomic_inc(&kmod_concurrent_max);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__request_module);
|
EXPORT_SYMBOL(__request_module);
|
||||||
|
|
||||||
#endif /* CONFIG_MODULES */
|
#endif /* CONFIG_MODULES */
|
||||||
|
|
||||||
static void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
static void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
||||||
|
@ -300,6 +300,7 @@ int unregister_module_notifier(struct notifier_block *nb)
|
|||||||
EXPORT_SYMBOL(unregister_module_notifier);
|
EXPORT_SYMBOL(unregister_module_notifier);
|
||||||
|
|
||||||
struct load_info {
|
struct load_info {
|
||||||
|
const char *name;
|
||||||
Elf_Ehdr *hdr;
|
Elf_Ehdr *hdr;
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
Elf_Shdr *sechdrs;
|
Elf_Shdr *sechdrs;
|
||||||
@ -600,7 +601,7 @@ static struct module *find_module_all(const char *name, size_t len,
|
|||||||
|
|
||||||
module_assert_mutex_or_preempt();
|
module_assert_mutex_or_preempt();
|
||||||
|
|
||||||
list_for_each_entry(mod, &modules, list) {
|
list_for_each_entry_rcu(mod, &modules, list) {
|
||||||
if (!even_unformed && mod->state == MODULE_STATE_UNFORMED)
|
if (!even_unformed && mod->state == MODULE_STATE_UNFORMED)
|
||||||
continue;
|
continue;
|
||||||
if (strlen(mod->name) == len && !memcmp(mod->name, name, len))
|
if (strlen(mod->name) == len && !memcmp(mod->name, name, len))
|
||||||
@ -1273,12 +1274,13 @@ static u32 resolve_rel_crc(const s32 *crc)
|
|||||||
return *(u32 *)((void *)crc + *crc);
|
return *(u32 *)((void *)crc + *crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_version(Elf_Shdr *sechdrs,
|
static int check_version(const struct load_info *info,
|
||||||
unsigned int versindex,
|
|
||||||
const char *symname,
|
const char *symname,
|
||||||
struct module *mod,
|
struct module *mod,
|
||||||
const s32 *crc)
|
const s32 *crc)
|
||||||
{
|
{
|
||||||
|
Elf_Shdr *sechdrs = info->sechdrs;
|
||||||
|
unsigned int versindex = info->index.vers;
|
||||||
unsigned int i, num_versions;
|
unsigned int i, num_versions;
|
||||||
struct modversion_info *versions;
|
struct modversion_info *versions;
|
||||||
|
|
||||||
@ -1312,17 +1314,16 @@ static int check_version(Elf_Shdr *sechdrs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Broken toolchain. Warn once, then let it go.. */
|
/* Broken toolchain. Warn once, then let it go.. */
|
||||||
pr_warn_once("%s: no symbol version for %s\n", mod->name, symname);
|
pr_warn_once("%s: no symbol version for %s\n", info->name, symname);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
bad_version:
|
bad_version:
|
||||||
pr_warn("%s: disagrees about version of symbol %s\n",
|
pr_warn("%s: disagrees about version of symbol %s\n",
|
||||||
mod->name, symname);
|
info->name, symname);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
|
static inline int check_modstruct_version(const struct load_info *info,
|
||||||
unsigned int versindex,
|
|
||||||
struct module *mod)
|
struct module *mod)
|
||||||
{
|
{
|
||||||
const s32 *crc;
|
const s32 *crc;
|
||||||
@ -1338,8 +1339,8 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs,
|
|||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
return check_version(sechdrs, versindex,
|
return check_version(info, VMLINUX_SYMBOL_STR(module_layout),
|
||||||
VMLINUX_SYMBOL_STR(module_layout), mod, crc);
|
mod, crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First part is kernel version, which we ignore if module has crcs. */
|
/* First part is kernel version, which we ignore if module has crcs. */
|
||||||
@ -1353,8 +1354,7 @@ static inline int same_magic(const char *amagic, const char *bmagic,
|
|||||||
return strcmp(amagic, bmagic) == 0;
|
return strcmp(amagic, bmagic) == 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline int check_version(Elf_Shdr *sechdrs,
|
static inline int check_version(const struct load_info *info,
|
||||||
unsigned int versindex,
|
|
||||||
const char *symname,
|
const char *symname,
|
||||||
struct module *mod,
|
struct module *mod,
|
||||||
const s32 *crc)
|
const s32 *crc)
|
||||||
@ -1362,8 +1362,7 @@ static inline int check_version(Elf_Shdr *sechdrs,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
|
static inline int check_modstruct_version(const struct load_info *info,
|
||||||
unsigned int versindex,
|
|
||||||
struct module *mod)
|
struct module *mod)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -1399,7 +1398,7 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
|
|||||||
if (!sym)
|
if (!sym)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
if (!check_version(info->sechdrs, info->index.vers, name, mod, crc)) {
|
if (!check_version(info, name, mod, crc)) {
|
||||||
sym = ERR_PTR(-EINVAL);
|
sym = ERR_PTR(-EINVAL);
|
||||||
goto getname;
|
goto getname;
|
||||||
}
|
}
|
||||||
@ -1662,21 +1661,6 @@ static inline void remove_notes_attrs(struct module *mod)
|
|||||||
}
|
}
|
||||||
#endif /* CONFIG_KALLSYMS */
|
#endif /* CONFIG_KALLSYMS */
|
||||||
|
|
||||||
static void add_usage_links(struct module *mod)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_MODULE_UNLOAD
|
|
||||||
struct module_use *use;
|
|
||||||
int nowarn;
|
|
||||||
|
|
||||||
mutex_lock(&module_mutex);
|
|
||||||
list_for_each_entry(use, &mod->target_list, target_list) {
|
|
||||||
nowarn = sysfs_create_link(use->target->holders_dir,
|
|
||||||
&mod->mkobj.kobj, mod->name);
|
|
||||||
}
|
|
||||||
mutex_unlock(&module_mutex);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void del_usage_links(struct module *mod)
|
static void del_usage_links(struct module *mod)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_MODULE_UNLOAD
|
#ifdef CONFIG_MODULE_UNLOAD
|
||||||
@ -1689,6 +1673,26 @@ static void del_usage_links(struct module *mod)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int add_usage_links(struct module *mod)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
#ifdef CONFIG_MODULE_UNLOAD
|
||||||
|
struct module_use *use;
|
||||||
|
|
||||||
|
mutex_lock(&module_mutex);
|
||||||
|
list_for_each_entry(use, &mod->target_list, target_list) {
|
||||||
|
ret = sysfs_create_link(use->target->holders_dir,
|
||||||
|
&mod->mkobj.kobj, mod->name);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_unlock(&module_mutex);
|
||||||
|
if (ret)
|
||||||
|
del_usage_links(mod);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int module_add_modinfo_attrs(struct module *mod)
|
static int module_add_modinfo_attrs(struct module *mod)
|
||||||
{
|
{
|
||||||
struct module_attribute *attr;
|
struct module_attribute *attr;
|
||||||
@ -1797,13 +1801,18 @@ static int mod_sysfs_setup(struct module *mod,
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_unreg_param;
|
goto out_unreg_param;
|
||||||
|
|
||||||
add_usage_links(mod);
|
err = add_usage_links(mod);
|
||||||
|
if (err)
|
||||||
|
goto out_unreg_modinfo_attrs;
|
||||||
|
|
||||||
add_sect_attrs(mod, info);
|
add_sect_attrs(mod, info);
|
||||||
add_notes_attrs(mod, info);
|
add_notes_attrs(mod, info);
|
||||||
|
|
||||||
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
|
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_unreg_modinfo_attrs:
|
||||||
|
module_remove_modinfo_attrs(mod);
|
||||||
out_unreg_param:
|
out_unreg_param:
|
||||||
module_param_sysfs_remove(mod);
|
module_param_sysfs_remove(mod);
|
||||||
out_unreg_holders:
|
out_unreg_holders:
|
||||||
@ -2910,9 +2919,15 @@ static int rewrite_section_headers(struct load_info *info, int flags)
|
|||||||
info->index.vers = 0; /* Pretend no __versions section! */
|
info->index.vers = 0; /* Pretend no __versions section! */
|
||||||
else
|
else
|
||||||
info->index.vers = find_sec(info, "__versions");
|
info->index.vers = find_sec(info, "__versions");
|
||||||
info->index.info = find_sec(info, ".modinfo");
|
|
||||||
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
||||||
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||||
|
|
||||||
|
info->index.info = find_sec(info, ".modinfo");
|
||||||
|
if (!info->index.info)
|
||||||
|
info->name = "(missing .modinfo section)";
|
||||||
|
else
|
||||||
|
info->name = get_modinfo(info, "name");
|
||||||
|
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2952,21 +2967,29 @@ static struct module *setup_load_info(struct load_info *info, int flags)
|
|||||||
|
|
||||||
info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
|
info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
|
||||||
if (!info->index.mod) {
|
if (!info->index.mod) {
|
||||||
pr_warn("No module found in object\n");
|
pr_warn("%s: No module found in object\n",
|
||||||
|
info->name ?: "(missing .modinfo name field)");
|
||||||
return ERR_PTR(-ENOEXEC);
|
return ERR_PTR(-ENOEXEC);
|
||||||
}
|
}
|
||||||
/* This is temporary: point mod into copy of data. */
|
/* This is temporary: point mod into copy of data. */
|
||||||
mod = (void *)info->sechdrs[info->index.mod].sh_addr;
|
mod = (void *)info->sechdrs[info->index.mod].sh_addr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we didn't load the .modinfo 'name' field, fall back to
|
||||||
|
* on-disk struct mod 'name' field.
|
||||||
|
*/
|
||||||
|
if (!info->name)
|
||||||
|
info->name = mod->name;
|
||||||
|
|
||||||
if (info->index.sym == 0) {
|
if (info->index.sym == 0) {
|
||||||
pr_warn("%s: module has no symbols (stripped?)\n", mod->name);
|
pr_warn("%s: module has no symbols (stripped?)\n", info->name);
|
||||||
return ERR_PTR(-ENOEXEC);
|
return ERR_PTR(-ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
info->index.pcpu = find_pcpusec(info);
|
info->index.pcpu = find_pcpusec(info);
|
||||||
|
|
||||||
/* Check module struct version now, before we try to use module. */
|
/* Check module struct version now, before we try to use module. */
|
||||||
if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))
|
if (!check_modstruct_version(info, mod))
|
||||||
return ERR_PTR(-ENOEXEC);
|
return ERR_PTR(-ENOEXEC);
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
@ -2987,7 +3010,7 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
|
|||||||
return err;
|
return err;
|
||||||
} else if (!same_magic(modmagic, vermagic, info->index.vers)) {
|
} else if (!same_magic(modmagic, vermagic, info->index.vers)) {
|
||||||
pr_err("%s: version magic '%s' should be '%s'\n",
|
pr_err("%s: version magic '%s' should be '%s'\n",
|
||||||
mod->name, modmagic, vermagic);
|
info->name, modmagic, vermagic);
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3237,7 +3260,7 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
|
|||||||
|
|
||||||
/* module_blacklist is a comma-separated list of module names */
|
/* module_blacklist is a comma-separated list of module names */
|
||||||
static char *module_blacklist;
|
static char *module_blacklist;
|
||||||
static bool blacklisted(char *module_name)
|
static bool blacklisted(const char *module_name)
|
||||||
{
|
{
|
||||||
const char *p;
|
const char *p;
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -3267,7 +3290,7 @@ static struct module *layout_and_allocate(struct load_info *info, int flags)
|
|||||||
if (IS_ERR(mod))
|
if (IS_ERR(mod))
|
||||||
return mod;
|
return mod;
|
||||||
|
|
||||||
if (blacklisted(mod->name))
|
if (blacklisted(info->name))
|
||||||
return ERR_PTR(-EPERM);
|
return ERR_PTR(-EPERM);
|
||||||
|
|
||||||
err = check_modinfo(mod, info, flags);
|
err = check_modinfo(mod, info, flags);
|
||||||
|
@ -2126,6 +2126,7 @@ static void add_header(struct buffer *b, struct module *mod)
|
|||||||
buf_printf(b, "#include <linux/compiler.h>\n");
|
buf_printf(b, "#include <linux/compiler.h>\n");
|
||||||
buf_printf(b, "\n");
|
buf_printf(b, "\n");
|
||||||
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
|
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
|
||||||
|
buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
|
||||||
buf_printf(b, "\n");
|
buf_printf(b, "\n");
|
||||||
buf_printf(b, "__visible struct module __this_module\n");
|
buf_printf(b, "__visible struct module __this_module\n");
|
||||||
buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
|
buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user