cregit-Linux how code gets into the kernel

Release 4.16 kernel/module.c

Directory: kernel
/*
   Copyright (C) 2002 Richard Henderson
   Copyright (C) 2001 Rusty Russell, 2002, 2010 Rusty Russell IBM.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <linux/export.h>
#include <linux/extable.h>
#include <linux/moduleloader.h>
#include <linux/trace_events.h>
#include <linux/init.h>
#include <linux/kallsyms.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/elf.h>
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <linux/rcupdate.h>
#include <linux/capability.h>
#include <linux/cpu.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/vermagic.h>
#include <linux/notifier.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/rculist.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <linux/set_memory.h>
#include <asm/mmu_context.h>
#include <linux/license.h>
#include <asm/sections.h>
#include <linux/tracepoint.h>
#include <linux/ftrace.h>
#include <linux/livepatch.h>
#include <linux/async.h>
#include <linux/percpu.h>
#include <linux/kmemleak.h>
#include <linux/jump_label.h>
#include <linux/pfn.h>
#include <linux/bsearch.h>
#include <linux/dynamic_debug.h>
#include <linux/audit.h>
#include <uapi/linux/module.h>
#include "module-internal.h"


#define CREATE_TRACE_POINTS
#include <trace/events/module.h>

#ifndef ARCH_SHF_SMALL

#define ARCH_SHF_SMALL 0
#endif

/*
 * Modules' sections will be aligned on page boundaries
 * to ensure complete separation of code and data, but
 * only when CONFIG_STRICT_MODULE_RWX=y
 */
#ifdef CONFIG_STRICT_MODULE_RWX

# define debug_align(X) ALIGN(X, PAGE_SIZE)
#else

# define debug_align(X) (X)
#endif

/* If this is set, the section belongs in the init part of the module */

#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))

/*
 * Mutex protects:
 * 1) List of modules (also safely readable with preempt_disable),
 * 2) module_use links,
 * 3) module_addr_min/module_addr_max.
 * (delete and add uses RCU list operations). */
DEFINE_MUTEX(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
static LIST_HEAD(modules);

#ifdef CONFIG_MODULES_TREE_LOOKUP

/*
 * Use a latched RB-tree for __module_address(); this allows us to use
 * RCU-sched lookups of the address from any context.
 *
 * This is conditional on PERF_EVENTS || TRACING because those can really hit
 * __module_address() hard by doing a lot of stack unwinding; potentially from
 * NMI context.
 */


static __always_inline unsigned long __mod_tree_val(struct latch_tree_node *n) { struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->base; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra3282.05%150.00%
Rusty Russell717.95%150.00%
Total39100.00%2100.00%


static __always_inline unsigned long __mod_tree_size(struct latch_tree_node *n) { struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->size; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra3282.05%150.00%
Rusty Russell717.95%150.00%
Total39100.00%2100.00%


static __always_inline bool mod_tree_less(struct latch_tree_node *a, struct latch_tree_node *b) { return __mod_tree_val(a) < __mod_tree_val(b); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra28100.00%1100.00%
Total28100.00%1100.00%


static __always_inline int mod_tree_comp(void *key, struct latch_tree_node *n) { unsigned long val = (unsigned long)key; unsigned long start, end; start = __mod_tree_val(n); if (val < start) return -1; end = start + __mod_tree_size(n); if (val >= end) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra70100.00%1100.00%
Total70100.00%1100.00%

static const struct latch_tree_ops mod_tree_ops = { .less = mod_tree_less, .comp = mod_tree_comp, }; static struct mod_tree_root { struct latch_tree_root root; unsigned long addr_min; unsigned long addr_max; } mod_tree __cacheline_aligned = { .addr_min = -1UL, }; #define module_addr_min mod_tree.addr_min #define module_addr_max mod_tree.addr_max
static noinline void __mod_tree_insert(struct mod_tree_node *node) { latch_tree_insert(&node->node, &mod_tree.root, &mod_tree_ops); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra28100.00%1100.00%
Total28100.00%1100.00%


static void __mod_tree_remove(struct mod_tree_node *node) { latch_tree_erase(&node->node, &mod_tree.root, &mod_tree_ops); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra27100.00%1100.00%
Total27100.00%1100.00%

/* * These modifications: insert, remove_init and remove; are serialized by the * module_mutex. */
static void mod_tree_insert(struct module *mod) { mod->core_layout.mtn.mod = mod; mod->init_layout.mtn.mod = mod; __mod_tree_insert(&mod->core_layout.mtn); if (mod->init_layout.size) __mod_tree_insert(&mod->init_layout.mtn); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra4474.58%266.67%
Rusty Russell1525.42%133.33%
Total59100.00%3100.00%


static void mod_tree_remove_init(struct module *mod) { if (mod->init_layout.size) __mod_tree_remove(&mod->init_layout.mtn); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra2379.31%266.67%
Rusty Russell620.69%133.33%
Total29100.00%3100.00%


static void mod_tree_remove(struct module *mod) { __mod_tree_remove(&mod->core_layout.mtn); mod_tree_remove_init(mod); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra2388.46%266.67%
Rusty Russell311.54%133.33%
Total26100.00%3100.00%


static struct module *mod_find(unsigned long addr) { struct latch_tree_node *ltn; ltn = latch_tree_find((void *)addr, &mod_tree.root, &mod_tree_ops); if (!ltn) return NULL; return container_of(ltn, struct mod_tree_node, node)->mod; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra57100.00%3100.00%
Total57100.00%3100.00%

#else /* MODULES_TREE_LOOKUP */ static unsigned long module_addr_min = -1UL, module_addr_max = 0;
static void mod_tree_insert(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra10100.00%1100.00%
Total10100.00%1100.00%


static void mod_tree_remove_init(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra10100.00%1100.00%
Total10100.00%1100.00%


static void mod_tree_remove(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra10100.00%1100.00%
Total10100.00%1100.00%


static struct module *mod_find(unsigned long addr) { struct module *mod; list_for_each_entry_rcu(mod, &modules, list) { if (within_module(addr, mod)) return mod; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra42100.00%1100.00%
Total42100.00%1100.00%

#endif /* MODULES_TREE_LOOKUP */ /* * Bounds of module text, for speeding up __module_address. * Protected by module_mutex. */
static void __mod_update_bounds(void *base, unsigned int size) { unsigned long min = (unsigned long)base; unsigned long max = min + size; if (min < module_addr_min) module_addr_min = min; if (max > module_addr_max) module_addr_max = max; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra52100.00%1100.00%
Total52100.00%1100.00%


static void mod_update_bounds(struct module *mod) { __mod_update_bounds(mod->core_layout.base, mod->core_layout.size); if (mod->init_layout.size) __mod_update_bounds(mod->init_layout.base, mod->init_layout.size); }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra3469.39%150.00%
Rusty Russell1530.61%150.00%
Total49100.00%2100.00%

#ifdef CONFIG_KGDB_KDB struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ #endif /* CONFIG_KGDB_KDB */
static void module_assert_mutex(void) { lockdep_assert_held(&module_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell857.14%150.00%
Peter Zijlstra642.86%150.00%
Total14100.00%2100.00%


static void module_assert_mutex_or_preempt(void) { #ifdef CONFIG_LOCKDEP if (unlikely(!debug_locks)) return; WARN_ON_ONCE(!rcu_read_lock_sched_held() && !lockdep_is_held(&module_mutex)); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Peter Zijlstra2877.78%133.33%
Rusty Russell719.44%133.33%
Steven Rostedt12.78%133.33%
Total36100.00%3100.00%

static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE); #ifndef CONFIG_MODULE_SIG_FORCE module_param(sig_enforce, bool_enable_only, 0644); #endif /* !CONFIG_MODULE_SIG_FORCE */ /* * Export sig_enforce kernel cmdline parameter to allow other subsystems rely * on that instead of directly to CONFIG_MODULE_SIG_FORCE config. */
bool is_module_sig_enforced(void) { return sig_enforce; }

Contributors

PersonTokensPropCommitsCommitProp
Bruno E. O. Meneguele10100.00%1100.00%
Total10100.00%1100.00%

EXPORT_SYMBOL(is_module_sig_enforced); /* Block module loading/unloading? */ int modules_disabled = 0; core_param(nomodule, modules_disabled, bint, 0); /* Waiting for a module to finish initializing? */ static DECLARE_WAIT_QUEUE_HEAD(module_wq); static BLOCKING_NOTIFIER_HEAD(module_notify_list);
int register_module_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&module_notify_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
John Levon1789.47%150.00%
Alan Stern210.53%150.00%
Total19100.00%2100.00%

EXPORT_SYMBOL(register_module_notifier);
int unregister_module_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&module_notify_list, nb); }

Contributors

PersonTokensPropCommitsCommitProp
John Levon1789.47%150.00%
Alan Stern210.53%150.00%
Total19100.00%2100.00%

EXPORT_SYMBOL(unregister_module_notifier); struct load_info { const char *name; Elf_Ehdr *hdr; unsigned long len; Elf_Shdr *sechdrs; char *secstrings, *strtab; unsigned long symoffs, stroffs; struct _ddebug *debug; unsigned int num_debug; bool sig_ok; #ifdef CONFIG_KALLSYMS unsigned long mod_kallsyms_init_off; #endif struct { unsigned int sym, str, mod, vers, info, pcpu; } index; }; /* * We require a truly strong try_module_get(): 0 means success. * Otherwise an error is returned due to ongoing or failed * initialization etc. */
static inline int strong_try_module_get(struct module *mod) { BUG_ON(mod && mod->state == MODULE_STATE_UNFORMED); if (mod && mod->state == MODULE_STATE_COMING) return -EBUSY; if (try_module_get(mod)) return 0; else return -ENOENT; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell52100.00%3100.00%
Total52100.00%3100.00%


static inline void add_taint_module(struct module *mod, unsigned flag, enum lockdep_ok lockdep_ok) { add_taint(flag, lockdep_ok); set_bit(flag, &mod->taints); }

Contributors

PersonTokensPropCommitsCommitProp
Florin Malita2466.67%133.33%
Petr Mladek616.67%133.33%
Rusty Russell616.67%133.33%
Total36100.00%3100.00%

/* * A thread that wants to hold a reference to a module only while it * is running can call this to safely exit. nfsd and lockd use this. */
void __noreturn __module_put_and_exit(struct module *mod, long code) { module_put(mod); do_exit(code); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2395.83%150.00%
Jiri Kosina14.17%150.00%
Total24100.00%2100.00%

EXPORT_SYMBOL(__module_put_and_exit); /* Find a module section: 0 means not found. */
static unsigned int find_sec(const struct load_info *info, const char *name) { unsigned int i; for (i = 1; i < info->hdr->e_shnum; i++) { Elf_Shdr *shdr = &info->sechdrs[i]; /* Alloc bit cleared means "ignore it." */ if ((shdr->sh_flags & SHF_ALLOC) && strcmp(info->secstrings + shdr->sh_name, name) == 0) return i; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell85100.00%2100.00%
Total85100.00%2100.00%

/* Find a module section, or NULL. */
static void *section_addr(const struct load_info *info, const char *name) { /* Section 0 has sh_addr 0. */ return (void *)info->sechdrs[find_sec(info, name)].sh_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell38100.00%2100.00%
Total38100.00%2100.00%

/* Find a module section, or NULL. Fill in number of "objects" in section. */
static void *section_objs(const struct load_info *info, const char *name, size_t object_size, unsigned int *num) { unsigned int sec = find_sec(info, name); /* Section 0 has sh_addr 0 and sh_size 0. */ *num = info->sechdrs[sec].sh_size / object_size; return (void *)info->sechdrs[sec].sh_addr; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell66100.00%2100.00%
Total66100.00%2100.00%

/* Provided by the linker */ extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const s32 __start___kcrctab[]; extern const s32 __start___kcrctab_gpl[]; extern const s32 __start___kcrctab_gpl_future[]; #ifdef CONFIG_UNUSED_SYMBOLS extern const struct kernel_symbol __start___ksymtab_unused[]; extern const struct kernel_symbol __stop___ksymtab_unused[]; extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const s32 __start___kcrctab_unused[]; extern const s32 __start___kcrctab_unused_gpl[]; #endif #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL #else #define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL) #endif
static bool each_symbol_in_section(const struct symsearch *arr, unsigned int arrsize, struct module *owner, bool (*fn)(const struct symsearch *syms, struct module *owner, void *data), void *data) { unsigned int j; for (j = 0; j < arrsize; j++) { if (fn(&arr[j], owner, data)) return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6068.97%150.00%
Sam Ravnborg2731.03%150.00%
Total87100.00%2100.00%

/* Returns true as soon as fn returns true, otherwise false. */
bool each_symbol_section(bool (*fn)(const struct symsearch *arr, struct module *owner, void *data), void *data) { struct module *mod; static const struct symsearch arr[] = { { __start___ksymtab, __stop___ksymtab, __start___kcrctab, NOT_GPL_ONLY, false }, { __start___ksymtab_gpl, __stop___ksymtab_gpl, __start___kcrctab_gpl, GPL_ONLY, false }, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false }, #ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true }, #endif }; module_assert_mutex_or_preempt(); if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) return true; list_for_each_entry_rcu(mod, &modules, list) { struct symsearch arr[] = { { mod->syms, mod->syms + mod->num_syms, mod->crcs, NOT_GPL_ONLY, false }, { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, mod->gpl_crcs, GPL_ONLY, false }, { mod->gpl_future_syms, mod->gpl_future_syms + mod->num_gpl_future_syms, mod->gpl_future_crcs, WILL_BE_GPL_ONLY, false }, #ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->unused_syms + mod->num_unused_syms, mod->unused_crcs, NOT_GPL_ONLY, true }, { mod->unused_gpl_syms, mod->unused_gpl_syms + mod->num_unused_gpl_syms, mod->unused_gpl_crcs, GPL_ONLY, true }, #endif }; if (mod->state == MODULE_STATE_UNFORMED) continue; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell24882.39%743.75%
Sam Ravnborg185.98%16.25%
Denys Vlasenko144.65%16.25%
Arjan van de Ven82.66%16.25%
Linus Torvalds (pre-git)51.66%16.25%
Peter Zijlstra31.00%16.25%
Greg Kroah-Hartman20.66%16.25%
Andi Kleen10.33%16.25%
David Mosberger-Tang10.33%16.25%
Linus Torvalds10.33%16.25%
Total301100.00%16100.00%

EXPORT_SYMBOL_GPL(each_symbol_section); struct find_symbol_arg { /* Input */ const char *name; bool gplok; bool warn; /* Output */ struct module *owner; const s32 *crc; const struct kernel_symbol *sym; };
static bool check_symbol(const struct symsearch *syms, struct module *owner, unsigned int symnum, void *data) { struct find_symbol_arg *fsa = data; if (!fsa->gplok) { if (syms->licence == GPL_ONLY) return false; if (syms->licence == WILL_BE_GPL_ONLY && fsa->warn) { pr_warn("Symbol %s is being used by a non-GPL module, " "which will not be allowed in the future\n", fsa->name); } } #ifdef CONFIG_UNUSED_SYMBOLS if (syms->unused && fsa->warn) { pr_warn("Symbol %s is marked as UNUSED, however this module is " "using it.\n", fsa->name); pr_warn("This symbol will go away in the future.\n"); pr_warn("Please evaluate if this is the right api to use and " "if it really is, submit a report to the linux kernel " "mailing list together with submitting your code for " "inclusion.\n"); } #endif fsa->owner = owner; fsa->crc = symversion(syms->crcs, symnum); fsa->sym = &syms->start[symnum]; return true; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell12382.00%228.57%
Arjan van de Ven96.00%114.29%
Andrew Morton85.33%114.29%
Denys Vlasenko53.33%114.29%
Yannick Guerrini32.00%114.29%
Tim Abbott21.33%114.29%
Total150100.00%7100.00%


static int cmp_name(const void *va, const void *vb) { const char *a; const struct kernel_symbol *b; a = va; b = vb; return strcmp(a, b->name); }

Contributors

PersonTokensPropCommitsCommitProp
Alessio Igor Bogani45100.00%1100.00%
Total45100.00%1100.00%


static bool find_symbol_in_section(const struct symsearch *syms, struct module *owner, void *data) { struct find_symbol_arg *fsa = data; struct kernel_symbol *sym; sym = bsearch(fsa->name, syms->start, syms->stop - syms->start, sizeof(struct kernel_symbol), cmp_name); if (sym != NULL && check_symbol(syms, owner, sym - syms->start, data)) return true; return false; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5359.55%150.00%
Alessio Igor Bogani3640.45%150.00%
Total89100.00%2100.00%

/* Find a symbol and return it, along with, (optional) crc and * (optional) module which owns it. Needs preempt disabled or module_mutex. */
const struct kernel_symbol *find_symbol(const char *name, struct module **owner, const s32 **crc, bool gplok, bool warn) { struct find_symbol_arg fsa; fsa.name = name; fsa.gplok = gplok; fsa.warn = warn; if (each_symbol_section(find_symbol_in_section, &fsa)) { if (owner) *owner = fsa.owner; if (crc) *crc = fsa.crc; return fsa.sym; } pr_debug("Failed to find symbol %s\n", name); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell7371.57%440.00%
Greg Kroah-Hartman1413.73%110.00%
Tim Abbott65.88%110.00%
Linus Torvalds (pre-git)54.90%110.00%
Arjan van de Ven21.96%110.00%
Jim Cromie10.98%110.00%
Ard Biesheuvel10.98%110.00%
Total102100.00%10100.00%

EXPORT_SYMBOL_GPL(find_symbol); /* * Search for module by name: must hold module_mutex (or preempt disabled * for read-only access). */
static struct module *find_module_all(const char *name, size_t len, bool even_unformed) { struct module *mod; module_assert_mutex_or_preempt(); list_for_each_entry_rcu(mod, &modules, list) { if (!even_unformed && mod->state == MODULE_STATE_UNFORMED) continue; if (strlen(mod->name) == len && !memcmp(mod->name, name, len)) return mod; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3848.72%342.86%
Linus Torvalds (pre-git)2126.92%114.29%
Mathias Krause1620.51%114.29%
Peter Zijlstra22.56%114.29%
Luis R. Rodriguez11.28%114.29%
Total78100.00%7100.00%


struct module *find_module(const char *name) { module_assert_mutex(); return find_module_all(name, strlen(name), false); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2382.14%266.67%
Mathias Krause517.86%133.33%
Total28100.00%3100.00%

EXPORT_SYMBOL_GPL(find_module); #ifdef CONFIG_SMP
static inline void __percpu *mod_percpu(struct module *mod) { return mod->percpu; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo19100.00%2100.00%
Total19100.00%2100.00%


static int percpu_modalloc(struct module *mod, struct load_info *info) { Elf_Shdr *pcpusec = &info->sechdrs[info->index.pcpu]; unsigned long align = pcpusec->sh_addralign; if (!pcpusec->sh_size) return 0; if (align > PAGE_SIZE) { pr_warn("%s: per-cpu alignment %li > %li\n", mod->name, align, PAGE_SIZE); align = PAGE_SIZE; } mod->percpu = __alloc_reserved_percpu(pcpusec->sh_size, align); if (!mod->percpu) { pr_warn("%s: Could not allocate %lu bytes percpu data\n", mod->name, (unsigned long)pcpusec->sh_size); return -ENOMEM; } mod->percpu_size = pcpusec->sh_size; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo7255.81%350.00%
Rusty Russell5542.64%233.33%
Andrew Morton21.55%116.67%
Total129100.00%6100.00%


static void percpu_modfree(struct module *mod) { free_percpu(mod->percpu); }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo18100.00%2100.00%
Total18100.00%2100.00%


static unsigned int find_pcpusec(struct load_info *info) { return find_sec(info, ".data..percpu"); }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo1575.00%133.33%
Rusty Russell420.00%133.33%
Denys Vlasenko15.00%133.33%
Total20100.00%3100.00%


static void percpu_modcopy(struct module *mod, const void *from, unsigned long size) { int cpu; for_each_possible_cpu(cpu) memcpy(per_cpu_ptr(mod->percpu, cpu), from, size); }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo43100.00%2100.00%
Total43100.00%2100.00%


bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr) { struct module *mod; unsigned int cpu; preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; if (!mod->percpu_size) continue; for_each_possible_cpu(cpu) { void *start = per_cpu_ptr(mod->percpu, cpu); void *va = (void *)addr; if (va >= start && va < start + mod->percpu_size) { if (can_addr) { *can_addr = (unsigned long) (va - start); *can_addr += (unsigned long) per_cpu_ptr(mod->percpu, get_boot_cpu_id()); } preempt_enable(); return true; } } } preempt_enable(); return false; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo8658.90%125.00%
Thomas Gleixner3221.92%125.00%
Peter Zijlstra1913.01%125.00%
Rusty Russell96.16%125.00%
Total146100.00%4100.00%

/** * is_module_percpu_address - test whether address is from module static percpu * @addr: address to test * * Test whether @addr belongs to module static percpu area. * * RETURNS: * %true if @addr is from module static percpu area */
bool is_module_percpu_address(unsigned long addr) { return __is_module_percpu_address(addr, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner17100.00%1100.00%
Total17100.00%1100.00%

#else /* ... !CONFIG_SMP */
static inline void __percpu *mod_percpu(struct module *mod) { return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo1376.47%150.00%
Andrew Morton423.53%150.00%
Total17100.00%2100.00%


static int percpu_modalloc(struct module *mod, struct load_info *info) { /* UP modules shouldn't have this section: ENOMEM isn't quite right */ if (info->sechdrs[info->index.pcpu].sh_size != 0) return -ENOMEM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2560.98%133.33%
Tejun Heo921.95%133.33%
Andrew Morton717.07%133.33%
Total41100.00%3100.00%


static inline void percpu_modfree(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton763.64%150.00%
Tejun Heo436.36%150.00%
Total11100.00%2100.00%


static unsigned int find_pcpusec(struct load_info *info) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton1280.00%150.00%
Rusty Russell320.00%150.00%
Total15100.00%2100.00%


static inline void percpu_modcopy(struct module *mod, const void *from, unsigned long size) { /* pcpusec should be 0, and size of that section should be 0. */ BUG_ON(size != 0); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton2586.21%150.00%
Tejun Heo413.79%150.00%
Total29100.00%2100.00%


bool is_module_percpu_address(unsigned long addr) { return false; }

Contributors

PersonTokensPropCommitsCommitProp
Tejun Heo12100.00%1100.00%
Total12100.00%1100.00%


bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr) { return false; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner17100.00%1100.00%
Total17100.00%1100.00%

#endif /* CONFIG_SMP */ #define MODINFO_ATTR(field) \ static void setup_modinfo_##field(struct module *mod, const char *s) \ { \ mod->field = kstrdup(s, GFP_KERNEL); \ } \ static ssize_t show_modinfo_##field(struct module_attribute *mattr, \ struct module_kobject *mk, char *buffer) \ { \ return scnprintf(buffer, PAGE_SIZE, "%s\n", mk->mod->field); \ } \ static int modinfo_##field##_exists(struct module *mod) \ { \ return mod->field != NULL; \ } \ static void free_modinfo_##field(struct module *mod) \ { \ kfree(mod->field); \ mod->field = NULL; \ } \ static struct module_attribute modinfo_##field = { \ .attr = { .name = __stringify(field), .mode = 0444 }, \ .show = show_modinfo_##field, \ .setup = setup_modinfo_##field, \ .test = modinfo_##field##_exists, \ .free = free_modinfo_##field, \ }; MODINFO_ATTR(version); MODINFO_ATTR(srcversion); static char last_unloaded_module[MODULE_NAME_LEN+1]; #ifdef CONFIG_MODULE_UNLOAD EXPORT_TRACEPOINT_SYMBOL(module_get); /* MODULE_REF_BASE is the base reference count by kmodule loader. */ #define MODULE_REF_BASE 1 /* Init the unload section of the module. */
static int module_unload_init(struct module *mod) { /* * Initialize reference counter to MODULE_REF_BASE. * refcnt == 0 means module is going. */ atomic_set(&mod->refcnt, MODULE_REF_BASE); INIT_LIST_HEAD(&mod->source_list); INIT_LIST_HEAD(&mod->target_list); /* Hold reference count during initialization. */ atomic_inc(&mod->refcnt); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell1938.00%222.22%
Masami Hiramatsu1020.00%222.22%
Linus Torvalds918.00%111.11%
Linus Torvalds (pre-git)612.00%111.11%
Andrew Morton48.00%111.11%
Christoph Lameter12.00%111.11%
Eric Dumazet12.00%111.11%
Total50100.00%9100.00%

/* Does a already use b? */
static int already_uses(struct module *a, struct module *b) { struct module_use *use; list_for_each_entry(use, &b->source_list, source_list) { if (use->source == a) { pr_debug("%s uses %s!\n", a->name, b->name); return 1; } } pr_debug("%s does not use %s!\n", a->name, b->name); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4663.01%125.00%
Linus Torvalds (pre-git)2230.14%125.00%
Linus Torvalds34.11%125.00%
Jim Cromie22.74%125.00%
Total73100.00%4100.00%

/* * Module a uses b * - we add 'a' as a "source", 'b' as a "target" of module use * - the module_use is added to the list of 'b' sources (so * 'b' can walk the list to see who sourced them), and of 'a' * targets (so 'a' can see what modules it targets). */
static int add_module_usage(struct module *a, struct module *b) { struct module_use *use; pr_debug("Allocating new usage for %s.\n", a->name); use = kmalloc(sizeof(*use), GFP_ATOMIC); if (!use) return -ENOMEM; use->source = a; use->target = b; list_add(&use->source_list, &b->source_list); list_add(&use->target_list, &a->target_list); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds9298.92%150.00%
Jim Cromie11.08%150.00%
Total93100.00%2100.00%

/* Module a uses b: caller needs module_mutex() */
int ref_module(struct module *a, struct module *b) { int err; if (b == NULL || already_uses(a, b)) return 0; /* If module isn't available, we fail. */ err = strong_try_module_get(b); if (err) return err; err = add_module_usage(a, b); if (err) { module_put(b); return err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5269.33%444.44%
Linus Torvalds (pre-git)1621.33%333.33%
Linus Torvalds56.67%111.11%
Kay Sievers22.67%111.11%
Total75100.00%9100.00%

EXPORT_SYMBOL_GPL(ref_module); /* Clear the unload stuff of the module. */
static void module_unload_free(struct module *mod) { struct module_use *use, *tmp; mutex_lock(&module_mutex); list_for_each_entry_safe(use, tmp, &mod->target_list, target_list) { struct module *i = use->target; pr_debug("%s unusing %s\n", mod->name, i->name); module_put(i); list_del(&use->source_list); list_del(&use->target_list); kfree(use); } mutex_unlock(&module_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5257.14%225.00%
Linus Torvalds2830.77%225.00%
Linus Torvalds (pre-git)1010.99%337.50%
Jim Cromie11.10%112.50%
Total91100.00%8100.00%

#ifdef CONFIG_MODULE_FORCE_UNLOAD
static inline int try_force_unload(unsigned int flags) { int ret = (flags & O_TRUNC); if (ret) add_taint(TAINT_FORCED_RMMOD, LOCKDEP_NOW_UNRELIABLE); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton2779.41%125.00%
Randy Dunlap38.82%125.00%
Akinobu Mita25.88%125.00%
Rusty Russell25.88%125.00%
Total34100.00%4100.00%

#else
static inline int try_force_unload(unsigned int flags) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton1392.86%150.00%
Akinobu Mita17.14%150.00%
Total14100.00%2100.00%

#endif /* CONFIG_MODULE_FORCE_UNLOAD */ /* Try to release refcount of module, 0 means success. */
static int try_release_module_ref(struct module *mod) { int ret; /* Try to decrement refcnt which we set at loading */ ret = atomic_sub_return(MODULE_REF_BASE, &mod->refcnt); BUG_ON(ret < 0); if (ret) /* Someone can put this right now, recover with checking */ ret = atomic_add_unless(&mod->refcnt, MODULE_REF_BASE, 0); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Masami Hiramatsu3562.50%133.33%
Andrew Morton1628.57%133.33%
Rusty Russell58.93%133.33%
Total56100.00%3100.00%


static int try_stop_module(struct module *mod, int flags, int *forced) { /* If it's not unused, quit unless we're forcing. */ if (try_release_module_ref(mod) != 0) { *forced = try_force_unload(flags); if (!(*forced)) return -EWOULDBLOCK; } /* Mark it as dying. */ mod->state = MODULE_STATE_GOING; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Masami Hiramatsu3660.00%114.29%
Rusty Russell1931.67%228.57%
Linus Torvalds (pre-git)46.67%342.86%
Andrew Morton11.67%114.29%
Total60100.00%7100.00%

/** * module_refcount - return the refcount or -1 if unloading * * @mod: the module we're checking * * Returns: * -1 if the module is in the process of unloading * otherwise the number of references in the kernel to the module */
int module_refcount(struct module *mod) { return atomic_read(&mod->refcnt) - MODULE_REF_BASE; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell942.86%233.33%
Masami Hiramatsu523.81%233.33%
Nicholas Piggin523.81%116.67%
Linus Torvalds (pre-git)29.52%116.67%
Total21100.00%6100.00%

EXPORT_SYMBOL(module_refcount); /* This exists whether we can unload or not */ static void free_module(struct module *mod); SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags) { struct module *mod; char name[MODULE_NAME_LEN]; int ret, forced = 0; if (!capable(CAP_SYS_MODULE) || modules_disabled) return -EPERM; if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) return -EFAULT; name[MODULE_NAME_LEN-1] = '\0'; audit_log_kern_module(name); if (mutex_lock_interruptible(&module_mutex) != 0) return -EINTR; mod = find_module(name); if (!mod) { ret = -ENOENT; goto out; } if (!list_empty(&mod->source_list)) { /* Other modules depend on us: get rid of them first. */ ret = -EWOULDBLOCK; goto out; } /* Doing init or already dying? */ if (mod->state != MODULE_STATE_LIVE) { /* FIXME: if (force), slam module count damn the torpedoes */ pr_debug("%s already dying\n", mod->name); ret = -EBUSY; goto out; } /* If it has an init func, it must have an exit func to unload */ if (mod->init && !mod->exit) { forced = try_force_unload(flags); if (!forced) { /* This module can't be removed */ ret = -EBUSY; goto out; } } /* Stop the machine so refcounts can't move and disable module. */ ret = try_stop_module(mod, flags, &forced); if (ret != 0) goto out; mutex_unlock(&module_mutex); /* Final destruction now no one is using it. */ if (mod->exit != NULL) mod->exit(); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); klp_module_going(mod); ftrace_release_mod(mod); async_synchronize_full(); /* Store the name of the last unloaded module for diagnostic purposes */ strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module)); free_module(mod); return 0; out: mutex_unlock(&module_mutex); return ret; }
static inline void print_unload_info(struct seq_file *m, struct module *mod) { struct module_use *use; int printed_something = 0; seq_printf(m, " %i ", module_refcount(mod)); /* * Always include a trailing , so userspace can differentiate * between this and the old multi-field proc format. */ list_for_each_entry(use, &mod->source_list, source_list) { printed_something = 1; seq_printf(m, "%s,", use->source->name); } if (mod->init != NULL && mod->exit == NULL) { printed_something = 1; seq_puts(m, "[permanent],"); } if (!printed_something) seq_puts(m, "-"); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell7469.81%545.45%
Linus Torvalds (pre-git)2321.70%218.18%
Ionut Alexa32.83%19.09%
Linus Torvalds32.83%19.09%
Brian Gerst21.89%19.09%
Jianjun Kong10.94%19.09%
Total106100.00%11100.00%


void __symbol_put(const char *symbol) { struct module *owner; preempt_disable(); if (!find_symbol(symbol, &owner, NULL, true, false)) BUG(); module_put(owner); preempt_enable(); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3371.74%555.56%
Linus Torvalds (pre-git)1123.91%222.22%
Tim Abbott12.17%111.11%
Kai Germaschewski12.17%111.11%
Total46100.00%9100.00%

EXPORT_SYMBOL(__symbol_put); /* Note this assumes addr is a function, which it currently always is. */
void symbol_put_addr(void *addr) { struct module *modaddr; unsigned long a = (unsigned long)dereference_function_descriptor(addr); if (core_kernel_text(a)) return; /* * Even though we hold a reference on the module; we still need to * disable preemption in order to safely traverse the data structure. */ preempt_disable(); modaddr = __module_text_address(a); BUG_ON(!modaddr); module_put(modaddr); preempt_enable(); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4473.33%466.67%
Trent Piepho915.00%116.67%
Peter Zijlstra711.67%116.67%
Total60100.00%6100.00%

EXPORT_SYMBOL_GPL(symbol_put_addr);
static ssize_t show_refcnt(struct module_attribute *mattr, struct module_kobject *mk, char *buffer) { return sprintf(buffer, "%i\n", module_refcount(mk->mod)); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2160.00%450.00%
Tejun Heo514.29%112.50%
Greg Kroah-Hartman411.43%112.50%
Kay Sievers411.43%112.50%
Christoph Hellwig12.86%112.50%
Total35100.00%8100.00%

static struct module_attribute modinfo_refcnt = __ATTR(refcnt, 0444, show_refcnt, NULL);
void __module_get(struct module *module) { if (module) { preempt_disable(); atomic_inc(&module->refcnt); trace_module_get(module, _RET_IP_); preempt_enable(); } }

Contributors

PersonTokensPropCommitsCommitProp
Steven Rostedt3491.89%150.00%
Masami Hiramatsu38.11%150.00%
Total37100.00%2100.00%

EXPORT_SYMBOL(__module_get);
bool try_module_get(struct module *module) { bool ret = true; if (module) { preempt_disable(); /* Note: here, we can fail to get a reference */ if (likely(module_is_live(module) && atomic_inc_not_zero(&module->refcnt) != 0)) trace_module_get(module, _RET_IP_); else ret = false; preempt_enable(); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Steven Rostedt5485.71%133.33%
Masami Hiramatsu914.29%266.67%
Total63100.00%3100.00%

EXPORT_SYMBOL(try_module_get);
void module_put(struct module *module) { int ret; if (module) { preempt_disable(); ret = atomic_dec_if_positive(&module->refcnt); WARN_ON(ret < 0); /* Failed to put refcount */ trace_module_put(module, _RET_IP_); preempt_enable(); } }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro2244.00%116.67%
Masami Hiramatsu1632.00%233.33%
Li Zefan714.00%116.67%
Christoph Lameter48.00%116.67%
Eric Dumazet12.00%116.67%
Total50100.00%6100.00%

EXPORT_SYMBOL(module_put); #else /* !CONFIG_MODULE_UNLOAD */
static inline void print_unload_info(struct seq_file *m, struct module *mod) { /* We don't know the usage count, or what modules are using. */ seq_puts(m, " - -"); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2392.00%250.00%
Ionut Alexa14.00%125.00%
Jianjun Kong14.00%125.00%
Total25100.00%4100.00%


static inline void module_unload_free(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell11100.00%2100.00%
Total11100.00%2100.00%


int ref_module(struct module *a, struct module *b) { return strong_try_module_get(b); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell21100.00%3100.00%
Total21100.00%3100.00%

EXPORT_SYMBOL_GPL(ref_module);
static inline int module_unload_init(struct module *mod) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell15100.00%2100.00%
Total15100.00%2100.00%

#endif /* CONFIG_MODULE_UNLOAD */
static size_t module_flags_taint(struct module *mod, char *buf) { size_t l = 0; int i; for (i = 0; i < TAINT_FLAGS_COUNT; i++) { if (taint_flags[i].module && test_bit(i, &mod->taints)) buf[l++] = taint_flags[i].c_true; } return l; }

Contributors

PersonTokensPropCommitsCommitProp
Kevin Winchester3041.10%120.00%
Petr Mladek2939.73%120.00%
Mathieu Desnoyers1013.70%120.00%
Josh Poimboeuf34.11%120.00%
Larry Finger11.37%120.00%
Total73100.00%5100.00%


static ssize_t show_initstate(struct module_attribute *mattr, struct module_kobject *mk, char *buffer) { const char *state = "unknown"; switch (mk->mod->state) { case MODULE_STATE_LIVE: state = "live"; break; case MODULE_STATE_COMING: state = "coming"; break; case MODULE_STATE_GOING: state = "going"; break; default: BUG(); } return sprintf(buffer, "%s\n", state); }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers7194.67%266.67%
Rusty Russell45.33%133.33%
Total75100.00%3100.00%

static struct module_attribute modinfo_initstate = __ATTR(initstate, 0444, show_initstate, NULL);
static ssize_t store_uevent(struct module_attribute *mattr, struct module_kobject *mk, const char *buffer, size_t count) { kobject_synth_uevent(&mk->kobj, buffer, count); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers3589.74%150.00%
Peter Rajnoha410.26%150.00%
Total39100.00%2100.00%

struct module_attribute module_uevent = __ATTR(uevent, 0200, NULL, store_uevent);
static ssize_t show_coresize(struct module_attribute *mattr, struct module_kobject *mk, char *buffer) { return sprintf(buffer, "%u\n", mk->mod->core_layout.size); }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers3391.67%266.67%
Rusty Russell38.33%133.33%
Total36100.00%3100.00%

static struct module_attribute modinfo_coresize = __ATTR(coresize, 0444, show_coresize, NULL);
static ssize_t show_initsize(struct module_attribute *mattr, struct module_kobject *mk, char *buffer) { return sprintf(buffer, "%u\n", mk->mod->init_layout.size); }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers3391.67%266.67%
Rusty Russell38.33%133.33%
Total36100.00%3100.00%

static struct module_attribute modinfo_initsize = __ATTR(initsize, 0444, show_initsize, NULL);
static ssize_t show_taint(struct module_attribute *mattr, struct module_kobject *mk, char *buffer) { size_t l; l = module_flags_taint(mk->mod, buffer); buffer[l++] = '\n'; return l; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers45100.00%2100.00%
Total45100.00%2100.00%

static struct module_attribute modinfo_taint = __ATTR(taint, 0444, show_taint, NULL); static struct module_attribute *modinfo_attrs[] = { &module_uevent, &modinfo_version, &modinfo_srcversion, &modinfo_initstate, &modinfo_coresize, &modinfo_initsize, &modinfo_taint, #ifdef CONFIG_MODULE_UNLOAD &modinfo_refcnt, #endif NULL, }; static const char vermagic[] = VERMAGIC_STRING;
static int try_to_force_load(struct module *mod, const char *reason) { #ifdef CONFIG_MODULE_FORCE_LOAD if (!test_taint(TAINT_FORCED_MODULE)) pr_warn("%s: %s: kernel tainted.\n", mod->name, reason); add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_NOW_UNRELIABLE); return 0; #else return -ENOEXEC; #endif }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds4984.48%120.00%
Rusty Russell58.62%240.00%
Andi Kleen35.17%120.00%
Andrew Morton11.72%120.00%
Total58100.00%5100.00%

#ifdef CONFIG_MODVERSIONS
static u32 resolve_rel_crc(const s32 *crc) { return *(u32 *)((void *)crc + *crc); }

Contributors

PersonTokensPropCommitsCommitProp
Ard Biesheuvel1657.14%150.00%
Rusty Russell1242.86%150.00%
Total28100.00%2100.00%


static int check_version(const struct load_info *info, const char *symname, struct module *mod, const s32 *crc) { Elf_Shdr *sechdrs = info->sechdrs; unsigned int versindex = info->index.vers; unsigned int i, num_versions; struct modversion_info *versions; /* Exporting module didn't supply crcs? OK, we're already tainted. */ if (!crc) return 1; /* No versions at all? modprobe --force does this. */ if (versindex == 0) return try_to_force_load(mod, symname) == 0; versions = (void *) sechdrs[versindex].sh_addr; num_versions = sechdrs[versindex].sh_size / sizeof(struct modversion_info); for (i = 0; i < num_versions; i++) { u32 crcval; if (strcmp(versions[i].name, symname) != 0) continue; if (IS_ENABLED(CONFIG_MODULE_REL_CRCS)) crcval = resolve_rel_crc(crc); else crcval = *crc; if (versions[i].crc == crcval) return 1; pr_debug("Found checksum %X vs module %lX\n", crcval, versions[i].crc); goto bad_version; } /* Broken toolchain. Warn once, then let it go.. */ pr_warn_once("%s: no symbol version for %s\n", info->name, symname); return 1; bad_version: pr_warn("%s: disagrees about version of symbol %s\n", info->name, symname); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski12555.07%17.69%
Rusty Russell3214.10%430.77%
Ard Biesheuvel2711.89%17.69%
Kees Cook2410.57%215.38%
Linus Torvalds167.05%215.38%
Ionut Alexa10.44%17.69%
Richard Henderson10.44%17.69%
Jim Cromie10.44%17.69%
Total227100.00%13100.00%


static inline int check_modstruct_version(const struct load_info *info, struct module *mod) { const s32 *crc; /* * Since this should be found in kernel (which can't be removed), no * locking is necessary -- use preempt_disable() to placate lockdep. */ preempt_disable(); if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL, &crc, true, false)) { preempt_enable(); BUG(); } preempt_enable(); return check_version(info, VMLINUX_SYMBOL_STR(module_layout), mod, crc); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4561.64%333.33%
Peter Zijlstra1216.44%111.11%
Kees Cook56.85%111.11%
Kai Germaschewski56.85%111.11%
James Hogan45.48%111.11%
Tim Abbott11.37%111.11%
Ard Biesheuvel11.37%111.11%
Total73100.00%9100.00%

/* First part is kernel version, which we ignore if module has crcs. */
static inline int same_magic(const char *amagic, const char *bmagic, bool has_crcs) { if (has_crcs) { amagic += strcspn(amagic, " "); bmagic += strcspn(bmagic, " "); } return strcmp(amagic, bmagic) == 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell54100.00%2100.00%
Total54100.00%2100.00%

#else
static inline int check_version(const struct load_info *info, const char *symname, struct module *mod, const s32 *crc) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski1754.84%120.00%
Rusty Russell929.03%240.00%
Kees Cook412.90%120.00%
Ard Biesheuvel13.23%120.00%
Total31100.00%5100.00%


static inline int check_modstruct_version(const struct load_info *info, struct module *mod) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell1780.95%150.00%
Kees Cook419.05%150.00%
Total21100.00%2100.00%


static inline int same_magic(const char *amagic, const char *bmagic, bool has_crcs) { return strcmp(amagic, bmagic) == 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell30100.00%2100.00%
Total30100.00%2100.00%

#endif /* CONFIG_MODVERSIONS */ /* Resolve a symbol for this module. I.e. if we find one, record usage. */
static const struct kernel_symbol *resolve_symbol(struct module *mod, const struct load_info *info, const char *name, char ownername[]) { struct module *owner; const struct kernel_symbol *sym; const s32 *crc; int err; /* * The module_mutex should not be a heavily contended lock; * if we get the occasional sleep here, we'll go an extra iteration * in the wait_event_interruptible(), which is harmless. */ sched_annotate_sleep(); mutex_lock(&module_mutex); sym = find_symbol(name, &owner, &crc, !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true); if (!sym) goto unlock; if (!check_version(info, name, mod, crc)) { sym = ERR_PTR(-EINVAL); goto getname; } err = ref_module(mod, owner); if (err) { sym = ERR_PTR(err); goto getname; } getname: /* We must make copy under the lock if we failed to get ref. */ strncpy(ownername, module_name(owner), MODULE_NAME_LEN); unlock: mutex_unlock(&module_mutex); return sym; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell9454.65%637.50%
Greg Kroah-Hartman4023.26%16.25%
Tim Abbott126.98%16.25%
Kai Germaschewski63.49%212.50%
Florin Malita63.49%16.25%
Peter Zijlstra42.33%16.25%
Andi Kleen42.33%16.25%
Richard Henderson31.74%16.25%
Linus Torvalds21.16%16.25%
Ard Biesheuvel10.58%16.25%
Total172100.00%16100.00%


static const struct kernel_symbol * resolve_symbol_wait(struct module *mod, const struct load_info *info, const char *name) { const struct kernel_symbol *ksym; char owner[MODULE_NAME_LEN]; if (wait_event_interruptible_timeout(module_wq, !IS_ERR(ksym = resolve_symbol(mod, info, name, owner)) || PTR_ERR(ksym) != -EBUSY, 30 * HZ) <= 0) { pr_warn("%s: gave up waiting for init of module %s.\n", mod->name, owner); } return ksym; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell9098.90%266.67%
Andrew Morton11.10%133.33%
Total91100.00%3100.00%

/* * /sys/module/foo/sections stuff * J. Corbet <corbet@lwn.net> */ #ifdef CONFIG_SYSFS #ifdef CONFIG_KALLSYMS
static inline bool sect_empty(const Elf_Shdr *sect) { return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ben Hutchings28100.00%1100.00%
Total28100.00%1100.00%

struct module_sect_attr { struct module_attribute mattr; char *name; unsigned long address; }; struct module_sect_attrs { struct attribute_group grp; unsigned int nsections; struct module_sect_attr attrs[0]; };
static ssize_t module_sect_show(struct module_attribute *mattr, struct module_kobject *mk, char *buf) { struct module_sect_attr *sattr = container_of(mattr, struct module_sect_attr, mattr); return sprintf(buf, "0x%pK\n", (void *)sattr->address); }

Contributors

PersonTokensPropCommitsCommitProp
Jonathan Corbet4078.43%125.00%
Kees Cook59.80%125.00%
Tejun Heo47.84%125.00%
Kay Sievers23.92%125.00%
Total51100.00%4100.00%


static void free_sect_attrs(struct module_sect_attrs *sect_attrs) { unsigned int section; for (section = 0; section < sect_attrs->nsections; section++) kfree(sect_attrs->attrs[section].name); kfree(sect_attrs); }

Contributors

PersonTokensPropCommitsCommitProp
Ian S. Nelson4697.87%150.00%
Rusty Russell12.13%150.00%
Total47100.00%2100.00%


static void add_sect_attrs(struct module *mod, const struct load_info *info) { unsigned int nloaded = 0, i, size[2]; struct module_sect_attrs *sect_attrs; struct module_sect_attr *sattr; struct attribute **gattr; /* Count loaded sections and allocate structures */ for (i = 0; i < info->hdr->e_shnum; i++) if (!sect_empty(&info->sechdrs[i])) nloaded++; size[0] = ALIGN(sizeof(*sect_attrs) + nloaded * sizeof(sect_attrs->attrs[0]), sizeof(sect_attrs->grp.attrs[0])); size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]); sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL); if (sect_attrs == NULL) return; /* Setup section attributes. */ sect_attrs->grp.name = "sections"; sect_attrs->grp.attrs = (void *)sect_attrs + size[0]; sect_attrs->nsections = 0; sattr = &sect_attrs->attrs[0]; gattr = &sect_attrs->grp.attrs[0]; for (i = 0; i < info->hdr->e_shnum; i++) { Elf_Shdr *sec = &info->sechdrs[i]; if (sect_empty(sec)) continue; sattr->address = sec->sh_addr; sattr->name = kstrdup(info->secstrings + sec->sh_name, GFP_KERNEL); if (sattr->name == NULL) goto out; sect_attrs->nsections++; sysfs_attr_init(&sattr->mattr.attr); sattr->mattr.show = module_sect_show; sattr->mattr.store = NULL; sattr->mattr.attr.name = sattr->name; sattr->mattr.attr.mode = S_IRUSR; *(gattr++) = &(sattr++)->mattr.attr; } *gattr = NULL; if (sysfs_create_group(&mod->mkobj.kobj, &sect_attrs->grp)) goto out; mod->sect_attrs = sect_attrs; return; out: free_sect_attrs(sect_attrs); }

Contributors

PersonTokensPropCommitsCommitProp
Jonathan Corbet16240.20%112.50%
Tejun Heo14736.48%112.50%
Rusty Russell348.44%112.50%
Ian S. Nelson348.44%112.50%
Eric W. Biedermann102.48%112.50%
Helge Deller102.48%112.50%
Ben Hutchings51.24%112.50%
Linus Torvalds10.25%112.50%
Total403100.00%8100.00%


static void remove_sect_attrs(struct module *mod) { if (mod->sect_attrs) { sysfs_remove_group(&mod->mkobj.kobj, &mod->sect_attrs->grp); /* We are positive that no one is using any sect attrs * at this point. Deallocate immediately. */ free_sect_attrs(mod->sect_attrs); mod->sect_attrs = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
Jonathan Corbet3366.00%133.33%
Tejun Heo1632.00%133.33%
Ian S. Nelson12.00%133.33%
Total50100.00%3100.00%

/* * /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections. */ struct module_notes_attrs { struct kobject *dir; unsigned int notes; struct bin_attribute attrs[0]; };
static ssize_t module_notes_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { /* * The caller checked the pos and count against our size. */ memcpy(buf, bin_attr->private + pos, count); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath4389.58%150.00%
Chris Wright510.42%150.00%
Total48100.00%2100.00%


static void free_notes_attrs(struct module_notes_attrs *notes_attrs, unsigned int i) { if (notes_attrs->dir) { while (i-- > 0) sysfs_remove_bin_file(notes_attrs->dir, &notes_attrs->attrs[i]); kobject_put(notes_attrs->dir); } kfree(notes_attrs); }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath5698.25%150.00%
Alexey Dobriyan11.75%150.00%
Total57100.00%2100.00%


static void add_notes_attrs(struct module *mod, const struct load_info *info) { unsigned int notes, loaded, i; struct module_notes_attrs *notes_attrs; struct bin_attribute *nattr; /* failed to create section attributes, so can't create notes */ if (!mod->sect_attrs) return; /* Count notes sections and allocate structures. */ notes = 0; for (i = 0; i < info->hdr->e_shnum; i++) if (!sect_empty(&info->sechdrs[i]) && (info->sechdrs[i].sh_type == SHT_NOTE)) ++notes; if (notes == 0) return; notes_attrs = kzalloc(sizeof(*notes_attrs) + notes * sizeof(notes_attrs->attrs[0]), GFP_KERNEL); if (notes_attrs == NULL) return; notes_attrs->notes = notes; nattr = &notes_attrs->attrs[0]; for (loaded = i = 0; i < info->hdr->e_shnum; ++i) { if (sect_empty(&info->sechdrs[i])) continue; if (info->sechdrs[i].sh_type == SHT_NOTE) { sysfs_bin_attr_init(nattr); nattr->attr.name = mod->sect_attrs->attrs[loaded].name; nattr->attr.mode = S_IRUGO; nattr->size = info->sechdrs[i].sh_size; nattr->private = (void *) info->sechdrs[i].sh_addr; nattr->read = module_notes_read; ++nattr; } ++loaded; } notes_attrs->dir = kobject_create_and_add("notes", &mod->mkobj.kobj); if (!notes_attrs->dir) goto out; for (i = 0; i < notes; ++i) if (sysfs_create_bin_file(notes_attrs->dir, &notes_attrs->attrs[i])) goto out; mod->notes_attrs = notes_attrs; return; out: free_notes_attrs(notes_attrs, i); }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath29685.06%116.67%
Rusty Russell267.47%116.67%
Ingo Molnar92.59%116.67%
Ben Hutchings92.59%116.67%
Eric W. Biedermann51.44%116.67%
Greg Kroah-Hartman30.86%116.67%
Total348100.00%6100.00%


static void remove_notes_attrs(struct module *mod) { if (mod->notes_attrs) free_notes_attrs(mod->notes_attrs, mod->notes_attrs->notes); }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath30100.00%1100.00%
Total30100.00%1100.00%

#else
static inline void add_sect_attrs(struct module *mod, const struct load_info *info) { }

Contributors

PersonTokensPropCommitsCommitProp
Jonathan Corbet1376.47%150.00%
Rusty Russell423.53%150.00%
Total17100.00%2100.00%


static inline void remove_sect_attrs(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Jonathan Corbet11100.00%1100.00%
Total11100.00%1100.00%


static inline void add_notes_attrs(struct module *mod, const struct load_info *info) { }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath1376.47%150.00%
Rusty Russell423.53%150.00%
Total17100.00%2100.00%


static inline void remove_notes_attrs(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Roland McGrath11100.00%1100.00%
Total11100.00%1100.00%

#endif /* CONFIG_KALLSYMS */
static void del_usage_links(struct module *mod) { #ifdef CONFIG_MODULE_UNLOAD struct module_use *use; mutex_lock(&module_mutex); list_for_each_entry(use, &mod->target_list, target_list) sysfs_remove_link(use->target->holders_dir, mod->name); mutex_unlock(&module_mutex); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5296.30%266.67%
Corentin Labbe23.70%133.33%
Total54100.00%3100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5158.62%266.67%
Corentin Labbe3641.38%133.33%
Total87100.00%3100.00%


static int module_add_modinfo_attrs(struct module *mod) { struct module_attribute *attr; struct module_attribute *temp_attr; int error = 0; int i; mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) * (ARRAY_SIZE(modinfo_attrs) + 1)), GFP_KERNEL); if (!mod->modinfo_attrs) return -ENOMEM; temp_attr = mod->modinfo_attrs; for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { if (!attr->test || attr->test(mod)) { memcpy(temp_attr, attr, sizeof(*temp_attr)); sysfs_attr_init(&temp_attr->attr); error = sysfs_create_file(&mod->mkobj.kobj, &temp_attr->attr); ++temp_attr; } } return error; }

Contributors

PersonTokensPropCommitsCommitProp
Matt Domsch7950.97%125.00%
Greg Kroah-Hartman6743.23%125.00%
Eric W. Biedermann85.16%125.00%
Rusty Russell10.65%125.00%
Total155100.00%4100.00%


static void module_remove_modinfo_attrs(struct module *mod) { struct module_attribute *attr; int i; for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) { /* pick a field to test for end of list */ if (!attr->attr.name) break; sysfs_remove_file(&mod->mkobj.kobj, &attr->attr); if (attr->free) attr->free(mod); } kfree(mod->modinfo_attrs); }

Contributors

PersonTokensPropCommitsCommitProp
Matt Domsch6068.18%133.33%
Greg Kroah-Hartman2730.68%133.33%
Rusty Russell11.14%133.33%
Total88100.00%3100.00%


static void mod_kobject_put(struct module *mod) { DECLARE_COMPLETION_ONSTACK(c); mod->mkobj.kobj_completion = &c; kobject_put(&mod->mkobj.kobj); wait_for_completion(&c); }

Contributors

PersonTokensPropCommitsCommitProp
Li Zhong41100.00%1100.00%
Total41100.00%1100.00%


static int mod_sysfs_init(struct module *mod) { int err; struct kobject *kobj; if (!module_sysfs_initialized) { pr_err("%s: module sysfs not initialized\n", mod->name); err = -EINVAL; goto out; } kobj = kset_find_obj(module_kset, mod->name); if (kobj) { pr_err("%s: module is already loaded\n", mod->name); kobject_put(kobj); err = -EINVAL; goto out; } mod->mkobj.mod = mod; memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj)); mod->mkobj.kobj.kset = module_kset; err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL, "%s", mod->name); if (err) mod_kobject_put(mod); /* delay uevent until full sysfs population */ out: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman11169.81%640.00%
Ed Swierk2113.21%16.67%
Kay Sievers1710.69%320.00%
Rusty Russell42.52%213.33%
Tejun Heo31.89%16.67%
Andrew Morton21.26%16.67%
Li Zhong10.63%16.67%
Total159100.00%15100.00%


static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params) { int err; err = mod_sysfs_init(mod); if (err) goto out; mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj); if (!mod->holders_dir) { err = -ENOMEM; goto out_unreg; } err = module_param_sysfs_setup(mod, kparam, num_params); if (err) goto out_unreg_holders; err = module_add_modinfo_attrs(mod); if (err) goto out_unreg_param; err = add_usage_links(mod); if (err) goto out_unreg_modinfo_attrs; add_sect_attrs(mod, info); add_notes_attrs(mod, info); kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD); return 0; out_unreg_modinfo_attrs: module_remove_modinfo_attrs(mod); out_unreg_param: module_param_sysfs_remove(mod); out_unreg_holders: kobject_put(mod->holders_dir); out_unreg: mod_kobject_put(mod); out: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Kay Sievers7439.78%318.75%
Rusty Russell5328.49%531.25%
Greg Kroah-Hartman1910.22%318.75%
Corentin Labbe168.60%16.25%
Matt Domsch136.99%16.25%
Akinobu Mita73.76%16.25%
Linus Torvalds (pre-git)31.61%16.25%
Li Zhong10.54%16.25%
Total186100.00%16100.00%


static void mod_sysfs_fini(struct module *mod) { remove_notes_attrs(mod); remove_sect_attrs(mod); mod_kobject_put(mod); }

Contributors

PersonTokensPropCommitsCommitProp
Denis V. Lunev1557.69%133.33%
Rusty Russell1038.46%133.33%
Li Zhong13.85%133.33%
Total26100.00%3100.00%


static void init_param_lock(struct module *mod) { mutex_init(&mod->param_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell19100.00%1100.00%
Total19100.00%1100.00%

#else /* !CONFIG_SYSFS */
static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell29100.00%2100.00%
Total29100.00%2100.00%


static void mod_sysfs_fini(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Denis V. Lunev10100.00%1100.00%
Total10100.00%1100.00%


static void module_remove_modinfo_attrs(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell10100.00%1100.00%
Total10100.00%1100.00%


static void del_usage_links(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell10100.00%1100.00%
Total10100.00%1100.00%


static void init_param_lock(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell10100.00%1100.00%
Total10100.00%1100.00%

#endif /* CONFIG_SYSFS */
static void mod_sysfs_teardown(struct module *mod) { del_usage_links(mod); module_remove_modinfo_attrs(mod); module_param_sysfs_remove(mod); kobject_put(mod->mkobj.drivers_dir); kobject_put(mod->holders_dir); mod_sysfs_fini(mod); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman1940.43%220.00%
Kay Sievers1429.79%330.00%
Rusty Russell817.02%330.00%
Matt Domsch510.64%110.00%
Denis V. Lunev12.13%110.00%
Total47100.00%10100.00%

#ifdef CONFIG_STRICT_MODULE_RWX /* * LKM RO/NX protection: protect module's text/ro-data * from modification and any data from execution. * * General layout of module is: * [text] [read-only-data] [ro-after-init] [writable data] * text_size -----^ ^ ^ ^ * ro_size ------------------------| | | * ro_after_init_size -----------------------------| | * size -----------------------------------------------------------| * * These values are always page-aligned (as is base) */
static void frob_text(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1)); set_memory((unsigned long)layout->base, layout->text_size >> PAGE_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4457.14%233.33%
Matthieu Castet2329.87%116.67%
David Howells56.49%116.67%
Linus Torvalds (pre-git)33.90%116.67%
Li Zefan22.60%116.67%
Total77100.00%6100.00%


static void frob_rodata(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1)); set_memory((unsigned long)layout->base + layout->text_size, (layout->ro_size - layout->text_size) >> PAGE_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5855.77%440.00%
Matthieu Castet2625.00%110.00%
Josh Poimboeuf98.65%110.00%
Jason Baron32.88%110.00%
Greg Kroah-Hartman32.88%110.00%
Andrew Morton32.88%110.00%
Jan Glauber21.92%110.00%
Total104100.00%10100.00%


static void frob_ro_after_init(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->ro_after_init_size & (PAGE_SIZE-1)); set_memory((unsigned long)layout->base + layout->ro_size, (layout->ro_after_init_size - layout->ro_size) >> PAGE_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu104100.00%1100.00%
Total104100.00%1100.00%


static void frob_writable_data(const struct module_layout *layout, int (*set_memory)(unsigned long start, int num_pages)) { BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->ro_after_init_size & (PAGE_SIZE-1)); BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1)); set_memory((unsigned long)layout->base + layout->ro_after_init_size, (layout->size - layout->ro_after_init_size) >> PAGE_SHIFT); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell9187.50%228.57%
Josh Poimboeuf43.85%114.29%
Matthieu Castet32.88%114.29%
Jessica Yu32.88%114.29%
Linus Torvalds (pre-git)21.92%114.29%
Jan Glauber10.96%114.29%
Total104100.00%7100.00%

/* livepatching wants to disable read-only so it can frob module. */
void module_disable_ro(const struct module *mod) { if (!rodata_enabled) return; frob_text(&mod->core_layout, set_memory_rw); frob_rodata(&mod->core_layout, set_memory_rw); frob_ro_after_init(&mod->core_layout, set_memory_rw); frob_text(&mod->init_layout, set_memory_rw); frob_rodata(&mod->init_layout, set_memory_rw); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3552.24%333.33%
Jessica Yu1014.93%111.11%
Jan Glauber68.96%111.11%
AKASHI Takahiro68.96%111.11%
Matthieu Castet68.96%111.11%
Linus Torvalds (pre-git)34.48%111.11%
Josh Poimboeuf11.49%111.11%
Total67100.00%9100.00%


void module_enable_ro(const struct module *mod, bool after_init) { if (!rodata_enabled) return; frob_text(&mod->core_layout, set_memory_ro); frob_rodata(&mod->core_layout, set_memory_ro); frob_text(&mod->init_layout, set_memory_ro); frob_rodata(&mod->init_layout, set_memory_ro); if (after_init) frob_ro_after_init(&mod->core_layout, set_memory_ro); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2837.84%535.71%
Jessica Yu1722.97%17.14%
Jan Glauber810.81%214.29%
Matthieu Castet79.46%17.14%
AKASHI Takahiro68.11%17.14%
Linus Torvalds (pre-git)45.41%214.29%
Josh Poimboeuf34.05%17.14%
Andrew Morton11.35%17.14%
Total74100.00%14100.00%


static void module_enable_nx(const struct module *mod) { frob_rodata(&mod->core_layout, set_memory_nx); frob_ro_after_init(&mod->core_layout, set_memory_nx); frob_writable_data(&mod->core_layout, set_memory_nx); frob_rodata(&mod->init_layout, set_memory_nx); frob_writable_data(&mod->init_layout, set_memory_nx); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3048.39%225.00%
Jan Glauber1117.74%225.00%
Jessica Yu1016.13%112.50%
Matthieu Castet914.52%112.50%
Josh Poimboeuf11.61%112.50%
Andrew Morton11.61%112.50%
Total62100.00%8100.00%


static void module_disable_nx(const struct module *mod) { frob_rodata(&mod->core_layout, set_memory_x); frob_ro_after_init(&mod->core_layout, set_memory_x); frob_writable_data(&mod->core_layout, set_memory_x); frob_rodata(&mod->init_layout, set_memory_x); frob_writable_data(&mod->init_layout, set_memory_x); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3454.84%233.33%
Matthieu Castet1219.35%116.67%
Jessica Yu1016.13%116.67%
Jan Glauber46.45%116.67%
Josh Poimboeuf23.23%116.67%
Total62100.00%6100.00%

/* Iterate through all modules and set each module's text as RW */
void set_all_modules_text_rw(void) { struct module *mod; if (!rodata_enabled) return; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; frob_text(&mod->core_layout, set_memory_rw); frob_text(&mod->init_layout, set_memory_rw); } mutex_unlock(&module_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Matthieu Castet4768.12%116.67%
Rusty Russell1318.84%350.00%
AKASHI Takahiro68.70%116.67%
Daniel J Blueman34.35%116.67%
Total69100.00%6100.00%

/* Iterate through all modules and set each module's text as RO */
void set_all_modules_text_ro(void) { struct module *mod; if (!rodata_enabled) return; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { /* * Ignore going modules since it's possible that ro * protection has already been disabled, otherwise we'll * run into protection faults at module deallocation. */ if (mod->state == MODULE_STATE_UNFORMED || mod->state == MODULE_STATE_GOING) continue; frob_text(&mod->core_layout, set_memory_ro); frob_text(&mod->init_layout, set_memory_ro); } mutex_unlock(&module_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Matthieu Castet4761.84%114.29%
Rusty Russell1317.11%342.86%
Aaron Tomlin79.21%114.29%
AKASHI Takahiro67.89%114.29%
Daniel J Blueman33.95%114.29%
Total76100.00%7100.00%


static void disable_ro_nx(const struct module_layout *layout) { if (rodata_enabled) { frob_text(layout, set_memory_rw); frob_rodata(layout, set_memory_rw); frob_ro_after_init(layout, set_memory_rw); } frob_rodata(layout, set_memory_x); frob_ro_after_init(layout, set_memory_x); frob_writable_data(layout, set_memory_x); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2236.67%120.00%
Matthieu Castet1321.67%120.00%
Jessica Yu1220.00%120.00%
AKASHI Takahiro1016.67%120.00%
Josh Poimboeuf35.00%120.00%
Total60100.00%5100.00%

#else
static void disable_ro_nx(const struct module_layout *layout) { }

Contributors

PersonTokensPropCommitsCommitProp
Matthieu Castet654.55%133.33%
Rusty Russell436.36%133.33%
Josh Poimboeuf19.09%133.33%
Total11100.00%3100.00%


static void module_enable_nx(const struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Matthieu Castet763.64%133.33%
Jan Glauber218.18%133.33%
Rusty Russell218.18%133.33%
Total11100.00%3100.00%


static void module_disable_nx(const struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Jan Glauber545.45%133.33%
Matthieu Castet436.36%133.33%
Rusty Russell218.18%133.33%
Total11100.00%3100.00%

#endif #ifdef CONFIG_LIVEPATCH /* * Persist Elf information about a module. Copy the Elf header, * section header table, section string table, and symtab section * index from info to mod->klp_info. */
static int copy_module_elf(struct module *mod, struct load_info *info) { unsigned int size, symndx; int ret; size = sizeof(*mod->klp_info); mod->klp_info = kmalloc(size, GFP_KERNEL); if (mod->klp_info == NULL) return -ENOMEM; /* Elf header */ size = sizeof(mod->klp_info->hdr); memcpy(&mod->klp_info->hdr, info->hdr, size); /* Elf section header table */ size = sizeof(*info->sechdrs) * info->hdr->e_shnum; mod->klp_info->sechdrs = kmalloc(size, GFP_KERNEL); if (mod->klp_info->sechdrs == NULL) { ret = -ENOMEM; goto free_info; } memcpy(mod->klp_info->sechdrs, info->sechdrs, size); /* Elf section name string table */ size = info->sechdrs[info->hdr->e_shstrndx].sh_size; mod->klp_info->secstrings = kmalloc(size, GFP_KERNEL); if (mod->klp_info->secstrings == NULL) { ret = -ENOMEM; goto free_sechdrs; } memcpy(mod->klp_info->secstrings, info->secstrings, size); /* Elf symbol section index */ symndx = info->index.sym; mod->klp_info->symndx = symndx; /* * For livepatch modules, core_kallsyms.symtab is a complete * copy of the original symbol table. Adjust sh_addr to point * to core_kallsyms.symtab since the copy of the symtab in module * init memory is freed at the end of do_init_module(). */ mod->klp_info->sechdrs[symndx].sh_addr = \ (unsigned long) mod->core_kallsyms.symtab; return 0; free_sechdrs: kfree(mod->klp_info->sechdrs); free_info: kfree(mod->klp_info); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu281100.00%1100.00%
Total281100.00%1100.00%


static void free_module_elf(struct module *mod) { kfree(mod->klp_info->sechdrs); kfree(mod->klp_info->secstrings); kfree(mod->klp_info); }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu36100.00%1100.00%
Total36100.00%1100.00%

#else /* !CONFIG_LIVEPATCH */
static int copy_module_elf(struct module *mod, struct load_info *info) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu19100.00%1100.00%
Total19100.00%1100.00%


static void free_module_elf(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu10100.00%1100.00%
Total10100.00%1100.00%

#endif /* CONFIG_LIVEPATCH */
void __weak module_memfree(void *module_region) { vfree(module_region); }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Bonn1493.33%150.00%
Rusty Russell16.67%150.00%
Total15100.00%2100.00%


void __weak module_arch_cleanup(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Bonn10100.00%1100.00%
Total10100.00%1100.00%


void __weak module_arch_freeing_init(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell10100.00%1100.00%
Total10100.00%1100.00%

/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod) { trace_module_free(mod); mod_sysfs_teardown(mod); /* We leave it in list to prevent duplicate loads, but make sure * that noone uses it while it's being deconstructed. */ mutex_lock(&module_mutex); mod->state = MODULE_STATE_UNFORMED; mutex_unlock(&module_mutex); /* Remove dynamic debug info */ ddebug_remove_module(mod->name); /* Arch-specific cleanup. */ module_arch_cleanup(mod); /* Module unload stuff */ module_unload_free(mod); /* Free any allocated parameters. */ destroy_params(mod->kp, mod->num_kp); if (is_livepatch_module(mod)) free_module_elf(mod); /* Now we can delete it from the lists */ mutex_lock(&module_mutex); /* Unlink carefully: kallsyms could be walking list. */ list_del_rcu(&mod->list); mod_tree_remove(mod); /* Remove this module from bug list, this uses list_del_rcu */ module_bug_cleanup(mod); /* Wait for RCU-sched synchronizing before releasing mod->list and buglist. */ synchronize_sched(); mutex_unlock(&module_mutex); /* This may be empty, but that's OK */ disable_ro_nx(&mod->init_layout); module_arch_freeing_init(mod); module_memfree(mod->init_layout.base); kfree(mod->args); percpu_modfree(mod); /* Free lock-classes; relies on the preceding sync_rcu(). */ lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size); /* Finally, free the core (containing the module structure) */ disable_ro_nx(&mod->core_layout); module_memfree(mod->core_layout.base); #ifdef CONFIG_MPU update_protections(current->mm); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Matthieu Castet7738.12%15.26%
Rusty Russell5627.72%736.84%
Masami Hiramatsu136.44%210.53%
Prarit Bhargava125.94%15.26%
Jessica Yu125.94%15.26%
Bernd Schmidt125.94%15.26%
Ingo Molnar94.46%15.26%
Peter Zijlstra83.96%315.79%
Linus Torvalds (pre-git)20.99%15.26%
Andrew Morton10.50%15.26%
Total202100.00%19100.00%


void *__symbol_get(const char *symbol) { struct module *owner; const struct kernel_symbol *sym; preempt_disable(); sym = find_symbol(symbol, &owner, NULL, true, true); if (sym && strong_try_module_get(owner)) sym = NULL; preempt_enable(); return sym ? (void *)sym->value : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4057.14%654.55%
Tim Abbott1622.86%19.09%
Linus Torvalds (pre-git)1318.57%327.27%
Kai Germaschewski11.43%19.09%
Total70100.00%11100.00%

EXPORT_SYMBOL_GPL(__symbol_get); /* * Ensure that an exported symbol [global namespace] does not already exist * in the kernel or in some other module's exported symbol table. * * You must hold the module_mutex. */
static int verify_export_symbols(struct module *mod) { unsigned int i; struct module *owner; const struct kernel_symbol *s; struct { const struct kernel_symbol *sym; unsigned int num; } arr[] = { { mod->syms, mod->num_syms }, { mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_future_syms, mod->num_gpl_future_syms }, #ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->num_unused_syms }, { mod->unused_gpl_syms, mod->num_unused_gpl_syms }, #endif }; for (i = 0; i < ARRAY_SIZE(arr); i++) { for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { if (find_symbol(s->name, &owner, NULL, true, false)) { pr_err("%s: exports duplicate symbol %s" " (owned by %s)\n", mod->name, s->name, module_name(owner)); return -ENOEXEC; } } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell11558.67%350.00%
Ashutosh Naik7337.24%116.67%
Denys Vlasenko73.57%116.67%
Andrew Morton10.51%116.67%
Total196100.00%6100.00%

/* Change all symbols so that st_value encodes the pointer directly. */
static int simplify_symbols(struct module *mod, const struct load_info *info) { Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Sym *sym = (void *)symsec->sh_addr; unsigned long secbase; unsigned int i; int ret = 0; const struct kernel_symbol *ksym; for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { const char *name = info->strtab + sym[i].st_name; switch (sym[i].st_shndx) { case SHN_COMMON: /* Ignore common symbols */ if (!strncmp(name, "__gnu_lto", 9)) break; /* We compiled with -fno-common. These are not supposed to happen. */ pr_debug("Common symbol: %s\n", name); pr_warn("%s: please compile with -fno-common\n", mod->name); ret = -ENOEXEC; break; case SHN_ABS: /* Don't need to do anything */ pr_debug("Absolute symbol: 0x%08lx\n", (long)sym[i].st_value); break; case SHN_LIVEPATCH: /* Livepatch symbols are resolved by livepatch */ break; case SHN_UNDEF: ksym = resolve_symbol_wait(mod, info, name); /* Ok if resolved. */ if (ksym && !IS_ERR(ksym)) { sym[i].st_value = ksym->value; break; } /* Ok if weak. */ if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; pr_warn("%s: Unknown symbol %s (err %li)\n", mod->name, name, PTR_ERR(ksym)); ret = PTR_ERR(ksym) ?: -ENOENT; break; default: /* Divert to percpu allocation if a percpu var. */ if (sym[i].st_shndx == info->index.pcpu) secbase = (unsigned long)mod_percpu(mod); else secbase = info->sechdrs[sym[i].st_shndx].sh_addr; sym[i].st_value += secbase; break; } } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell23673.29%527.78%
Andrew Morton309.32%316.67%
Tim Abbott164.97%15.56%
Linus Torvalds (pre-git)154.66%422.22%
Joe Mario144.35%15.56%
Jessica Yu51.55%15.56%
Tejun Heo30.93%15.56%
Jim Cromie20.62%15.56%
Ionut Alexa10.31%15.56%
Total322100.00%18100.00%


static int apply_relocations(struct module *mod, const struct load_info *info) { unsigned int i; int err = 0; /* Now do relocations. */ for (i = 1; i < info->hdr->e_shnum; i++) { unsigned int infosec = info->sechdrs[i].sh_info; /* Not a valid relocation section? */ if (infosec >= info->hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) continue; /* Livepatch relocation sections are applied by livepatch */ if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) continue; if (info->sechdrs[i].sh_type == SHT_REL) err = apply_relocate(info->sechdrs, info->strtab, info->index.sym, i, mod); else if (info->sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(info->sechdrs, info->strtab, info->index.sym, i, mod); if (err < 0) break; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell17291.98%266.67%
Jessica Yu158.02%133.33%
Total187100.00%3100.00%

/* Additional bytes needed by arch in front of individual sections */
unsigned int __weak arch_mod_section_prepend(struct module *mod, unsigned int section) { /* default implementation just returns zero */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Helge Deller20100.00%1100.00%
Total20100.00%1100.00%

/* Update size with this section: return offset. */
static long get_offset(struct module *mod, unsigned int *size, Elf_Shdr *sechdr, unsigned int section) { long ret; *size += arch_mod_section_prepend(mod, section); ret = ALIGN(*size, sechdr->sh_addralign ?: 1); *size = ret + sechdr->sh_size; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3757.81%228.57%
Helge Deller1929.69%114.29%
Linus Torvalds (pre-git)710.94%342.86%
Denys Vlasenko11.56%114.29%
Total64100.00%7100.00%

/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld might -- code, read-only data, read-write data, small data. Tally sizes, and place the offsets into sh_entsize fields: high bit means it belongs in init. */
static void layout_sections(struct module *mod, struct load_info *info) { static unsigned long const masks[][2] = { /* NOTE: all executable code must be the first section * in this array; otherwise modify the text_size * finder in the two loops below */ { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, { ARCH_SHF_SMALL | SHF_ALLOC, 0 } }; unsigned int m, i; for (i = 0; i < info->hdr->e_shnum; i++) info->sechdrs[i].sh_entsize = ~0UL; pr_debug("Core section allocation order:\n"); for (m = 0; m < ARRAY_SIZE(masks); ++m) { for (i = 0; i < info->hdr->e_shnum; ++i) { Elf_Shdr *s = &info->sechdrs[i]; const char *sname = info->secstrings + s->sh_name; if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL || strstarts(sname, ".init")) continue; s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i); pr_debug("\t%s\n", sname); } switch (m) { case 0: /* executable */ mod->core_layout.size = debug_align(mod->core_layout.size); mod->core_layout.text_size = mod->core_layout.size; break; case 1: /* RO: text and ro-data */ mod->core_layout.size = debug_align(mod->core_layout.size); mod->core_layout.ro_size = mod->core_layout.size; break; case 2: /* RO after init */ mod->core_layout.size = debug_align(mod->core_layout.size); mod->core_layout.ro_after_init_size = mod->core_layout.size; break; case 4: /* whole core */ mod->core_layout.size = debug_align(mod->core_layout.size); break; } } pr_debug("Init section allocation order:\n"); for (m = 0; m < ARRAY_SIZE(masks); ++m) { for (i = 0; i < info->hdr->e_shnum; ++i) { Elf_Shdr *s = &info->sechdrs[i]; const char *sname = info->secstrings + s->sh_name; if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL || !strstarts(sname, ".init")) continue; s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i) | INIT_OFFSET_MASK); pr_debug("\t%s\n", sname); } switch (m) { case 0: /* executable */ mod->init_layout.size = debug_align(mod->init_layout.size); mod->init_layout.text_size = mod->init_layout.size; break; case 1: /* RO: text and ro-data */ mod->init_layout.size = debug_align(mod->init_layout.size); mod->init_layout.ro_size = mod->init_layout.size; break; case 2: /* * RO after init doesn't apply to init_layout (only * core_layout), so it just takes the value of ro_size. */ mod->init_layout.ro_after_init_size = mod->init_layout.ro_size; break; case 4: /* whole init */ mod->init_layout.size = debug_align(mod->init_layout.size); break; } } }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell42967.88%847.06%
Matthieu Castet10015.82%15.88%
Jessica Yu599.34%15.88%
Richard Henderson233.64%15.88%
Helge Deller81.27%15.88%
Linus Torvalds (pre-git)81.27%423.53%
Jim Cromie50.79%15.88%
Total632100.00%17100.00%


static void set_license(struct module *mod, const char *license) { if (!license) license = "unspecified"; if (!license_is_gpl_compatible(license)) { if (!test_taint(TAINT_PROPRIETARY_MODULE)) pr_warn("%s: module license '%s' taints kernel.\n", mod->name, license); add_taint_module(mod, TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); } }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4571.43%333.33%
Florin Malita812.70%111.11%
Randy Dunlap46.35%222.22%
Andi Kleen34.76%111.11%
Andrew Morton34.76%222.22%
Total63100.00%9100.00%

/* Parse tag=value strings from .modinfo section */
static char *next_string(char *string, unsigned long *secsize) { /* Skip non-zero chars */ while (string[0]) { string++; if ((*secsize)-- <= 1) return NULL; } /* Skip any zero padding. */ while (!string[0]) { string++; if ((*secsize)-- <= 1) return NULL; } return string; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell72100.00%1100.00%
Total72100.00%1100.00%


static char *get_modinfo(struct load_info *info, const char *tag) { char *p; unsigned int taglen = strlen(tag); Elf_Shdr *infosec = &info->sechdrs[info->index.info]; unsigned long size = infosec->sh_size; for (p = (char *)infosec->sh_addr; p; p = next_string(p, &size)) { if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') return p + taglen + 1; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell110100.00%2100.00%
Total110100.00%2100.00%


static void setup_modinfo(struct module *mod, struct load_info *info) { struct module_attribute *attr; int i; for (i = 0; (attr = modinfo_attrs[i]); i++) { if (attr->setup) attr->setup(mod, get_modinfo(info, attr->attr.name)); } }

Contributors

PersonTokensPropCommitsCommitProp
Matt Domsch6494.12%150.00%
Rusty Russell45.88%150.00%
Total68100.00%2100.00%


static void free_modinfo(struct module *mod) { struct module_attribute *attr; int i; for (i = 0; (attr = modinfo_attrs[i]); i++) { if (attr->free) attr->free(mod); } }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell52100.00%1100.00%
Total52100.00%1100.00%

#ifdef CONFIG_KALLSYMS /* lookup symbol in given range of kernel_symbols */
static const struct kernel_symbol *lookup_symbol(const char *name, const struct kernel_symbol *start, const struct kernel_symbol *stop) { return bsearch(name, start, stop - start, sizeof(struct kernel_symbol), cmp_name); }

Contributors

PersonTokensPropCommitsCommitProp
Américo Wang3269.57%150.00%
Alessio Igor Bogani1430.43%150.00%
Total46100.00%2100.00%


static int is_exported(const char *name, unsigned long value, const struct module *mod) { const struct kernel_symbol *ks; if (!mod) ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab); else ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); return ks != NULL && ks->value == value; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3243.24%125.00%
Tim Abbott2736.49%125.00%
Sam Ravnborg1418.92%125.00%
Alexey Dobriyan11.35%125.00%
Total74100.00%4100.00%

/* As per nm */
static char elf_type(const Elf_Sym *sym, const struct load_info *info) { const Elf_Shdr *sechdrs = info->sechdrs; if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) return 'v'; else return 'w'; } if (sym->st_shndx == SHN_UNDEF) return 'U'; if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) return 'a'; if (sym->st_shndx >= SHN_LORESERVE) return '?'; if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) return 't'; if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC && sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) return 'r'; else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) return 'g'; else return 'd'; } if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) return 's'; else return 'b'; } if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, ".debug")) { return 'n'; } return '?'; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell23595.92%375.00%
Miroslav Benes104.08%125.00%
Total245100.00%4100.00%


static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, unsigned int shnum, unsigned int pcpundx) { const Elf_Shdr *sec; if (src->st_shndx == SHN_UNDEF || src->st_shndx >= shnum || !src->st_name) return false; #ifdef CONFIG_KALLSYMS_ALL if (src->st_shndx == pcpundx) return true; #endif sec = sechdrs + src->st_shndx; if (!(sec->sh_flags & SHF_ALLOC) #ifndef CONFIG_KALLSYMS_ALL || !(sec->sh_flags & SHF_EXECINSTR) #endif || (sec->sh_entsize & INIT_OFFSET_MASK)) return false; return true; }

Contributors

PersonTokensPropCommitsCommitProp
Jan Beulich9482.46%150.00%
Miroslav Benes2017.54%150.00%
Total114100.00%2100.00%

/* * We only allocate and copy the strings needed by the parts of symtab * we keep. This is simple, but has the effect of making multiple * copies of duplicates. We could be more sophisticated, see * linux-kernel thread starting with * <73defb5e4bca04a6431392cc341112b1@localhost>. */
static void layout_symtab(struct module *mod, struct load_info *info) { Elf_Shdr *symsect = info->sechdrs + info->index.sym; Elf_Shdr *strsect = info->sechdrs + info->index.str; const Elf_Sym *src; unsigned int i, nsrc, ndst, strtab_size = 0; /* Put symbol section at end of init part of module. */ symsect->sh_flags |= SHF_ALLOC; symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect, info->index.sym) | INIT_OFFSET_MASK; pr_debug("\t%s\n", info->secstrings + symsect->sh_name); src = (void *)info->hdr + symsect->sh_offset; nsrc = symsect->sh_size / sizeof(*src); /* Compute total space required for the core symbols' strtab. */ for (ndst = i = 0; i < nsrc; i++) { if (i == 0 || is_livepatch_module(mod) || is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, info->index.pcpu)) { strtab_size += strlen(&info->strtab[src[i].st_name])+1; ndst++; } } /* Append room for core symbols at end of core part. */ info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); mod->core_layout.size += strtab_size; mod->core_layout.size = debug_align(mod->core_layout.size); /* Put string table section at end of init part of module. */ strsect->sh_flags |= SHF_ALLOC; strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, info->index.str) | INIT_OFFSET_MASK; pr_debug("\t%s\n", info->secstrings + strsect->sh_name); /* We'll tack temporary mod_kallsyms on the end. */ mod->init_layout.size = ALIGN(mod->init_layout.size, __alignof__(struct mod_kallsyms)); info->mod_kallsyms_init_off = mod->init_layout.size; mod->init_layout.size += sizeof(struct mod_kallsyms); mod->init_layout.size = debug_align(mod->init_layout.size); }

Contributors

PersonTokensPropCommitsCommitProp
Jan Beulich18548.68%216.67%
Rusty Russell14237.37%433.33%
Kevin Cernekee225.79%18.33%
Laura Abbott174.47%18.33%
Miroslav Benes61.58%18.33%
Jessica Yu51.32%18.33%
Satoru Takeuchi20.53%18.33%
Jim Cromie10.26%18.33%
Total380100.00%12100.00%

/* * We use the full symtab and strtab which layout_symtab arranged to * be appended to the init section. Later we switch to the cut-down * core-only ones. */
static void add_kallsyms(struct module *mod, const struct load_info *info) { unsigned int i, ndst; const Elf_Sym *src; Elf_Sym *dst; char *s; Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; /* Set up to point into init section. */ mod->kallsyms = mod->init_layout.base + info->mod_kallsyms_init_off; mod->kallsyms->symtab = (void *)symsec->sh_addr; mod->kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym); /* Make sure we get permanent strtab: don't use info->strtab. */ mod->kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr; /* Set types up while we still have access to sections. */ for (i = 0; i < mod->kallsyms->num_symtab; i++) mod->kallsyms->symtab[i].st_info = elf_type(&mod->kallsyms->symtab[i], info); /* Now populate the cut down core kallsyms for after init. */ mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs; mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs; src = mod->kallsyms->symtab; for (ndst = i = 0; i < mod->kallsyms->num_symtab; i++) { if (i == 0 || is_livepatch_module(mod) || is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum, info->index.pcpu)) { dst[ndst] = src[i]; dst[ndst++].st_name = s - mod->core_kallsyms.strtab; s += strlcpy(s, &mod->kallsyms->strtab[src[i].st_name], KSYM_NAME_LEN) + 1; } } mod->core_kallsyms.num_symtab = ndst; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell20061.35%861.54%
Jan Beulich8425.77%215.38%
Kevin Cernekee319.51%17.69%
Miroslav Benes61.84%17.69%
Jessica Yu51.53%17.69%
Total326100.00%13100.00%

#else
static inline void layout_symtab(struct module *mod, struct load_info *info) { }

Contributors

PersonTokensPropCommitsCommitProp
Jan Beulich1168.75%266.67%
Rusty Russell531.25%133.33%
Total16100.00%3100.00%


static void add_kallsyms(struct module *mod, const struct load_info *info) { }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton1275.00%133.33%
Rusty Russell318.75%133.33%
Michał Mirosław16.25%133.33%
Total16100.00%3100.00%

#endif /* CONFIG_KALLSYMS */
static void dynamic_debug_setup(struct module *mod, struct _ddebug *debug, unsigned int num) { if (!debug) return; #ifdef CONFIG_DYNAMIC_DEBUG if (ddebug_add_module(debug, num, mod->name)) pr_err("dynamic debug error adding module: %s\n", debug->modname); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Jason Baron3056.60%233.33%
Rusty Russell1528.30%233.33%
Zhou Chengming713.21%116.67%
Andrew Morton11.89%116.67%
Total53100.00%6100.00%


static void dynamic_debug_remove(struct module *mod, struct _ddebug *debug) { if (debug) ddebug_remove_module(mod->name); }

Contributors

PersonTokensPropCommitsCommitProp
Yehuda Sadeh Weinraub2074.07%150.00%
Zhou Chengming725.93%150.00%
Total27100.00%2100.00%


void * __weak module_alloc(unsigned long size) { return vmalloc_exec(size); }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Bonn17100.00%1100.00%
Total17100.00%1100.00%

#ifdef CONFIG_DEBUG_KMEMLEAK
static void kmemleak_load_module(const struct module *mod, const struct load_info *info) { unsigned int i; /* only scan the sections containing data */ kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL); for (i = 1; i < info->hdr->e_shnum; i++) { /* Scan all writable sections that's not executable */ if (!(info->sechdrs[i].sh_flags & SHF_ALLOC) || !(info->sechdrs[i].sh_flags & SHF_WRITE) || (info->sechdrs[i].sh_flags & SHF_EXECINSTR)) continue; kmemleak_scan_area((void *)info->sechdrs[i].sh_addr, info->sechdrs[i].sh_size, GFP_KERNEL); } }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas8264.57%240.00%
Steven Rostedt2318.11%120.00%
Rusty Russell2116.54%120.00%
Linus Torvalds10.79%120.00%
Total127100.00%5100.00%

#else
static inline void kmemleak_load_module(const struct module *mod, const struct load_info *info) { }

Contributors

PersonTokensPropCommitsCommitProp
Catalin Marinas1372.22%133.33%
Rusty Russell422.22%133.33%
Linus Torvalds15.56%133.33%
Total18100.00%3100.00%

#endif #ifdef CONFIG_MODULE_SIG
static int module_sig_check(struct load_info *info, int flags) { int err = -ENOKEY; const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; const void *mod = info->hdr; /* * Require flags == 0, as a module with version information * removed is no longer the module that was signed */ if (flags == 0 && info->len > markerlen && memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { /* We truncate the module to discard the signature */ info->len -= markerlen; err = mod_verify_sig(mod, &info->len); } if (!err) { info->sig_ok = true; return 0; } /* Not having a signature is only an error if we're strict. */ if (err == -ENOKEY && !sig_enforce) err = 0; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell8869.84%125.00%
Kees Cook1915.08%125.00%
David Howells118.73%125.00%
Ben Hutchings86.35%125.00%
Total126100.00%4100.00%

#else /* !CONFIG_MODULE_SIG */
static int module_sig_check(struct load_info *info, int flags) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell1482.35%150.00%
Ben Hutchings317.65%150.00%
Total17100.00%2100.00%

#endif /* !CONFIG_MODULE_SIG */ /* Sanity checks against invalid binaries, wrong arch, weird elf version. */
static int elf_header_check(struct load_info *info) { if (info->len < sizeof(*(info->hdr))) return -ENOEXEC; if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0 || info->hdr->e_type != ET_REL || !elf_check_arch(info->hdr) || info->hdr->e_shentsize != sizeof(Elf_Shdr)) return -ENOEXEC; if (info->hdr->e_shoff >= info->len || (info->hdr->e_shnum * sizeof(Elf_Shdr) > info->len - info->hdr->e_shoff)) return -ENOEXEC; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6150.41%337.50%
Kees Cook3125.62%112.50%
Linus Torvalds1915.70%225.00%
David Howells97.44%112.50%
Andrew Morton10.83%112.50%
Total121100.00%8100.00%

#define COPY_CHUNK_SIZE (16*PAGE_SIZE)
static int copy_chunked_from_user(void *dst, const void __user *usrc, unsigned long len) { do { unsigned long n = min(len, COPY_CHUNK_SIZE); if (copy_from_user(dst, usrc, n) != 0) return -EFAULT; cond_resched(); dst += n; usrc += n; len -= n; } while (len); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds74100.00%1100.00%
Total74100.00%1100.00%

#ifdef CONFIG_LIVEPATCH
static int check_modinfo_livepatch(struct module *mod, struct load_info *info) { if (get_modinfo(info, "livepatch")) { mod->klp = true; add_taint_module(mod, TAINT_LIVEPATCH, LOCKDEP_STILL_OK); pr_notice_once("%s: tainting kernel with TAINT_LIVEPATCH\n", mod->name); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu2648.15%133.33%
Josh Poimboeuf1935.19%133.33%
Joe Lawrence916.67%133.33%
Total54100.00%3100.00%

#else /* !CONFIG_LIVEPATCH */
static int check_modinfo_livepatch(struct module *mod, struct load_info *info) { if (get_modinfo(info, "livepatch")) { pr_err("%s: module is marked as livepatch module, but livepatch support is disabled", mod->name); return -ENOEXEC; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu4297.67%150.00%
Josh Poimboeuf12.33%150.00%
Total43100.00%2100.00%

#endif /* CONFIG_LIVEPATCH */
static void check_modinfo_retpoline(struct module *mod, struct load_info *info) { if (retpoline_module_ok(get_modinfo(info, "retpoline"))) return; pr_warn("%s: loading module not compiled with retpoline compiler.\n", mod->name); }

Contributors

PersonTokensPropCommitsCommitProp
Andi Kleen38100.00%1100.00%
Total38100.00%1100.00%

/* Sets info->hdr and info->len. */
static int copy_module_from_user(const void __user *umod, unsigned long len, struct load_info *info) { int err; info->len = len; if (info->len < sizeof(*(info->hdr))) return -ENOEXEC; err = security_kernel_read_file(NULL, READING_MODULE); if (err) return err; /* Suck in entire file: we'll want most of it. */ info->hdr = __vmalloc(info->len, GFP_KERNEL | __GFP_NOWARN, PAGE_KERNEL); if (!info->hdr) return -ENOMEM; if (copy_chunked_from_user(info->hdr, umod, info->len) != 0) { vfree(info->hdr); return -EFAULT; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook5946.09%228.57%
Rusty Russell5845.31%228.57%
Kirill A. Shutemov75.47%114.29%
Mimi Zohar32.34%114.29%
Linus Torvalds10.78%114.29%
Total128100.00%7100.00%


static void free_copy(struct load_info *info) { vfree(info->hdr); }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook1161.11%133.33%
Linus Torvalds633.33%133.33%
Rusty Russell15.56%133.33%
Total18100.00%3100.00%


static int rewrite_section_headers(struct load_info *info, int flags) { unsigned int i; /* This should always be true, but let's be sure. */ info->sechdrs[0].sh_addr = 0; for (i = 1; i < info->hdr->e_shnum; i++) { Elf_Shdr *shdr = &info->sechdrs[i]; if (shdr->sh_type != SHT_NOBITS && info->len < shdr->sh_offset + shdr->sh_size) { pr_err("Module len %lu truncated\n", info->len); return -ENOEXEC; } /* Mark all sections sh_addr with their address in the temporary image. */ shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset; #ifndef CONFIG_MODULE_UNLOAD /* Don't load .exit sections */ if (strstarts(info->secstrings+shdr->sh_name, ".exit")) shdr->sh_flags &= ~(unsigned long)SHF_ALLOC; #endif } /* Track but don't keep modinfo and version sections. */ if (flags & MODULE_INIT_IGNORE_MODVERSIONS) info->index.vers = 0; /* Pretend no __versions section! */ else info->index.vers = find_sec(info, "__versions"); 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; }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook19174.32%218.18%
Linus Torvalds2911.28%218.18%
Rusty Russell259.73%436.36%
Ben Hutchings83.11%19.09%
Greg Kroah-Hartman31.17%19.09%
Andrew Morton10.39%19.09%
Total257100.00%11100.00%

/* * Set up our basic convenience variables (pointers to section headers, * search for module section index etc), and do some basic section * verification. * * Return the temporary module pointer (we'll replace it with the final * one when we move the module sections around). */
static struct module *setup_load_info(struct load_info *info, int flags) { unsigned int i; int err; struct module *mod; /* Set up the convenience variables */ info->sechdrs = (void *)info->hdr + info->hdr->e_shoff; info->secstrings = (void *)info->hdr + info->sechdrs[info->hdr->e_shstrndx].sh_offset; err = rewrite_section_headers(info, flags); if (err) return ERR_PTR(err); /* Find internal symbols and strings. */ for (i = 1; i < info->hdr->e_shnum; i++) { if (info->sechdrs[i].sh_type == SHT_SYMTAB) { info->index.sym = i; info->index.str = info->sechdrs[i].sh_link; info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset; break; } } info->index.mod = find_sec(info, ".gnu.linkonce.this_module"); if (!info->index.mod) { pr_warn("%s: No module found in object\n", info->name ?: "(missing .modinfo name field)"); return ERR_PTR(-ENOEXEC); } /* This is temporary: point mod into copy of data. */ 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) { pr_warn("%s: module has no symbols (stripped?)\n", info->name); return ERR_PTR(-ENOEXEC); } info->index.pcpu = find_pcpusec(info); /* Check module struct version now, before we try to use module. */ if (!check_modstruct_version(info, mod)) return ERR_PTR(-ENOEXEC); return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook22070.06%222.22%
Linus Torvalds5216.56%222.22%
Rusty Russell3410.83%333.33%
Jason Baron61.91%111.11%
Andrew Morton20.64%111.11%
Total314100.00%9100.00%


static int check_modinfo(struct module *mod, struct load_info *info, int flags) { const char *modmagic = get_modinfo(info, "vermagic"); int err; if (flags & MODULE_INIT_IGNORE_VERMAGIC) modmagic = NULL; /* This is allowed: modprobe --force will invalidate it. */ if (!modmagic) { err = try_to_force_load(mod, "bad vermagic"); if (err) return err; } else if (!same_magic(modmagic, vermagic, info->index.vers)) { pr_err("%s: version magic '%s' should be '%s'\n", info->name, modmagic, vermagic); return -ENOEXEC; } if (!get_modinfo(info, "intree")) { if (!test_taint(TAINT_OOT_MODULE)) pr_warn("%s: loading out-of-tree module taints kernel.\n", mod->name); add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); } check_modinfo_retpoline(mod, info); if (get_modinfo(info, "staging")) { add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK); pr_warn("%s: module is from the staging directory, the quality " "is unknown, you have been warned.\n", mod->name); } err = check_modinfo_livepatch(mod, info); if (err) return err; /* Set up license info based on the info section */ set_license(mod, get_modinfo(info, "license")); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds8741.23%211.76%
Rusty Russell4521.33%529.41%
Steven Rostedt199.00%15.88%
Libor Pechacek199.00%15.88%
Jessica Yu157.11%15.88%
Ben Hutchings94.27%15.88%
Andi Kleen73.32%15.88%
Kees Cook41.90%211.76%
Andrew Morton41.90%15.88%
Greg Kroah-Hartman10.47%15.88%
Josh Poimboeuf10.47%15.88%
Total211100.00%17100.00%


static int find_module_sections(struct module *mod, struct load_info *info) { mod->kp = section_objs(info, "__param", sizeof(*mod->kp), &mod->num_kp); mod->syms = section_objs(info, "__ksymtab", sizeof(*mod->syms), &mod->num_syms); mod->crcs = section_addr(info, "__kcrctab"); mod->gpl_syms = section_objs(info, "__ksymtab_gpl", sizeof(*mod->gpl_syms), &mod->num_gpl_syms); mod->gpl_crcs = section_addr(info, "__kcrctab_gpl"); mod->gpl_future_syms = section_objs(info, "__ksymtab_gpl_future", sizeof(*mod->gpl_future_syms), &mod->num_gpl_future_syms); mod->gpl_future_crcs = section_addr(info, "__kcrctab_gpl_future"); #ifdef CONFIG_UNUSED_SYMBOLS mod->unused_syms = section_objs(info, "__ksymtab_unused", sizeof(*mod->unused_syms), &mod->num_unused_syms); mod->unused_crcs = section_addr(info, "__kcrctab_unused"); mod->unused_gpl_syms = section_objs(info, "__ksymtab_unused_gpl", sizeof(*mod->unused_gpl_syms), &mod->num_unused_gpl_syms); mod->unused_gpl_crcs = section_addr(info, "__kcrctab_unused_gpl"); #endif #ifdef CONFIG_CONSTRUCTORS mod->ctors = section_objs(info, ".ctors", sizeof(*mod->ctors), &mod->num_ctors); if (!mod->ctors) mod->ctors = section_objs(info, ".init_array", sizeof(*mod->ctors), &mod->num_ctors); else if (find_sec(info, ".init_array")) { /* * This shouldn't happen with same compiler and binutils * building all parts of the module. */ pr_warn("%s: has both .ctors and .init_array.\n", mod->name); return -EINVAL; } #endif #ifdef CONFIG_TRACEPOINTS mod->tracepoints_ptrs = section_objs(info, "__tracepoints_ptrs", sizeof(*mod->tracepoints_ptrs), &mod->num_tracepoints); #endif #ifdef HAVE_JUMP_LABEL mod->jump_entries = section_objs(info, "__jump_table", sizeof(*mod->jump_entries), &mod->num_jump_entries); #endif #ifdef CONFIG_EVENT_TRACING mod->trace_events = section_objs(info, "_ftrace_events", sizeof(*mod->trace_events), &mod->num_trace_events); mod->trace_evals = section_objs(info, "_ftrace_eval_map", sizeof(*mod->trace_evals), &mod->num_trace_evals); #endif #ifdef CONFIG_TRACING mod->trace_bprintk_fmt_start = section_objs(info, "__trace_printk_fmt", sizeof(*mod->trace_bprintk_fmt_start), &mod->num_trace_bprintk_fmt); #endif #ifdef CONFIG_FTRACE_MCOUNT_RECORD /* sechdrs[0].sh_size is always zero */ mod->ftrace_callsites = section_objs(info, "__mcount_loc", sizeof(*mod->ftrace_callsites), &mod->num_ftrace_callsites); #endif #ifdef CONFIG_FUNCTION_ERROR_INJECTION mod->ei_funcs = section_objs(info, "_error_injection_whitelist", sizeof(*mod->ei_funcs), &mod->num_ei_funcs); #endif mod->extable = section_objs(info, "__ex_table", sizeof(*mod->extable), &mod->num_exentries); if (section_addr(info, "__obsparm")) pr_warn("%s: Ignoring obsolete parameters\n", mod->name); info->debug = section_objs(info, "__verbose", sizeof(*info->debug), &info->num_debug); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds26345.82%312.00%
Rusty Russell13623.69%624.00%
Frantisek Hrbata6010.45%14.00%
Steven Rostedt406.97%28.00%
Jason Baron254.36%14.00%
Josef Bacik244.18%14.00%
Matthew Garrett81.39%14.00%
Masami Hiramatsu50.87%14.00%
Jeremy Linton40.70%28.00%
Mathieu Desnoyers30.52%14.00%
Linus Torvalds (pre-git)20.35%28.00%
Greg Kroah-Hartman10.17%14.00%
Andrew Morton10.17%14.00%
Davi Arnaut10.17%14.00%
Ionut Alexa10.17%14.00%
Total574100.00%25100.00%


static int move_module(struct module *mod, struct load_info *info) { int i; void *ptr; /* Do the allocs. */ ptr = module_alloc(mod->core_layout.size); /* * The pointer to this block is stored in the module structure * which is inside the block. Just mark it as not being a * leak. */ kmemleak_not_leak(ptr); if (!ptr) return -ENOMEM; memset(ptr, 0, mod->core_layout.size); mod->core_layout.base = ptr; if (mod->init_layout.size) { ptr = module_alloc(mod->init_layout.size); /* * The pointer to this block is stored in the module structure * which is inside the block. This block doesn't need to be * scanned as it contains data and code that will be freed * after the module is initialized. */ kmemleak_ignore(ptr); if (!ptr) { module_memfree(mod->core_layout.base); return -ENOMEM; } memset(ptr, 0, mod->init_layout.size); mod->init_layout.base = ptr; } else mod->init_layout.base = NULL; /* Transfer each section which specifies SHF_ALLOC */ pr_debug("final section addresses:\n"); for (i = 0; i < info->hdr->e_shnum; i++) { void *dest; Elf_Shdr *shdr = &info->sechdrs[i]; if (!(shdr->sh_flags & SHF_ALLOC)) continue; if (shdr->sh_entsize & INIT_OFFSET_MASK) dest = mod->init_layout.base + (shdr->sh_entsize & ~INIT_OFFSET_MASK); else dest = mod->core_layout.base + shdr->sh_entsize; if (shdr->sh_type != SHT_NOBITS) memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size); /* Update sh_addr to point to copy in image. */ shdr->sh_addr = (unsigned long)dest; pr_debug("\t0x%lx %s\n", (long)shdr->sh_addr, info->secstrings + shdr->sh_name); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds17659.06%421.05%
Rusty Russell10836.24%947.37%
Jim Cromie51.68%15.26%
Andrew Morton31.01%15.26%
Arjan van de Ven20.67%15.26%
Peter Zijlstra20.67%15.26%
Jonas Bonn10.34%15.26%
Kai Germaschewski10.34%15.26%
Total298100.00%19100.00%


static int check_module_license_and_versions(struct module *mod) { int prev_taint = test_taint(TAINT_PROPRIETARY_MODULE); /* * ndiswrapper is under GPL by itself, but loads proprietary modules. * Don't use add_taint_module(), as it would prevent ndiswrapper from * using GPL-only symbols it needs. */ if (strcmp(mod->name, "ndiswrapper") == 0) add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); /* driverloader was caught wrongly pretending to be under GPL */ if (strcmp(mod->name, "driverloader") == 0) add_taint_module(mod, TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); /* lve claims to be GPL but upstream won't provide source */ if (strcmp(mod->name, "lve") == 0) add_taint_module(mod, TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); if (!prev_taint && test_taint(TAINT_PROPRIETARY_MODULE)) pr_warn("%s: module license taints kernel.\n", mod->name); #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !mod->crcs) || (mod->num_gpl_syms && !mod->gpl_crcs) || (mod->num_gpl_future_syms && !mod->gpl_future_crcs) #ifdef CONFIG_UNUSED_SYMBOLS || (mod->num_unused_syms && !mod->unused_crcs) || (mod->num_unused_gpl_syms && !mod->unused_gpl_crcs) #endif ) { return try_to_force_load(mod, "no versions for exported symbols"); } #endif return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds11361.08%330.00%
Libor Pechacek2714.59%110.00%
Rusty Russell2412.97%330.00%
Matthew Garrett168.65%110.00%
Andrew Morton42.16%110.00%
Tejun Heo10.54%110.00%
Total185100.00%10100.00%


static void flush_module_icache(const struct module *mod) { mm_segment_t old_fs; /* flush the icache in correct context */ old_fs = get_fs(); set_fs(KERNEL_DS); /* * Flush the instruction cache, since we've played with text. * Do it before processing of module parameters, so the module * can provide parameter accessor functions of its own. */ if (mod->init_layout.base) flush_icache_range((unsigned long)mod->init_layout.base, (unsigned long)mod->init_layout.base + mod->init_layout.size); flush_icache_range((unsigned long)mod->core_layout.base, (unsigned long)mod->core_layout.base + mod->core_layout.size); set_fs(old_fs); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds4848.98%333.33%
Rusty Russell4747.96%444.44%
Andrew Morton22.04%111.11%
Arjan van de Ven11.02%111.11%
Total98100.00%9100.00%


int __weak module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Bonn26100.00%1100.00%
Total26100.00%1100.00%

/* module_blacklist is a comma-separated list of module names */ static char *module_blacklist;
static bool blacklisted(const char *module_name) { const char *p; size_t len; if (!module_blacklist) return false; for (p = module_blacklist; *p; p += len) { len = strcspn(p, ","); if (strlen(module_name) == len && !memcmp(module_name, p, len)) return true; if (p[len] == ',') len++; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Prarit Bhargava8798.86%150.00%
Luis R. Rodriguez11.14%150.00%
Total88100.00%2100.00%

core_param(module_blacklist, module_blacklist, charp, 0400);
static struct module *layout_and_allocate(struct load_info *info, int flags) { /* Module within temporary copy. */ struct module *mod; unsigned int ndx; int err; mod = setup_load_info(info, flags); if (IS_ERR(mod)) return mod; if (blacklisted(info->name)) return ERR_PTR(-EPERM); err = check_modinfo(mod, info, flags); if (err) return ERR_PTR(err); /* Allow arches to frob section contents and sizes. */ err = module_frob_arch_sections(info->hdr, info->sechdrs, info->secstrings, mod); if (err < 0) return ERR_PTR(err); /* We will do a special allocation for per-cpu sections later. */ info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC; /* * Mark ro_after_init section with SHF_RO_AFTER_INIT so that * layout_sections() can put it in the right place. * Note: ro_after_init sections also have SHF_{WRITE,ALLOC} set. */ ndx = find_sec(info, ".data..ro_after_init"); if (ndx) info->sechdrs[ndx].sh_flags |= SHF_RO_AFTER_INIT; /* Determine total sizes, and put offsets in sh_entsize. For now this is done generically; there doesn't appear to be any special cases for the architectures. */ layout_sections(mod, info); layout_symtab(mod, info); /* Allocate and move to the final place */ err = move_module(mod, info); if (err) return ERR_PTR(err); /* Module has been copied to its final place now: return it. */ mod = (void *)info->sechdrs[info->index.mod].sh_addr; kmemleak_load_module(mod, info); return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell14262.28%844.44%
Linus Torvalds3414.91%316.67%
Jessica Yu2912.72%15.56%
Prarit Bhargava156.58%15.56%
Arjan van de Ven31.32%15.56%
Matt Domsch20.88%15.56%
Kees Cook10.44%15.56%
Randy Dunlap10.44%15.56%
Kai Germaschewski10.44%15.56%
Total228100.00%18100.00%

/* mod is no longer valid after this! */
static void module_deallocate(struct module *mod, struct load_info *info) { percpu_modfree(mod); module_arch_freeing_init(mod); module_memfree(mod->init_layout.base); module_memfree(mod->core_layout.base); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell44100.00%6100.00%
Total44100.00%6100.00%


int __weak module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *me) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jonas Bonn1770.83%133.33%
Rusty Russell729.17%266.67%
Total24100.00%3100.00%


static int post_relocation(struct module *mod, const struct load_info *info) { /* Sort exception table now relocations are done. */ sort_extable(mod->extable, mod->extable + mod->num_exentries); /* Copy relocated percpu area over. */ percpu_modcopy(mod, (void *)info->sechdrs[info->index.pcpu].sh_addr, info->sechdrs[info->index.pcpu].sh_size); /* Setup kallsyms-specific fields. */ add_kallsyms(mod, info); /* Arch-specific module finalizing. */ return module_finalize(info->hdr, info->sechdrs, mod); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell8693.48%480.00%
Linus Torvalds66.52%120.00%
Total92100.00%5100.00%

/* Is this module of this name done loading? No locks held. */
static bool finished_loading(const char *name) { struct module *mod; bool ret; /* * The module_mutex should not be a heavily contended lock; * if we get the occasional sleep here, we'll go an extra iteration * in the wait_event_interruptible(), which is harmless. */ sched_annotate_sleep(); mutex_lock(&module_mutex); mod = find_module_all(name, strlen(name), true); ret = !mod || mod->state == MODULE_STATE_LIVE || mod->state == MODULE_STATE_GOING; mutex_unlock(&module_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3753.62%650.00%
Kees Cook1318.84%18.33%
Linus Torvalds710.14%216.67%
Mathias Krause57.25%18.33%
Peter Zijlstra45.80%18.33%
Peter Oberparleiter34.35%18.33%
Total69100.00%12100.00%

/* Call module constructors. */
static void do_mod_ctors(struct module *mod) { #ifdef CONFIG_CONSTRUCTORS unsigned long i; for (i = 0; i < mod->num_ctors; i++) mod->ctors[i](); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook4397.73%150.00%
Linus Torvalds12.27%150.00%
Total44100.00%2100.00%

/* For freeing module_init on success, in case kallsyms traversing */ struct mod_initfree { struct rcu_head rcu; void *module_init; };
static void do_free_init(struct rcu_head *head) { struct mod_initfree *m = container_of(head, struct mod_initfree, rcu); module_memfree(m->module_init); kfree(m); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell38100.00%1100.00%
Total38100.00%1100.00%

/* * This is where the real work happens. * * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb * helper command 'lx-symbols'. */
static noinline int do_init_module(struct module *mod) { int ret = 0; struct mod_initfree *freeinit; freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL); if (!freeinit) { ret = -ENOMEM; goto fail; } freeinit->module_init = mod->init_layout.base; /* * We want to find out whether @mod uses async during init. Clear * PF_USED_ASYNC. async_schedule*() will set it. */ current->flags &= ~PF_USED_ASYNC; do_mod_ctors(mod); /* Start the module */ if (mod->init != NULL) ret = do_one_initcall(mod->init); if (ret < 0) { goto fail_free_freeinit; } if (ret > 0) { pr_warn("%s: '%s'->init suspiciously returned %d, it should " "follow 0/-E convention\n" "%s: loading module anyway...\n", __func__, mod->name, ret, __func__); dump_stack(); } /* Now it's a first class citizen! */ mod->state = MODULE_STATE_LIVE; blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_LIVE, mod); /* * We need to finish all async code before the module init sequence * is done. This has potential to deadlock. For example, a newly * detected block device can trigger request_module() of the * default iosched from async probing task. Once userland helper * reaches here, async_synchronize_full() will wait on the async * task waiting on request_module() and deadlock. * * This deadlock is avoided by perfomring async_synchronize_full() * iff module init queued any async jobs. This isn't a full * solution as it will deadlock the same if module loading from * async jobs nests more than once; however, due to the various * constraints, this hack seems to be the best option for now. * Please refer to the following thread for details. * * http://thread.gmane.org/gmane.linux.kernel/1420814 */ if (!mod->async_probe_requested && (current->flags & PF_USED_ASYNC)) async_synchronize_full(); ftrace_free_mem(mod, mod->init_layout.base, mod->init_layout.base + mod->init_layout.size); mutex_lock(&module_mutex); /* Drop initial reference. */ module_put(mod); trim_init_extable(mod); #ifdef CONFIG_KALLSYMS /* Switch to core kallsyms now init is done: kallsyms may be walking! */ rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms); #endif module_enable_ro(mod, true); mod_tree_remove_init(mod); disable_ro_nx(&mod->init_layout); module_arch_freeing_init(mod); mod->init_layout.base = NULL; mod->init_layout.size = 0; mod->init_layout.ro_size = 0; mod->init_layout.ro_after_init_size = 0; mod->init_layout.text_size = 0; /* * We want to free module_init, but be aware that kallsyms may be * walking this with preempt disabled. In all the failure paths, we * call synchronize_sched(), but we don't want to slow down the success * path, so use actual RCU here. */ call_rcu_sched(&freeinit->rcu, do_free_init); mutex_unlock(&module_mutex); wake_up_all(&module_wq); return 0; fail_free_freeinit: kfree(freeinit); fail: /* Try to protect us from buggy refcounters. */ mod->state = MODULE_STATE_GOING; synchronize_sched(); module_put(mod); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); klp_module_going(mod); ftrace_release_mod(mod); free_module(mod); wake_up_all(&module_wq); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell18850.40%1644.44%
Kees Cook7520.11%12.78%
Steven Rostedt266.97%38.33%
Jessica Yu256.70%38.33%
Linus Torvalds215.63%411.11%
Tejun Heo174.56%12.78%
Peter Zijlstra71.88%25.56%
Luis R. Rodriguez71.88%12.78%
Andrew Morton30.80%12.78%
Randy Dunlap20.54%25.56%
Jan Kiszka10.27%12.78%
Catalin Marinas10.27%12.78%
Total373100.00%36100.00%


static int may_init_module(void) { if (!capable(CAP_SYS_MODULE) || modules_disabled) return -EPERM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Kees Cook2184.00%150.00%
Rusty Russell416.00%150.00%
Total25100.00%2100.00%

/* * We try to place it in the list now to make sure it's unique before * we dedicate too many resources. In particular, temporary percpu * memory exhaustion. */
static int add_unformed_module(struct module *mod) { int err; struct module *old; mod->state = MODULE_STATE_UNFORMED; again: mutex_lock(&module_mutex); old = find_module_all(mod->name, strlen(mod->name), true); if (old != NULL) { if (old->state == MODULE_STATE_COMING || old->state == MODULE_STATE_UNFORMED) { /* Wait in case it fails to load. */ mutex_unlock(&module_mutex); err = wait_event_interruptible(module_wq, finished_loading(mod->name)); if (err) goto out_unlocked; goto again; } err = -EEXIST; goto out; } mod_update_bounds(mod); list_add_rcu(&mod->list, &modules); mod_tree_insert(mod); err = 0; out: mutex_unlock(&module_mutex); out_unlocked: return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell11173.03%228.57%
Peter Zijlstra1811.84%342.86%
Kees Cook127.89%114.29%
Mathias Krause117.24%114.29%
Total152100.00%7100.00%


static int complete_formation(struct module *mod, struct load_info *info) { int err; mutex_lock(&module_mutex); /* Find duplicate symbols (must be called under lock). */ err = verify_export_symbols(mod); if (err < 0) goto out; /* This relies on module_mutex for list integrity. */ module_bug_finalize(info->hdr, info->sechdrs, mod); module_enable_ro(mod, false); module_enable_nx(mod); /* Mark state as coming so strong_try_module_get() ignores us, * but kallsyms etc. can see us. */ mod->state = MODULE_STATE_COMING; mutex_unlock(&module_mutex); return 0; out: mutex_unlock(&module_mutex); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell7882.11%360.00%
Jessica Yu1717.89%240.00%
Total95100.00%5100.00%


static int prepare_coming_module(struct module *mod) { int err; ftrace_module_enable(mod); err = klp_module_coming(mod); if (err) return err; blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jessica Yu3167.39%360.00%
Rusty Russell1532.61%240.00%
Total46100.00%5100.00%


static int unknown_module_param_cb(char *param, char *val, const char *modname, void *arg) { struct module *mod = arg; int ret; if (strcmp(param, "async_probe") == 0) { mod->async_probe_requested = true; return 0; } /* Check for magic 'dyndbg' arg */ ret = ddebug_dyndbg_module_param_cb(param, val, modname); if (ret != 0) pr_warn("%s: unknown parameter '%s' ignored\n", modname, param); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4654.12%125.00%
Luis R. Rodriguez3844.71%250.00%
Andrew Morton11.18%125.00%
Total85100.00%4100.00%

/* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */
static int load_module(struct load_info *info, const char __user *uargs, int flags) { struct module *mod; long err; char *after_dashes; err = module_sig_check(info, flags); if (err) goto free_copy; err = elf_header_check(info); if (err) goto free_copy; /* Figure out module layout, and allocate all the memory. */ mod = layout_and_allocate(info, flags); if (IS_ERR(mod)) { err = PTR_ERR(mod); goto free_copy; } audit_log_kern_module(mod->name); /* Reserve our place in the list. */ err = add_unformed_module(mod); if (err) goto free_module; #ifdef CONFIG_MODULE_SIG mod->sig_ok = info->sig_ok; if (!mod->sig_ok) { pr_notice_once("%s: module verification failed: signature " "and/or required key missing - tainting " "kernel\n", mod->name); add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK); } #endif /* To avoid stressing percpu allocator, do this once we're unique. */ err = percpu_modalloc(mod, info); if (err) goto unlink_mod; /* Now module is in final location, initialize linked lists, etc. */ err = module_unload_init(mod); if (err) goto unlink_mod; init_param_lock(mod); /* Now we've got everything in the final locations, we can * find optional sections. */ err = find_module_sections(mod, info); if (err) goto free_unload; err = check_module_license_and_versions(mod); if (err) goto free_unload; /* Set up MODINFO_ATTR fields */ setup_modinfo(mod, info); /* Fix up syms, so that st_value is a pointer to location. */ err = simplify_symbols(mod, info); if (err < 0) goto free_modinfo; err = apply_relocations(mod, info); if (err < 0) goto free_modinfo; err = post_relocation(mod, info); if (err < 0) goto free_modinfo; flush_module_icache(mod); /* Now copy in args */ mod->args = strndup_user(uargs, ~0UL >> 1); if (IS_ERR(mod->args)) { err = PTR_ERR(mod->args); goto free_arch_cleanup; } dynamic_debug_setup(mod, info->debug, info->num_debug); /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */ ftrace_module_init(mod); /* Finally it's fully formed, ready to start executing. */ err = complete_formation(mod, info); if (err) goto ddebug_cleanup; err = prepare_coming_module(mod); if (err) goto bug_cleanup; /* Module is ready to execute: parsing args may do that. */ after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, -32768, 32767, mod, unknown_module_param_cb); if (IS_ERR(after_dashes)) { err = PTR_ERR(after_dashes); goto coming_cleanup; } else if (after_dashes) { pr_warn("%s: parameters '%s' after `--' ignored\n", mod->name, after_dashes); } /* Link in to sysfs. */ err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp); if (err < 0) goto coming_cleanup; if (is_livepatch_module(mod)) { err = copy_module_elf(mod, info); if (err < 0) goto sysfs_cleanup; } /* Get rid of temporary copy. */ free_copy(info); /* Done! */ trace_module_load(mod); return do_init_module(mod); sysfs_cleanup: mod_sysfs_teardown(mod); coming_cleanup: mod->state = MODULE_STATE_GOING; destroy_params(mod->kp, mod->num_kp); blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); klp_module_going(mod); bug_cleanup: /* module_bug_cleanup needs module_mutex protection */ mutex_lock(&module_mutex); module_bug_cleanup(mod); mutex_unlock(&module_mutex); /* we can't deallocate the module until we clear memory protection */ module_disable_ro(mod); module_disable_nx(mod); ddebug_cleanup: ftrace_release_mod(mod); dynamic_debug_remove(mod, info->debug); synchronize_sched(); kfree(mod->args); free_arch_cleanup: module_arch_cleanup(mod); free_modinfo: free_modinfo(mod); free_unload: module_unload_free(mod); unlink_mod: mutex_lock(&module_mutex); /* Unlink carefully: kallsyms could be walking list. */ list_del_rcu(&mod->list); mod_tree_remove(mod); wake_up_all(&module_wq); /* Wait for RCU-sched synchronizing before releasing mod->list. */ synchronize_sched(); mutex_unlock(&module_mutex); free_module: /* Free lock-classes; relies on the preceding sync_rcu() */ lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size); module_deallocate(mod, info); free_copy: free_copy(info); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell30643.04%2838.36%
Kees Cook15621.94%11.37%
Jessica Yu679.42%34.11%
Steven Rostedt294.08%22.74%
Peter Zijlstra172.39%34.11%
Luis R. Rodriguez131.83%34.11%
Linus Torvalds121.69%45.48%
Andrew Morton111.55%22.74%
Linus Torvalds (pre-git)101.41%34.11%
Andrew Lutomirski91.27%11.37%
Frantisek Hrbata91.27%11.37%
Richard Guy Briggs81.13%11.37%
Greg Kroah-Hartman70.98%11.37%
Peter Oberparleiter60.84%22.74%
Aaron Tomlin60.84%11.37%
Namit Gupta50.70%11.37%
Dan Streetman40.56%11.37%
Jan Beulich40.56%22.74%
Li Zefan40.56%11.37%
Alexey Dobriyan40.56%11.37%
Zhou Chengming40.56%11.37%
Matthieu Castet30.42%11.37%
Brian Gerst30.42%11.37%
Yehuda Sadeh Weinraub30.42%11.37%
Pawel Moll20.28%11.37%
Ben Hutchings20.28%11.37%
Masami Hiramatsu20.28%11.37%
Arjan van de Ven20.28%11.37%
Marcel Holtmann10.14%11.37%
Mathieu Desnoyers10.14%11.37%
Paul E. McKenney10.14%11.37%
Total711100.00%73100.00%

SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs) { int err; struct load_info info = { }; err = may_init_module(); if (err) return err; pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); err = copy_module_from_user(umod, len, &info); if (err) return err; return load_module(&info, uargs, 0); } SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { struct load_info info = { }; loff_t size; void *hdr; int err; err = may_init_module(); if (err) return err; pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS |MODULE_INIT_IGNORE_VERMAGIC)) return -EINVAL; err = kernel_read_file_from_fd(fd, &hdr, &size, INT_MAX, READING_MODULE); if (err) return err; info.hdr = hdr; info.len = size; return load_module(&info, uargs, flags); }
static inline int within(unsigned long addr, void *start, unsigned long size) { return ((void *)addr >= start && (void *)addr < start + size); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3997.50%266.67%
Peter Oberparleiter12.50%133.33%
Total40100.00%3100.00%

#ifdef CONFIG_KALLSYMS /* * This ignores the intensely annoying "mapping symbols" found * in ARM ELF files: $a, $t and $d. */
static inline int is_arm_mapping_symbol(const char *str) { if (str[0] == '.' && str[1] == 'L') return true; return str[0] == '$' && strchr("axtd", str[1]) && (str[2] == '\0' || str[2] == '.'); }

Contributors

PersonTokensPropCommitsCommitProp
Russell King6498.46%266.67%
Kyle McMartin11.54%133.33%
Total65100.00%3100.00%


static const char *symname(struct mod_kallsyms *kallsyms, unsigned int symnum) { return kallsyms->strtab + kallsyms->symtab[symnum].st_name; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell31100.00%2100.00%
Total31100.00%2100.00%


static const char *get_ksymbol(struct module *mod, unsigned long addr, unsigned long *size, unsigned long *offset) { unsigned int i, best = 0; unsigned long nextval; struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); /* At worse, next value is at end of module */ if (within_module_init(addr, mod)) nextval = (unsigned long)mod->init_layout.base+mod->init_layout.text_size; else nextval = (unsigned long)mod->core_layout.base+mod->core_layout.text_size; /* Scan for closest preceding symbol, and next symbol. (ELF starts real symbols at 1). */ for (i = 1; i < kallsyms->num_symtab; i++) { if (kallsyms->symtab[i].st_shndx == SHN_UNDEF) continue; /* We ignore unnamed symbols: they're uninformative * and inserted at a whim. */ if (*symname(kallsyms, i) == '\0' || is_arm_mapping_symbol(symname(kallsyms, i))) continue; if (kallsyms->symtab[i].st_value <= addr && kallsyms->symtab[i].st_value > kallsyms->symtab[best].st_value) best = i; if (kallsyms->symtab[i].st_value > addr && kallsyms->symtab[i].st_value < nextval) nextval = kallsyms->symtab[i].st_value; } if (!best) return NULL; if (size) *size = nextval - kallsyms->symtab[best].st_value; if (offset) *offset = addr - kallsyms->symtab[best].st_value; return symname(kallsyms, best); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell26596.01%763.64%
Alexey Dobriyan82.90%19.09%
Lucas De Marchi10.36%19.09%
Kai Germaschewski10.36%19.09%
Masami Hiramatsu10.36%19.09%
Total276100.00%11100.00%


void * __weak dereference_module_function_descriptor(struct module *mod, void *ptr) { return ptr; }

Contributors

PersonTokensPropCommitsCommitProp
Sergey Senozhatsky19100.00%1100.00%
Total19100.00%1100.00%

/* For kallsyms to ask for address resolution. NULL means not found. Careful * not to lock to avoid deadlock on oopses, simply disable preemption. */
const char *module_address_lookup(unsigned long addr, unsigned long *size, unsigned long *offset, char **modname, char *namebuf) { const char *ret = NULL; struct module *mod; preempt_disable(); mod = __module_address(addr); if (mod) { if (modname) *modname = mod->name; ret = get_ksymbol(mod, addr, size, offset); } /* Make a copy in here where it's safe */ if (ret) { strncpy(namebuf, ret, KSYM_NAME_LEN - 1); ret = namebuf; } preempt_enable(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell9586.36%562.50%
Peter Zijlstra109.09%112.50%
Franck Bui-Huu43.64%112.50%
Andrew Morton10.91%112.50%
Total110100.00%8100.00%


int lookup_module_symbol_name(unsigned long addr, char *symname) { struct module *mod; preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; if (within_module(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, NULL, NULL); if (!sym) goto out; strlcpy(symname, sym, KSYM_NAME_LEN); preempt_enable(); return 0; } } out: preempt_enable(); return -ERANGE; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan8483.17%120.00%
Rusty Russell1514.85%240.00%
Andi Kleen10.99%120.00%
Petr Mladek10.99%120.00%
Total101100.00%5100.00%


int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name) { struct module *mod; preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; if (within_module(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, size, offset); if (!sym) goto out; if (modname) strlcpy(modname, mod->name, MODULE_NAME_LEN); if (name) strlcpy(name, sym, KSYM_NAME_LEN); preempt_enable(); return 0; } } out: preempt_enable(); return -ERANGE; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan11787.31%120.00%
Rusty Russell1511.19%240.00%
Andi Kleen10.75%120.00%
Petr Mladek10.75%120.00%
Total134100.00%5100.00%


int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *name, char *module_name, int *exported) { struct module *mod; preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { struct mod_kallsyms *kallsyms; if (mod->state == MODULE_STATE_UNFORMED) continue; kallsyms = rcu_dereference_sched(mod->kallsyms); if (symnum < kallsyms->num_symtab) { *value = kallsyms->symtab[symnum].st_value; *type = kallsyms->symtab[symnum].st_info; strlcpy(name, symname(kallsyms, symnum), KSYM_NAME_LEN); strlcpy(module_name, mod->name, MODULE_NAME_LEN); *exported = is_exported(name, *value, mod); preempt_enable(); return 0; } symnum -= kallsyms->num_symtab; } preempt_enable(); return -ERANGE; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell12074.07%654.55%
Alexey Dobriyan3420.99%218.18%
Andreas Gruenbacher42.47%19.09%
Tim Abbott31.85%19.09%
Andi Kleen10.62%19.09%
Total162100.00%11100.00%


static unsigned long mod_find_symname(struct module *mod, const char *name) { unsigned int i; struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); for (i = 0; i < kallsyms->num_symtab; i++) if (strcmp(name, symname(kallsyms, i)) == 0 && kallsyms->symtab[i].st_info != 'U') return kallsyms->symtab[i].st_value; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton5865.91%125.00%
Rusty Russell2022.73%250.00%
Keith Owens1011.36%125.00%
Total88100.00%4100.00%

/* Look for this name: can be of form module:name. */
unsigned long module_kallsyms_lookup_name(const char *name) { struct module *mod; char *colon; unsigned long ret = 0; /* Don't lock: we're in enough trouble already. */ preempt_disable(); if ((colon = strnchr(name, MODULE_NAME_LEN, ':')) != NULL) { if ((mod = find_module_all(name, colon - name, false)) != NULL) ret = mod_find_symname(mod, colon+1); } else { list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; if ((ret = mod_find_symname(mod, name)) != 0) break; } } preempt_enable(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton9577.24%116.67%
Rusty Russell1713.82%233.33%
Mathias Krause75.69%116.67%
Naveen N. Rao32.44%116.67%
Andi Kleen10.81%116.67%
Total123100.00%6100.00%


int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, unsigned long), void *data) { struct module *mod; unsigned int i; int ret; module_assert_mutex(); list_for_each_entry(mod, &modules, list) { /* We hold module_mutex: no need for rcu_dereference_sched */ struct mod_kallsyms *kallsyms = mod->kallsyms; if (mod->state == MODULE_STATE_UNFORMED) continue; for (i = 0; i < kallsyms->num_symtab; i++) { ret = fn(data, symname(kallsyms, i), mod, kallsyms->symtab[i].st_value); if (ret != 0) return ret; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Anders H Kaseorg9977.34%120.00%
Rusty Russell2620.31%360.00%
Peter Zijlstra32.34%120.00%
Total128100.00%5100.00%

#endif /* CONFIG_KALLSYMS */ /* Maximum number of characters written by module_flags() */ #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) /* Keep in sync with MODULE_FLAGS_BUF_SIZE !!! */
static char *module_flags(struct module *mod, char *buf) { int bx = 0; BUG_ON(mod->state == MODULE_STATE_UNFORMED); if (mod->taints || mod->state == MODULE_STATE_GOING || mod->state == MODULE_STATE_COMING) { buf[bx++] = '('; bx += module_flags_taint(mod, buf + bx); /* Show a - for module-is-being-unloaded */ if (mod->state == MODULE_STATE_GOING) buf[bx++] = '-'; /* Show a + for module-is-being-loaded */ if (mod->state == MODULE_STATE_COMING) buf[bx++] = '+'; buf[bx++] = ')'; } buf[bx] = '\0'; return buf; }

Contributors

PersonTokensPropCommitsCommitProp
Arjan van de Ven5343.80%120.00%
Florin Malita4940.50%120.00%
Rusty Russell97.44%120.00%
Kay Sievers64.96%120.00%
Greg Kroah-Hartman43.31%120.00%
Total121100.00%5100.00%

#ifdef CONFIG_PROC_FS /* Called by the /proc file system to return a list of modules. */
static void *m_start(struct seq_file *m, loff_t *pos) { mutex_lock(&module_mutex); return seq_list_start(&modules, *pos); }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan32100.00%1100.00%
Total32100.00%1100.00%


static void *m_next(struct seq_file *m, void *p, loff_t *pos) { return seq_list_next(p, &modules, pos); }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan31100.00%1100.00%
Total31100.00%1100.00%


static void m_stop(struct seq_file *m, void *p) { mutex_unlock(&module_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan21100.00%1100.00%
Total21100.00%1100.00%


static int m_show(struct seq_file *m, void *p) { struct module *mod = list_entry(p, struct module, list); char buf[MODULE_FLAGS_BUF_SIZE]; void *value; /* We always ignore unformed modules. */ if (mod->state == MODULE_STATE_UNFORMED) return 0; seq_printf(m, "%s %u", mod->name, mod->init_layout.size + mod->core_layout.size); print_unload_info(m, mod); /* Informative for users. */ seq_printf(m, " %s", mod->state == MODULE_STATE_GOING ? "Unloading" : mod->state == MODULE_STATE_COMING ? "Loading" : "Live"); /* Used by oprofile and other similar tools. */ value = m->private ? NULL : mod->core_layout.base; seq_printf(m, " 0x%px", value); /* Taints info */ if (mod->taints) seq_printf(m, " %s", module_flags(mod, buf)); seq_puts(m, "\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell9357.06%538.46%
Florin Malita2515.34%17.69%
Ingo Molnar2213.50%17.69%
Linus Torvalds1911.66%215.38%
Arjan van de Ven10.61%17.69%
Ionut Alexa10.61%17.69%
Petr Mladek10.61%17.69%
Denys Vlasenko10.61%17.69%
Total163100.00%13100.00%

/* Format: modulename size refcount deps address Where refcount is a number or -, and deps is a comma-separated list of depends or -. */ static const struct seq_operations modules_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = m_show }; /* * This also sets the "private" pointer to non-NULL if the * kernel pointers should be hidden (so you can just test * "m->private" to see if you should keep the values private). * * We use the same logic as for /proc/kallsyms. */
static int modules_open(struct inode *inode, struct file *file) { int err = seq_open(file, &modules_op); if (!err) { struct seq_file *m = file->private_data; m->private = kallsyms_show_value() ? NULL : (void *)8ul; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds3659.02%133.33%
Alexey Dobriyan2439.34%133.33%
Leon Yu11.64%133.33%
Total61100.00%3100.00%

static const struct file_operations proc_modules_operations = { .open = modules_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, };
static int __init proc_modules_init(void) { proc_create("modules", 0, NULL, &proc_modules_operations); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexey Dobriyan24100.00%1100.00%
Total24100.00%1100.00%

module_init(proc_modules_init); #endif /* Given an address, look for it in the module exception tables. */
const struct exception_table_entry *search_module_extables(unsigned long addr) { const struct exception_table_entry *e = NULL; struct module *mod; preempt_disable(); mod = __module_address(addr); if (!mod) goto out; if (!mod->num_exentries) goto out; e = search_extable(mod->extable, mod->num_exentries, addr); out: preempt_enable(); /* * Now, if we found one, we are running inside it now, hence * we cannot unload the module, hence no refcnt needed. */ return e; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6179.22%480.00%
Peter Zijlstra1620.78%120.00%
Total77100.00%5100.00%

/* * is_module_address - is this address inside a module? * @addr: the address to check. * * See is_module_text_address() if you simply want to see if the address * is code (not data). */
bool is_module_address(unsigned long addr) { bool ret; preempt_disable(); ret = __module_address(addr) != NULL; preempt_enable(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ingo Molnar1756.67%133.33%
Rusty Russell1343.33%266.67%
Total30100.00%3100.00%

/* * __module_address - get the module which contains an address. * @addr: the address. * * Must be called with preempt disabled or module mutex held so that * module doesn't get freed during this. */
struct module *__module_address(unsigned long addr) { struct module *mod; if (addr < module_addr_min || addr > module_addr_max) return NULL; module_assert_mutex_or_preempt(); mod = mod_find(addr); if (mod) { BUG_ON(!within_module(addr, mod)); if (mod->state == MODULE_STATE_UNFORMED) mod = NULL; } return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4563.38%555.56%
Peter Zijlstra2535.21%333.33%
Petr Mladek11.41%111.11%
Total71100.00%9100.00%

EXPORT_SYMBOL_GPL(__module_address); /* * is_module_text_address - is this address inside module code? * @addr: the address to check. * * See is_module_address() if you simply want to see if the address is * anywhere in a module. See kernel_text_address() for testing if an * address corresponds to kernel or module code. */
bool is_module_text_address(unsigned long addr) { bool ret; preempt_disable(); ret = __module_text_address(addr) != NULL; preempt_enable(); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell30100.00%1100.00%
Total30100.00%1100.00%

/* * __module_text_address - get the module whose code contains an address. * @addr: the address. * * Must be called with preempt disabled or module mutex held so that * module doesn't get freed during this. */
struct module *__module_text_address(unsigned long addr) { struct module *mod = __module_address(addr); if (mod) { /* Make sure it's within the text section. */ if (!within(addr, mod->init_layout.base, mod->init_layout.text_size) && !within(addr, mod->core_layout.base, mod->core_layout.text_size)) mod = NULL; } return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell73100.00%4100.00%
Total73100.00%4100.00%

EXPORT_SYMBOL_GPL(__module_text_address); /* Don't grab lock, we're oopsing. */
void print_modules(void) { struct module *mod; char buf[MODULE_FLAGS_BUF_SIZE]; printk(KERN_DEFAULT "Modules linked in:"); /* Most callers should already have preempt disabled, but make sure */ preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; pr_cont(" %s%s", mod->name, module_flags(mod, buf)); } preempt_enable(); if (last_unloaded_module[0]) pr_cont(" [last unloaded: %s]", last_unloaded_module); pr_cont("\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton3541.18%111.11%
Arjan van de Ven1416.47%222.22%
Randy Dunlap1214.12%111.11%
Rusty Russell1112.94%111.11%
Andi Kleen89.41%111.11%
Jiri Slaby33.53%111.11%
Petr Mladek11.18%111.11%
Linus Torvalds11.18%111.11%
Total85100.00%9100.00%

#ifdef CONFIG_MODVERSIONS /* Generate the signature for all relevant module structures here. * If these change, we don't want to try to parse the module. */
void module_layout(struct module *mod, struct modversion_info *ver, struct kernel_param *kp, struct kernel_symbol *ks, struct tracepoint * const *tp) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2993.55%266.67%
Mathieu Desnoyers26.45%133.33%
Total31100.00%3100.00%

EXPORT_SYMBOL(module_layout); #endif

Overall Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell800440.23%11526.87%
Linus Torvalds12266.16%194.44%
Kees Cook10165.11%61.40%
Jessica Yu8984.51%51.17%
Peter Zijlstra7883.96%133.04%
Tejun Heo4962.49%102.34%
Roland McGrath4742.38%10.23%
Kay Sievers4412.22%102.34%
Andrew Morton4402.21%163.74%
Alexey Dobriyan4332.18%81.87%
Matthieu Castet4222.12%10.23%
Greg Kroah-Hartman3921.97%122.80%
Jan Beulich3791.91%20.47%
Linus Torvalds (pre-git)2761.39%163.74%
Jonathan Corbet2641.33%10.23%
Steven Rostedt2471.24%143.27%
Matt Domsch2441.23%10.23%
Kai Germaschewski1770.89%61.40%
Arjan van de Ven1500.75%61.40%
Masami Hiramatsu1460.73%81.87%
Prarit Bhargava1310.66%20.47%
Tim Abbott1260.63%30.70%
Florin Malita1120.56%10.23%
Catalin Marinas1060.53%30.70%
Anders H Kaseorg990.50%10.23%
Alessio Igor Bogani980.49%20.47%
Jonas Bonn850.43%10.23%
Ian S. Nelson810.41%10.23%
Ashutosh Naik760.38%20.47%
Ben Hutchings720.36%30.70%
Andi Kleen710.36%30.70%
Frantisek Hrbata690.35%10.23%
Luis R. Rodriguez680.34%71.64%
Jason Baron670.34%40.93%
Thomas Gleixner670.34%10.23%
Russell King650.33%20.47%
Ingo Molnar640.32%40.93%
Sam Ravnborg620.31%20.47%
Helge Deller590.30%30.70%
Corentin Labbe540.27%10.23%
Kevin Cernekee540.27%10.23%
Ard Biesheuvel530.27%10.23%
John Levon510.26%10.23%
Josh Poimboeuf480.24%20.47%
Denys Vlasenko470.24%30.70%
Petr Mladek460.23%20.47%
Libor Pechacek460.23%10.23%
Li Zhong440.22%10.23%
Mathias Krause440.22%10.23%
Miroslav Benes430.22%20.47%
Jan Glauber410.21%30.70%
Mimi Zohar340.17%10.23%
AKASHI Takahiro340.17%10.23%
Américo Wang330.17%10.23%
Randy Dunlap320.16%40.93%
Al Viro300.15%10.23%
Kevin Winchester300.15%10.23%
Denis V. Lunev290.15%10.23%
Richard Henderson280.14%20.47%
Peter Oberparleiter270.14%20.47%
David Howells250.13%30.70%
Matthew Garrett240.12%10.23%
Josef Bacik240.12%10.23%
Yehuda Sadeh Weinraub230.12%10.23%
Eric W. Biedermann230.12%10.23%
Laura Abbott220.11%30.70%
Ed Swierk210.11%10.23%
Jim Cromie200.10%10.23%
Mathieu Desnoyers190.10%30.70%
Sergey Senozhatsky190.10%10.23%
Li Zefan190.10%10.23%
Zhou Chengming180.09%10.23%
Jason Wessel170.09%10.23%
Bruno E. O. Meneguele160.08%10.23%
Richard Guy Briggs160.08%20.47%
Bernd Schmidt150.08%10.23%
Joe Mario140.07%10.23%
Aaron Tomlin130.07%20.47%
Brian Gerst130.07%10.23%
Heiko Carstens120.06%10.23%
Dave Young110.06%10.23%
Akinobu Mita110.06%20.47%
Keith Owens100.05%10.23%
Joe Lawrence90.05%10.23%
Trent Piepho90.05%10.23%
Andrew Lutomirski90.05%10.23%
Ionut Alexa80.04%10.23%
Christoph Lameter80.04%20.47%
Kirill A. Shutemov70.04%10.23%
Stephen Rothwell60.03%10.23%
Daniel J Blueman60.03%10.23%
Alan Stern60.03%10.23%
Namit Gupta50.03%10.23%
Chris Wright50.03%10.23%
Nicholas Piggin50.03%10.23%
Andreas Gruenbacher40.02%10.23%
Jeremy Linton40.02%20.47%
Paul Gortmaker40.02%20.47%
Peter Rajnoha40.02%10.23%
Franck Bui-Huu40.02%10.23%
James Hogan40.02%10.23%
Dan Streetman40.02%10.23%
Yannick Guerrini30.02%10.23%
Naveen N. Rao30.02%10.23%
Luis de Bethencourt30.02%10.23%
Jiri Slaby30.02%10.23%
Lucas De Marchi20.01%10.23%
Jianjun Kong20.01%10.23%
Satoru Takeuchi20.01%10.23%
Jan Kiszka20.01%10.23%
Tim Schmielau20.01%10.23%
Pawel Moll20.01%10.23%
Eric Dumazet20.01%10.23%
Matti Linnanvuori10.01%10.23%
Christoph Hellwig10.01%10.23%
Michał Mirosław10.01%10.23%
Marcel Holtmann10.01%10.23%
David Mosberger-Tang10.01%10.23%
Jiri Kosina10.01%10.23%
Paul E. McKenney10.01%10.23%
Kyle McMartin10.01%10.23%
Michael Ellerman10.01%10.23%
Chen Gang S10.01%10.23%
Larry Finger10.01%10.23%
Davi Arnaut10.01%10.23%
Robert P. J. Day10.01%10.23%
Leon Yu10.01%10.23%
Total19895100.00%428100.00%
Directory: kernel
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.