|
|
|
@@ -87,8 +87,7 @@
|
|
|
|
|
* 3) module_addr_min/module_addr_max.
|
|
|
|
|
* (delete and add uses RCU list operations).
|
|
|
|
|
*/
|
|
|
|
|
DEFINE_MUTEX(module_mutex);
|
|
|
|
|
EXPORT_SYMBOL_GPL(module_mutex);
|
|
|
|
|
static DEFINE_MUTEX(module_mutex);
|
|
|
|
|
static LIST_HEAD(modules);
|
|
|
|
|
|
|
|
|
|
/* Work queue for freeing init sections in success case */
|
|
|
|
@@ -256,11 +255,6 @@ static void mod_update_bounds(struct module *mod)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void module_assert_mutex_or_preempt(void)
|
|
|
|
|
{
|
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
|
|
@@ -414,19 +408,8 @@ 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
|
|
|
|
@@ -434,87 +417,14 @@ extern const s32 __start___kcrctab_unused_gpl[];
|
|
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns true as soon as fn returns true, otherwise false. */
|
|
|
|
|
static 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,
|
|
|
|
|
lockdep_is_held(&module_mutex)) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
struct symsearch {
|
|
|
|
|
const struct kernel_symbol *start, *stop;
|
|
|
|
|
const s32 *crcs;
|
|
|
|
|
enum mod_license {
|
|
|
|
|
NOT_GPL_ONLY,
|
|
|
|
|
GPL_ONLY,
|
|
|
|
|
} license;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct find_symbol_arg {
|
|
|
|
|
/* Input */
|
|
|
|
@@ -535,28 +445,8 @@ static bool check_exported_symbol(const struct symsearch *syms,
|
|
|
|
|
{
|
|
|
|
|
struct find_symbol_arg *fsa = data;
|
|
|
|
|
|
|
|
|
|
if (!fsa->gplok) {
|
|
|
|
|
if (syms->license == GPL_ONLY)
|
|
|
|
|
return false;
|
|
|
|
|
if (syms->license == 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
|
|
|
|
|
|
|
|
|
|
if (!fsa->gplok && syms->license == GPL_ONLY)
|
|
|
|
|
return false;
|
|
|
|
|
fsa->owner = owner;
|
|
|
|
|
fsa->crc = symversion(syms->crcs, symnum);
|
|
|
|
|
fsa->sym = &syms->start[symnum];
|
|
|
|
@@ -619,31 +509,44 @@ static bool find_exported_symbol_in_section(const struct symsearch *syms,
|
|
|
|
|
* Find an exported symbol and return it, along with, (optional) crc and
|
|
|
|
|
* (optional) module which owns it. Needs preempt disabled or module_mutex.
|
|
|
|
|
*/
|
|
|
|
|
static const struct kernel_symbol *find_symbol(const char *name,
|
|
|
|
|
struct module **owner,
|
|
|
|
|
const s32 **crc,
|
|
|
|
|
enum mod_license *license,
|
|
|
|
|
bool gplok,
|
|
|
|
|
bool warn)
|
|
|
|
|
static bool find_symbol(struct find_symbol_arg *fsa)
|
|
|
|
|
{
|
|
|
|
|
struct find_symbol_arg fsa;
|
|
|
|
|
static const struct symsearch arr[] = {
|
|
|
|
|
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
|
|
|
|
|
NOT_GPL_ONLY },
|
|
|
|
|
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
|
|
|
|
|
__start___kcrctab_gpl,
|
|
|
|
|
GPL_ONLY },
|
|
|
|
|
};
|
|
|
|
|
struct module *mod;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
fsa.name = name;
|
|
|
|
|
fsa.gplok = gplok;
|
|
|
|
|
fsa.warn = warn;
|
|
|
|
|
module_assert_mutex_or_preempt();
|
|
|
|
|
|
|
|
|
|
if (each_symbol_section(find_exported_symbol_in_section, &fsa)) {
|
|
|
|
|
if (owner)
|
|
|
|
|
*owner = fsa.owner;
|
|
|
|
|
if (crc)
|
|
|
|
|
*crc = fsa.crc;
|
|
|
|
|
if (license)
|
|
|
|
|
*license = fsa.license;
|
|
|
|
|
return fsa.sym;
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(arr); i++)
|
|
|
|
|
if (find_exported_symbol_in_section(&arr[i], NULL, fsa))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(mod, &modules, list,
|
|
|
|
|
lockdep_is_held(&module_mutex)) {
|
|
|
|
|
struct symsearch arr[] = {
|
|
|
|
|
{ mod->syms, mod->syms + mod->num_syms, mod->crcs,
|
|
|
|
|
NOT_GPL_ONLY },
|
|
|
|
|
{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
|
|
|
|
|
mod->gpl_crcs,
|
|
|
|
|
GPL_ONLY },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (mod->state == MODULE_STATE_UNFORMED)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(arr); i++)
|
|
|
|
|
if (find_exported_symbol_in_section(&arr[i], mod, fsa))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pr_debug("Failed to find symbol %s\n", name);
|
|
|
|
|
return NULL;
|
|
|
|
|
pr_debug("Failed to find symbol %s\n", fsa->name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -669,10 +572,8 @@ static struct module *find_module_all(const char *name, size_t len,
|
|
|
|
|
|
|
|
|
|
struct module *find_module(const char *name)
|
|
|
|
|
{
|
|
|
|
|
module_assert_mutex();
|
|
|
|
|
return find_module_all(name, strlen(name), false);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(find_module);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
|
|
|
|
|
@@ -1107,12 +1008,15 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
|
|
|
|
|
|
|
|
|
void __symbol_put(const char *symbol)
|
|
|
|
|
{
|
|
|
|
|
struct module *owner;
|
|
|
|
|
struct find_symbol_arg fsa = {
|
|
|
|
|
.name = symbol,
|
|
|
|
|
.gplok = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
|
if (!find_symbol(symbol, &owner, NULL, NULL, true, false))
|
|
|
|
|
if (!find_symbol(&fsa))
|
|
|
|
|
BUG();
|
|
|
|
|
module_put(owner);
|
|
|
|
|
module_put(fsa.owner);
|
|
|
|
|
preempt_enable();
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(__symbol_put);
|
|
|
|
@@ -1381,19 +1285,22 @@ bad_version:
|
|
|
|
|
static inline int check_modstruct_version(const struct load_info *info,
|
|
|
|
|
struct module *mod)
|
|
|
|
|
{
|
|
|
|
|
const s32 *crc;
|
|
|
|
|
struct find_symbol_arg fsa = {
|
|
|
|
|
.name = "module_layout",
|
|
|
|
|
.gplok = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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("module_layout", NULL, &crc, NULL, true, false)) {
|
|
|
|
|
if (!find_symbol(&fsa)) {
|
|
|
|
|
preempt_enable();
|
|
|
|
|
BUG();
|
|
|
|
|
}
|
|
|
|
|
preempt_enable();
|
|
|
|
|
return check_version(info, "module_layout", mod, crc);
|
|
|
|
|
return check_version(info, "module_layout", mod, fsa.crc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* First part is kernel version, which we ignore if module has crcs. */
|
|
|
|
@@ -1487,10 +1394,11 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
|
|
|
|
|
const char *name,
|
|
|
|
|
char ownername[])
|
|
|
|
|
{
|
|
|
|
|
struct module *owner;
|
|
|
|
|
const struct kernel_symbol *sym;
|
|
|
|
|
const s32 *crc;
|
|
|
|
|
enum mod_license license;
|
|
|
|
|
struct find_symbol_arg fsa = {
|
|
|
|
|
.name = name,
|
|
|
|
|
.gplok = !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)),
|
|
|
|
|
.warn = true,
|
|
|
|
|
};
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -1500,42 +1408,40 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
|
|
|
|
|
*/
|
|
|
|
|
sched_annotate_sleep();
|
|
|
|
|
mutex_lock(&module_mutex);
|
|
|
|
|
sym = find_symbol(name, &owner, &crc, &license,
|
|
|
|
|
!(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true);
|
|
|
|
|
if (!sym)
|
|
|
|
|
if (!find_symbol(&fsa))
|
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
|
|
if (license == GPL_ONLY)
|
|
|
|
|
if (fsa.license == GPL_ONLY)
|
|
|
|
|
mod->using_gplonly_symbols = true;
|
|
|
|
|
|
|
|
|
|
if (!inherit_taint(mod, owner)) {
|
|
|
|
|
sym = NULL;
|
|
|
|
|
if (!inherit_taint(mod, fsa.owner)) {
|
|
|
|
|
fsa.sym = NULL;
|
|
|
|
|
goto getname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!check_version(info, name, mod, crc)) {
|
|
|
|
|
sym = ERR_PTR(-EINVAL);
|
|
|
|
|
if (!check_version(info, name, mod, fsa.crc)) {
|
|
|
|
|
fsa.sym = ERR_PTR(-EINVAL);
|
|
|
|
|
goto getname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = verify_namespace_is_imported(info, sym, mod);
|
|
|
|
|
err = verify_namespace_is_imported(info, fsa.sym, mod);
|
|
|
|
|
if (err) {
|
|
|
|
|
sym = ERR_PTR(err);
|
|
|
|
|
fsa.sym = ERR_PTR(err);
|
|
|
|
|
goto getname;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ref_module(mod, owner);
|
|
|
|
|
err = ref_module(mod, fsa.owner);
|
|
|
|
|
if (err) {
|
|
|
|
|
sym = ERR_PTR(err);
|
|
|
|
|
fsa.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);
|
|
|
|
|
strncpy(ownername, module_name(fsa.owner), MODULE_NAME_LEN);
|
|
|
|
|
unlock:
|
|
|
|
|
mutex_unlock(&module_mutex);
|
|
|
|
|
return sym;
|
|
|
|
|
return fsa.sym;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct kernel_symbol *
|
|
|
|
@@ -2296,16 +2202,19 @@ static void free_module(struct module *mod)
|
|
|
|
|
|
|
|
|
|
void *__symbol_get(const char *symbol)
|
|
|
|
|
{
|
|
|
|
|
struct module *owner;
|
|
|
|
|
const struct kernel_symbol *sym;
|
|
|
|
|
struct find_symbol_arg fsa = {
|
|
|
|
|
.name = symbol,
|
|
|
|
|
.gplok = true,
|
|
|
|
|
.warn = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
|
sym = find_symbol(symbol, &owner, NULL, NULL, true, true);
|
|
|
|
|
if (sym && strong_try_module_get(owner))
|
|
|
|
|
sym = NULL;
|
|
|
|
|
if (!find_symbol(&fsa) || strong_try_module_get(fsa.owner)) {
|
|
|
|
|
preempt_enable();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
preempt_enable();
|
|
|
|
|
|
|
|
|
|
return sym ? (void *)kernel_symbol_value(sym) : NULL;
|
|
|
|
|
return (void *)kernel_symbol_value(fsa.sym);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(__symbol_get);
|
|
|
|
|
|
|
|
|
@@ -2318,7 +2227,6 @@ EXPORT_SYMBOL_GPL(__symbol_get);
|
|
|
|
|
static int verify_exported_symbols(struct module *mod)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
struct module *owner;
|
|
|
|
|
const struct kernel_symbol *s;
|
|
|
|
|
struct {
|
|
|
|
|
const struct kernel_symbol *sym;
|
|
|
|
@@ -2326,21 +2234,19 @@ static int verify_exported_symbols(struct module *mod)
|
|
|
|
|
} 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(kernel_symbol_name(s), &owner, NULL,
|
|
|
|
|
NULL, true, false)) {
|
|
|
|
|
struct find_symbol_arg fsa = {
|
|
|
|
|
.name = kernel_symbol_name(s),
|
|
|
|
|
.gplok = true,
|
|
|
|
|
};
|
|
|
|
|
if (find_symbol(&fsa)) {
|
|
|
|
|
pr_err("%s: exports duplicate symbol %s"
|
|
|
|
|
" (owned by %s)\n",
|
|
|
|
|
mod->name, kernel_symbol_name(s),
|
|
|
|
|
module_name(owner));
|
|
|
|
|
module_name(fsa.owner));
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -2348,6 +2254,21 @@ static int verify_exported_symbols(struct module *mod)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ignore_undef_symbol(Elf_Half emachine, const char *name)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* On x86, PIC code and Clang non-PIC code may have call foo@PLT. GNU as
|
|
|
|
|
* before 2.37 produces an unreferenced _GLOBAL_OFFSET_TABLE_ on x86-64.
|
|
|
|
|
* i386 has a similar problem but may not deserve a fix.
|
|
|
|
|
*
|
|
|
|
|
* If we ever have to ignore many symbols, consider refactoring the code to
|
|
|
|
|
* only warn if referenced by a relocation.
|
|
|
|
|
*/
|
|
|
|
|
if (emachine == EM_386 || emachine == EM_X86_64)
|
|
|
|
|
return !strcmp(name, "_GLOBAL_OFFSET_TABLE_");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change all symbols so that st_value encodes the pointer directly. */
|
|
|
|
|
static int simplify_symbols(struct module *mod, const struct load_info *info)
|
|
|
|
|
{
|
|
|
|
@@ -2395,8 +2316,10 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ok if weak. */
|
|
|
|
|
if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK)
|
|
|
|
|
/* Ok if weak or ignored. */
|
|
|
|
|
if (!ksym &&
|
|
|
|
|
(ELF_ST_BIND(sym[i].st_info) == STB_WEAK ||
|
|
|
|
|
ignore_undef_symbol(info->hdr->e_machine, name)))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ret = PTR_ERR(ksym) ?: -ENOENT;
|
|
|
|
@@ -2964,7 +2887,7 @@ static int module_sig_check(struct load_info *info, int flags)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_module_sig_enforced()) {
|
|
|
|
|
pr_notice("%s: loading of %s is rejected\n", info->name, reason);
|
|
|
|
|
pr_notice("Loading of %s is rejected\n", reason);
|
|
|
|
|
return -EKEYREJECTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2977,9 +2900,33 @@ static int module_sig_check(struct load_info *info, int flags)
|
|
|
|
|
}
|
|
|
|
|
#endif /* !CONFIG_MODULE_SIG */
|
|
|
|
|
|
|
|
|
|
/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
|
|
|
|
|
static int elf_header_check(struct load_info *info)
|
|
|
|
|
static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr)
|
|
|
|
|
{
|
|
|
|
|
unsigned long secend;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check for both overflow and offset/size being
|
|
|
|
|
* too large.
|
|
|
|
|
*/
|
|
|
|
|
secend = shdr->sh_offset + shdr->sh_size;
|
|
|
|
|
if (secend < shdr->sh_offset || secend > info->len)
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Sanity checks against invalid binaries, wrong arch, weird elf version.
|
|
|
|
|
*
|
|
|
|
|
* Also do basic validity checks against section offsets and sizes, the
|
|
|
|
|
* section name string table, and the indices used for it (sh_name).
|
|
|
|
|
*/
|
|
|
|
|
static int elf_validity_check(struct load_info *info)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
Elf_Shdr *shdr, *strhdr;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (info->len < sizeof(*(info->hdr)))
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
@@ -2989,11 +2936,78 @@ static int elf_header_check(struct load_info *info)
|
|
|
|
|
|| info->hdr->e_shentsize != sizeof(Elf_Shdr))
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* e_shnum is 16 bits, and sizeof(Elf_Shdr) is
|
|
|
|
|
* known and small. So e_shnum * sizeof(Elf_Shdr)
|
|
|
|
|
* will not overflow unsigned long on any platform.
|
|
|
|
|
*/
|
|
|
|
|
if (info->hdr->e_shoff >= info->len
|
|
|
|
|
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) >
|
|
|
|
|
info->len - info->hdr->e_shoff))
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Verify if the section name table index is valid.
|
|
|
|
|
*/
|
|
|
|
|
if (info->hdr->e_shstrndx == SHN_UNDEF
|
|
|
|
|
|| info->hdr->e_shstrndx >= info->hdr->e_shnum)
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
|
|
|
|
|
err = validate_section_offset(info, strhdr);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The section name table must be NUL-terminated, as required
|
|
|
|
|
* by the spec. This makes strcmp and pr_* calls that access
|
|
|
|
|
* strings in the section safe.
|
|
|
|
|
*/
|
|
|
|
|
info->secstrings = (void *)info->hdr + strhdr->sh_offset;
|
|
|
|
|
if (info->secstrings[strhdr->sh_size - 1] != '\0')
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The code assumes that section 0 has a length of zero and
|
|
|
|
|
* an addr of zero, so check for it.
|
|
|
|
|
*/
|
|
|
|
|
if (info->sechdrs[0].sh_type != SHT_NULL
|
|
|
|
|
|| info->sechdrs[0].sh_size != 0
|
|
|
|
|
|| info->sechdrs[0].sh_addr != 0)
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
|
|
|
shdr = &info->sechdrs[i];
|
|
|
|
|
switch (shdr->sh_type) {
|
|
|
|
|
case SHT_NULL:
|
|
|
|
|
case SHT_NOBITS:
|
|
|
|
|
continue;
|
|
|
|
|
case SHT_SYMTAB:
|
|
|
|
|
if (shdr->sh_link == SHN_UNDEF
|
|
|
|
|
|| shdr->sh_link >= info->hdr->e_shnum)
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
fallthrough;
|
|
|
|
|
default:
|
|
|
|
|
err = validate_section_offset(info, shdr);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
pr_err("Invalid ELF section in module (section %u type %u)\n",
|
|
|
|
|
i, shdr->sh_type);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shdr->sh_flags & SHF_ALLOC) {
|
|
|
|
|
if (shdr->sh_name >= strhdr->sh_size) {
|
|
|
|
|
pr_err("Invalid ELF section name in module (section %u type %u)\n",
|
|
|
|
|
i, shdr->sh_type);
|
|
|
|
|
return -ENOEXEC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3095,11 +3109,6 @@ static int rewrite_section_headers(struct load_info *info, int flags)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@@ -3133,11 +3142,6 @@ static int setup_load_info(struct load_info *info, int flags)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
/* Try to find a name early so we can log errors with a module name */
|
|
|
|
|
info->index.info = find_sec(info, ".modinfo");
|
|
|
|
|
if (info->index.info)
|
|
|
|
@@ -3241,22 +3245,7 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
|
|
|
|
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);
|
|
|
|
@@ -3437,14 +3426,8 @@ static int check_module_license_and_versions(struct module *mod)
|
|
|
|
|
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
|
|
|
|
|
) {
|
|
|
|
|
if ((mod->num_syms && !mod->crcs) ||
|
|
|
|
|
(mod->num_gpl_syms && !mod->gpl_crcs)) {
|
|
|
|
|
return try_to_force_load(mod,
|
|
|
|
|
"no versions for exported symbols");
|
|
|
|
|
}
|
|
|
|
@@ -3894,26 +3877,50 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
|
|
|
|
long err = 0;
|
|
|
|
|
char *after_dashes;
|
|
|
|
|
|
|
|
|
|
err = elf_header_check(info);
|
|
|
|
|
/*
|
|
|
|
|
* Do the signature check (if any) first. All that
|
|
|
|
|
* the signature check needs is info->len, it does
|
|
|
|
|
* not need any of the section info. That can be
|
|
|
|
|
* set up later. This will minimize the chances
|
|
|
|
|
* of a corrupt module causing problems before
|
|
|
|
|
* we even get to the signature check.
|
|
|
|
|
*
|
|
|
|
|
* The check will also adjust info->len by stripping
|
|
|
|
|
* off the sig length at the end of the module, making
|
|
|
|
|
* checks against info->len more correct.
|
|
|
|
|
*/
|
|
|
|
|
err = module_sig_check(info, flags);
|
|
|
|
|
if (err)
|
|
|
|
|
goto free_copy;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do basic sanity checks against the ELF header and
|
|
|
|
|
* sections.
|
|
|
|
|
*/
|
|
|
|
|
err = elf_validity_check(info);
|
|
|
|
|
if (err) {
|
|
|
|
|
pr_err("Module has invalid ELF header\n");
|
|
|
|
|
pr_err("Module has invalid ELF structures\n");
|
|
|
|
|
goto free_copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Everything checks out, so set up the section info
|
|
|
|
|
* in the info structure.
|
|
|
|
|
*/
|
|
|
|
|
err = setup_load_info(info, flags);
|
|
|
|
|
if (err)
|
|
|
|
|
goto free_copy;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now that we know we have the correct module name, check
|
|
|
|
|
* if it's blacklisted.
|
|
|
|
|
*/
|
|
|
|
|
if (blacklisted(info->name)) {
|
|
|
|
|
err = -EPERM;
|
|
|
|
|
pr_err("Module %s is blacklisted\n", info->name);
|
|
|
|
|
goto free_copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = module_sig_check(info, flags);
|
|
|
|
|
if (err)
|
|
|
|
|
goto free_copy;
|
|
|
|
|
|
|
|
|
|
err = rewrite_section_headers(info, flags);
|
|
|
|
|
if (err)
|
|
|
|
|
goto free_copy;
|
|
|
|
@@ -4374,16 +4381,16 @@ unsigned long module_kallsyms_lookup_name(const char *name)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_LIVEPATCH
|
|
|
|
|
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();
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&module_mutex);
|
|
|
|
|
list_for_each_entry(mod, &modules, list) {
|
|
|
|
|
/* We hold module_mutex: no need for rcu_dereference_sched */
|
|
|
|
|
struct mod_kallsyms *kallsyms = mod->kallsyms;
|
|
|
|
@@ -4399,11 +4406,13 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
|
|
|
|
|
ret = fn(data, kallsyms_symbol_name(kallsyms, i),
|
|
|
|
|
mod, kallsyms_symbol_value(sym));
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
return ret;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
mutex_unlock(&module_mutex);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_LIVEPATCH */
|
|
|
|
|
#endif /* CONFIG_KALLSYMS */
|
|
|
|
|
|
|
|
|
|
/* Maximum number of characters written by module_flags() */
|
|
|
|
|