root/kernel/module/version.c
// 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"

int check_version(const struct load_info *info,
                  const char *symname,
                         struct module *mod,
                         const u32 *crc)
{
        Elf_Shdr *sechdrs = info->sechdrs;
        unsigned int versindex = info->index.vers;
        unsigned int i, num_versions;
        struct modversion_info *versions;
        struct modversion_info_ext version_ext;

        /* Exporting module didn't supply crcs?  OK, we're already tainted. */
        if (!crc)
                return 1;

        /* If we have extended version info, rely on it */
        if (info->index.vers_ext_crc) {
                for_each_modversion_info_ext(version_ext, info) {
                        if (strcmp(version_ext.name, symname) != 0)
                                continue;
                        if (*version_ext.crc == *crc)
                                return 1;
                        pr_debug("Found checksum %X vs module %X\n",
                                 *crc, *version_ext.crc);
                        goto bad_version;
                }
                pr_warn_once("%s: no extended symbol version for %s\n",
                             info->name, symname);
                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;

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

int check_modstruct_version(const struct load_info *info,
                            struct module *mod)
{
        struct find_symbol_arg fsa = {
                .name   = "module_layout",
                .gplok  = true,
        };
        bool have_symbol;

        /*
         * Since this should be found in kernel (which can't be removed), no
         * locking is necessary. Regardless use a RCU read section to keep
         * lockdep happy.
         */
        scoped_guard(rcu)
                have_symbol = find_symbol(&fsa);
        BUG_ON(!have_symbol);

        return check_version(info, "module_layout", mod, fsa.crc);
}

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

void modversion_ext_start(const struct load_info *info,
                          struct modversion_info_ext *start)
{
        unsigned int crc_idx = info->index.vers_ext_crc;
        unsigned int name_idx = info->index.vers_ext_name;
        Elf_Shdr *sechdrs = info->sechdrs;

        /*
         * Both of these fields are needed for this to be useful
         * Any future fields should be initialized to NULL if absent.
         */
        if (crc_idx == 0 || name_idx == 0) {
                start->remaining = 0;
                return;
        }

        start->crc = (const u32 *)sechdrs[crc_idx].sh_addr;
        start->name = (const char *)sechdrs[name_idx].sh_addr;
        start->remaining = sechdrs[crc_idx].sh_size / sizeof(*start->crc);
}

void modversion_ext_advance(struct modversion_info_ext *vers)
{
        vers->remaining--;
        vers->crc++;
        vers->name += strlen(vers->name) + 1;
}

/*
 * 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);