Received: by 2002:a05:7412:b130:b0:e2:908c:2ebd with SMTP id az48csp946299rdb; Fri, 17 Nov 2023 18:58:27 -0800 (PST) X-Google-Smtp-Source: AGHT+IEJnc8rMYOF3nelXSSbXReCsRXDJhHM0+gGCpNqkCNqoleXIFPYINh9ud3hpC4Jz11jcdDl X-Received: by 2002:a05:6830:2b10:b0:6d6:9145:6570 with SMTP id l16-20020a0568302b1000b006d691456570mr1105251otv.34.1700276306996; Fri, 17 Nov 2023 18:58:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1700276306; cv=none; d=google.com; s=arc-20160816; b=DdX/uBcQ4cS8sAUEi2f8/L8z/VpcRcxfppDDtbsdffTWXFMYy2RI6IRoypjbf/9I9X X9AXwWmZa/rPRmF+aolYlEXrUx4DH/4fcZPsIqKOVdncsv0DD2wGjJ3oL//LiwBu6lef KEyoebDDbHTfnG4SAS2/wGXgyilyzNkxu3evbFCM9xsfRiBSREjpePjZT93mSFYFUYT8 Obl/qMiQSJ4tTmAYAKiZfQcGgfkahuoE62gZye+HjCeOpE2Qnz4GovCdmSATMfSKmWg5 fTHYX8vQVzWMl3P5kEmqCKzJKC3haCtW7khqMmfM3J0nfuD9PY4DAirpvwhmUfw9Ligh PE5g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=YNSJ/gYYBG8A4ykrZfDNuKpVx6Mci9cgUfNx2nrc6vM=; fh=tvII/HPajp/NlqnxeuWRc9nIfYr8jgLiigCgB7HTnyI=; b=ezS3VlC6fFcYJyV8/qUjTq/JEgp7vaUsSCG9YXbkh1oJYwu/lfsrj1MOrhEN4reQMF y1IjDPdYptploGsLWQ4HTPo5NHwfO15u1sMCJqGvb+RM7NA6opDtUk11sPQ9Ma/t7c96 gXeNJfpFilS2Zact7AsT3i1jmwYfNT4Z3hnlu5cGgNDDe8coEsEYdzDlG8grWty/3WFf E7sTBE5rJhZ9OfgAM/x55hYmnQjmFW9Z6aqIa7AaMsTvYI+g/H8uNTApVe4t8RR63dZp ZKPxAHDFFFNyzf1LDY1FK/UGAA8EbNH9NTkOAXsAFOkjFa8TNf5wLp9avC16ZaVgGrio NgVA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=yTZMmN9d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from snail.vger.email (snail.vger.email. [23.128.96.37]) by mx.google.com with ESMTPS id c1-20020a63da01000000b005aaab9e7bcfsi3365398pgh.388.2023.11.17.18.58.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Nov 2023 18:58:26 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) client-ip=23.128.96.37; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=yTZMmN9d; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 085258258CAB; Fri, 17 Nov 2023 18:58:24 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233019AbjKRC6T (ORCPT + 99 others); Fri, 17 Nov 2023 21:58:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51752 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232854AbjKRC6R (ORCPT ); Fri, 17 Nov 2023 21:58:17 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 748C41702 for ; Fri, 17 Nov 2023 18:58:12 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5af9b0850fdso41525597b3.1 for ; Fri, 17 Nov 2023 18:58:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1700276291; x=1700881091; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=YNSJ/gYYBG8A4ykrZfDNuKpVx6Mci9cgUfNx2nrc6vM=; b=yTZMmN9dxaz/CCsYMxZi23R36CRoJ/aFea1WHji/hOb89D3bbpqid5+TloxCTFdHpK r3urdVBBxL82JAX6Cu4SyOzw7L9n+KKfC6UhMgMoO2/T65nKAc3TjWYpbD8SMGqyp+gI JyqMhM/kA11hfJ+BThp+zEtSe28XQzmx/l9hcU1F6UCBcdSBMlW0J6U1TU2HYzs+26jv 4sRYMBs2/tPTpy9KWbV5dsx746izODZds9y53LDd/CQvQGIhbSQdHamI53DE9iuezxBr 6CFZtvWcZnlD227SwJ6N1nPriuvySMgAYgnGFHQXD5dfgZejxjI9xftBdBD6VnByRfs/ axpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700276291; x=1700881091; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=YNSJ/gYYBG8A4ykrZfDNuKpVx6Mci9cgUfNx2nrc6vM=; b=sKK1Xky2Fr9xmGf23nm5quZlttcxAfdL7YRD13oH+cMB+OyKrCOVNJ9m3A2BqInDBe vjpz2lzTOeASK19OO3E/P5uTFa8sMEAxASr7zkglokSEsuMK+gVWPLodTUt8D4d70TcU FSIw0l26FO8wvwM6NIBHpylNSq5gsCQUniLUuqBhv7olf3Xaj6yGraWVFP0AsfTDBe6O MOyAElOijWIb5sbFS93kVfxQDmTRBY57ziS/CryfPOYfq9c5FKCW1Sb9bAEwfub6UfWi KHDS64r7mch1e2LPxBHzc/H7YF5F5uGK9/GVnfefDvs+ggln5DA4ZkzApCpoa54Wkjow ZeVg== X-Gm-Message-State: AOJu0YwlLR55M5p3DSb2eDekUYkK2E5SeHsBc464M/19ujAmYS6wQ82b hke/reSNNuFlIAAO9nkoZuy8NLAPcZqM X-Received: from anyblade.c.googlers.com ([fda3:e722:ac3:cc00:20:ed76:c0a8:1791]) (user=mmaurer job=sendgmr) by 2002:a81:a1cb:0:b0:5be:9a1e:5867 with SMTP id y194-20020a81a1cb000000b005be9a1e5867mr38515ywg.6.1700276291732; Fri, 17 Nov 2023 18:58:11 -0800 (PST) Date: Sat, 18 Nov 2023 02:54:44 +0000 In-Reply-To: <20231118025748.2778044-1-mmaurer@google.com> Mime-Version: 1.0 References: <20231118025748.2778044-1-mmaurer@google.com> X-Mailer: git-send-email 2.43.0.rc0.421.g78406f8d94-goog Message-ID: <20231118025748.2778044-4-mmaurer@google.com> Subject: [PATCH v2 3/5] modpost: Extended modversion support From: Matthew Maurer To: Masahiro Yamada , Nick Desaulniers , Miguel Ojeda , Gary Guo , Luis Chamberlain Cc: Nathan Chancellor , Nicolas Schier , linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org, linux-modules@vger.kernel.org, linux-kbuild@vger.kernel.org, rust-for-linux@vger.kernel.org, Laura Abbott , Matthew Maurer Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (snail.vger.email [0.0.0.0]); Fri, 17 Nov 2023 18:58:24 -0800 (PST) Adds a new format for modversions which stores each field in a separate ELF section. This initially adds support for variable length names, but could later be used to add additional fields to modversions in a backwards compatible way if needed. Since PPC munges its version records to strip leading dots, we reproduce the munging for the new format. Other architectures do not appear to have architecture-specific usage of this information. Signed-off-by: Matthew Maurer --- arch/powerpc/kernel/module_64.c | 25 ++++++++- kernel/module/internal.h | 11 ++++ kernel/module/main.c | 92 ++++++++++++++++++++++++++++++--- kernel/module/version.c | 43 +++++++++++++++ scripts/mod/modpost.c | 37 +++++++++++-- 5 files changed, 195 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 7112adc597a8..bff03627014c 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -22,6 +22,7 @@ #include #include #include +#include /* FIXME: We don't do .init separately. To do this, we'd need to have a separate r2 value in the init and core section, and stub between @@ -355,6 +356,24 @@ static void dedotify_versions(struct modversion_info *vers, } } +static void dedotify_ext_version_names(char *str_seq, unsigned long size) +{ + unsigned long out = 0; + unsigned long in; + char last = '\0'; + + for (in = 0; in < size; in++) { + if (last == '\0') + /* Skip all leading dots */ + if (str_seq[in] == '.') + continue; + last = str_seq[in]; + str_seq[out++] = last; + } + /* Zero the trailing portion of the names table for robustness */ + bzero(&str_seq[out], size - out); +} + /* * Undefined symbols which refer to .funcname, hack to funcname. Make .TOC. * seem to be defined (value set later). @@ -424,10 +443,12 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, me->arch.toc_section = i; if (sechdrs[i].sh_addralign < 8) sechdrs[i].sh_addralign = 8; - } - else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0) + } else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") == 0) dedotify_versions((void *)hdr + sechdrs[i].sh_offset, sechdrs[i].sh_size); + else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") == 0) + dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size); if (sechdrs[i].sh_type == SHT_SYMTAB) dedotify((void *)hdr + sechdrs[i].sh_offset, diff --git a/kernel/module/internal.h b/kernel/module/internal.h index d8dc52eb9c82..35d89c3f657c 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -86,6 +86,8 @@ struct load_info { unsigned int vers; unsigned int info; unsigned int pcpu; + unsigned int vers_ext_crc; + unsigned int vers_ext_name; } index; }; @@ -389,6 +391,15 @@ void module_layout(struct module *mod, struct modversion_info *ver, struct kerne struct kernel_symbol *ks, struct tracepoint * const *tp); int check_modstruct_version(const struct load_info *info, struct module *mod); int same_magic(const char *amagic, const char *bmagic, bool has_crcs); +struct modversion_info_ext { + size_t remaining; + const s32 *crc; + const char *name; +}; +void modversion_ext_start(const struct load_info *info, struct modversion_info_ext *ver); +void modversion_ext_advance(struct modversion_info_ext *ver); +#define for_each_modversion_info_ext(ver, info) \ + for (modversion_ext_start(info, &ver); ver.remaining > 0; modversion_ext_advance(&ver)) #else /* !CONFIG_MODVERSIONS */ static inline int check_version(const struct load_info *info, const char *symname, diff --git a/kernel/module/main.c b/kernel/module/main.c index 8b2848b3183a..2b175b7953e7 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2021,6 +2021,82 @@ static int elf_validity_cache_index_str(struct load_info *info) return 0; } +/** + * elf_validity_cache_index_versions() - Validate and cache version indices + * @info: Load info to cache version indices in. + * Must have &load_info->sechdrs and &load_info->secstrings populated. + * @flags: Load flags, relevant to suppress version loading, see + * uapi/linux/module.h + * + * If we're ignoring modversions based on @flags, zero all version indices + * and return validity. Othewrise check: + * + * * If "__version_ext_crcs" is present, "__version_ext_names" is present + * * There is a name present for every crc + * + * Then populate: + * + * * &load_info->index.vers + * * &load_info->index.vers_ext_crc + * * &load_info->index.vers_ext_names + * + * if present. + * + * Return: %0 if valid, %-ENOEXEC on failure. + */ +static int elf_validity_cache_index_versions(struct load_info *info, int flags) +{ + unsigned int vers_ext_crc; + unsigned int vers_ext_name; + size_t crc_count; + size_t remaining_len; + size_t name_size; + char *name; + + /* If modversions were suppressed, pretend we didn't find any */ + if (flags & MODULE_INIT_IGNORE_MODVERSIONS) { + info->index.vers = 0; + info->index.vers_ext_crc = 0; + info->index.vers_ext_name = 0; + return 0; + } + + vers_ext_crc = find_sec(info, "__version_ext_crcs"); + vers_ext_name = find_sec(info, "__version_ext_names"); + + /* If we have one field, we must have the other */ + if (!!vers_ext_crc != !!vers_ext_name) { + pr_err("extended version crc+name presence does not match"); + return -ENOEXEC; + } + + /* + * If we have extended version information, we should have the same + * number of entries in every section. + */ + if (vers_ext_crc) { + crc_count = info->sechdrs[vers_ext_crc].sh_size / sizeof(s32); + name = (void *)info->hdr + + info->sechdrs[vers_ext_name].sh_offset; + remaining_len = info->sechdrs[vers_ext_name].sh_size; + + while (crc_count--) { + name_size = strnlen(name, remaining_len) + 1; + if (name_size > remaining_len) { + pr_err("more extended version crcs than names"); + return -ENOEXEC; + } + remaining_len -= name_size; + name += name_size; + } + } + + info->index.vers = find_sec(info, "__versions"); + info->index.vers_ext_crc = vers_ext_crc; + info->index.vers_ext_name = vers_ext_name; + return 0; +} + /** * elf_validity_cache_index() - Resolve, validate, cache section indices * @info: Load info to read from and update. @@ -2035,9 +2111,7 @@ static int elf_validity_cache_index_str(struct load_info *info) * * elf_validity_cache_index_mod() * * elf_validity_cache_index_sym() * * elf_validity_cache_index_str() - * - * If versioning is not suppressed via flags, load the version index from - * a section called "__versions" with no validation. + * * elf_validity_cache_index_versions() * * If CONFIG_SMP is enabled, load the percpu section by name with no * validation. @@ -2060,11 +2134,9 @@ static int elf_validity_cache_index(struct load_info *info, int flags) err = elf_validity_cache_index_str(info); if (err < 0) return err; - - if (flags & MODULE_INIT_IGNORE_MODVERSIONS) - info->index.vers = 0; /* Pretend no __versions section! */ - else - info->index.vers = find_sec(info, "__versions"); + err = elf_validity_cache_index_versions(info, flags); + if (err < 0) + return err; info->index.pcpu = find_pcpusec(info); @@ -2291,6 +2363,10 @@ static int rewrite_section_headers(struct load_info *info, int flags) /* Track but don't keep modinfo and version sections. */ info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC; + info->sechdrs[info->index.vers_ext_crc].sh_flags &= + ~(unsigned long)SHF_ALLOC; + info->sechdrs[info->index.vers_ext_name].sh_flags &= + ~(unsigned long)SHF_ALLOC; info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC; return 0; diff --git a/kernel/module/version.c b/kernel/module/version.c index 53f43ac5a73e..02d8340bdb57 100644 --- a/kernel/module/version.c +++ b/kernel/module/version.c @@ -19,11 +19,28 @@ int check_version(const struct load_info *info, 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; @@ -87,6 +104,32 @@ int same_magic(const char *amagic, const char *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; + + start->crc = (const s32 *)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. diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 973b5e5ae2dd..530890bf5733 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1910,15 +1910,46 @@ static void add_versions(struct buffer *b, struct module *mod) continue; } if (strlen(s->name) >= MODULE_NAME_LEN) { - error("too long symbol \"%s\" [%s.ko]\n", - s->name, mod->name); - break; + /* this symbol will only be in the extended info */ + continue; } buf_printf(b, "\t{ %#8x, \"%s\" },\n", s->crc, s->name); } buf_printf(b, "};\n"); + + buf_printf(b, "static const s32 ____version_ext_crcs[]\n"); + buf_printf(b, "__used __section(\"__version_ext_crcs\") = {\n"); + list_for_each_entry(s, &mod->unresolved_symbols, list) { + if (!s->module) + continue; + if (!s->crc_valid) { + /* + * We already warned on this when producing the legacy + * modversions table. + */ + continue; + } + buf_printf(b, "\t%#8x,\n", s->crc); + } + buf_printf(b, "};\n"); + + buf_printf(b, "static const char ____version_ext_names[]\n"); + buf_printf(b, "__used __section(\"__version_ext_names\") =\n"); + list_for_each_entry(s, &mod->unresolved_symbols, list) { + if (!s->module) + continue; + if (!s->crc_valid) { + /* + * We already warned on this when producing the legacy + * modversions table. + */ + continue; + } + buf_printf(b, "\t\"%s\\0\"\n", s->name); + } + buf_printf(b, ";\n"); } static void add_depends(struct buffer *b, struct module *mod) -- 2.43.0.rc0.421.g78406f8d94-goog