Skip to content

Commit

Permalink
module: Move version support into a separate file
Browse files Browse the repository at this point in the history
No functional change.

This patch migrates module version support out of core code into
kernel/module/version.c. In addition simple code refactoring to
make this possible.

Signed-off-by: Aaron Tomlin <atomlin@redhat.com>
  • Loading branch information
Aaron Tomlin authored and intel-lab-lkp committed Jan 28, 2022
1 parent 49d529b commit 201551e
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 148 deletions.
1 change: 1 addition & 0 deletions kernel/module/Makefile
Expand Up @@ -15,3 +15,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PROC_FS) += procfs.o
obj-$(CONFIG_SYSFS) += sysfs.o
obj-$(CONFIG_MODVERSIONS) += version.o
51 changes: 51 additions & 0 deletions kernel/module/internal.h
Expand Up @@ -59,7 +59,31 @@ struct load_info {
} index;
};

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 */
const char *name;
bool gplok;
bool warn;

/* Output */
struct module *owner;
const s32 *crc;
const struct kernel_symbol *sym;
enum mod_license license;
};

extern int mod_verify_sig(const void *mod, struct load_info *info);
extern int try_to_force_load(struct module *mod, const char *reason);
extern bool find_symbol(struct find_symbol_arg *fsa);
extern struct module *find_module_all(const char *name, size_t len, bool even_unformed);
extern unsigned long kernel_symbol_value(const struct kernel_symbol *sym);
extern int cmp_name(const void *name, const void *sym);
Expand Down Expand Up @@ -149,3 +173,30 @@ static inline void module_remove_modinfo_attrs(struct module *mod, int end) { }
static inline void del_usage_links(struct module *mod) { }
static inline void init_param_lock(struct module *mod) { }
#endif /* CONFIG_SYSFS */

#ifdef CONFIG_MODVERSIONS
extern int check_version(const struct load_info *info,
const char *symname, struct module *mod, const s32 *crc);
extern int check_modstruct_version(const struct load_info *info, struct module *mod);
extern int same_magic(const char *amagic, const char *bmagic, bool has_crcs);
#else /* !CONFIG_MODVERSIONS */
static inline int check_version(const struct load_info *info,
const char *symname,
struct module *mod,
const s32 *crc)
{
return 1;
}

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

static inline int same_magic(const char *amagic, const char *bmagic,
bool has_crcs)
{
return strcmp(amagic, bmagic) == 0;
}
#endif /* CONFIG_MODVERSIONS */
150 changes: 2 additions & 148 deletions kernel/module/main.c
Expand Up @@ -231,28 +231,6 @@ static __maybe_unused void *any_section_objs(const struct load_info *info,
#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)
#endif

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 */
const char *name;
bool gplok;
bool warn;

/* Output */
struct module *owner;
const s32 *crc;
const struct kernel_symbol *sym;
enum mod_license license;
};

static bool check_exported_symbol(const struct symsearch *syms,
struct module *owner,
unsigned int symnum, void *data)
Expand Down Expand Up @@ -323,7 +301,7 @@ 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 bool find_symbol(struct find_symbol_arg *fsa)
bool find_symbol(struct find_symbol_arg *fsa)
{
static const struct symsearch arr[] = {
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
Expand Down Expand Up @@ -997,7 +975,7 @@ size_t modinfo_attrs_count = ARRAY_SIZE(modinfo_attrs);

static const char vermagic[] = VERMAGIC_STRING;

static int try_to_force_load(struct module *mod, const char *reason)
int try_to_force_load(struct module *mod, const char *reason)
{
#ifdef CONFIG_MODULE_FORCE_LOAD
if (!test_taint(TAINT_FORCED_MODULE))
Expand All @@ -1009,115 +987,6 @@ static int try_to_force_load(struct module *mod, const char *reason)
#endif
}

#ifdef CONFIG_MODVERSIONS

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

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

static inline int check_modstruct_version(const struct load_info *info,
struct module *mod)
{
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(&fsa)) {
preempt_enable();
BUG();
}
preempt_enable();
return check_version(info, "module_layout", mod, fsa.crc);
}

/* 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;
}
#else
static inline int check_version(const struct load_info *info,
const char *symname,
struct module *mod,
const s32 *crc)
{
return 1;
}

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

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

static char *get_modinfo(const struct load_info *info, const char *tag);
static char *get_next_modinfo(const struct load_info *info, const char *tag,
char *prev);
Expand Down Expand Up @@ -3229,18 +3098,3 @@ void print_modules(void)
pr_cont(" [last unloaded: %s]", last_unloaded_module);
pr_cont("\n");
}

#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)
{
}
EXPORT_SYMBOL(module_layout);
#endif
110 changes: 110 additions & 0 deletions kernel/module/version.c
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Module version support
*
* Copyright (C) 2008 Rusty Russell
*/

#include <linux/module.h>
#include <linux/string.h>
#include <linux/printk.h>
#include "internal.h"

/*
* 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)
{
}
EXPORT_SYMBOL(module_layout);

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

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

inline int check_modstruct_version(const struct load_info *info,
struct module *mod)
{
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(&fsa)) {
preempt_enable();
BUG();
}
preempt_enable();
return check_version(info, "module_layout", mod, fsa.crc);
}

/* First part is kernel version, which we ignore if module has crcs. */
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;
}

0 comments on commit 201551e

Please sign in to comment.