From nobody Fri Dec 19 06:02:40 2025 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7355A1FA8C3 for ; Fri, 3 Jan 2025 17:37:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735925829; cv=none; b=WQfXR0hNaCzEnfj6xFK15V8q959CbaDH+tyBj70Q4fYKvf4+6Dcx7pkNVqcYkyRRQCCR6TXpCaCi0xCzGJGjjll7hIJcCMegOTTSjhoVdUcW4+KohnG6nS7V1Vxumo9XiN3tfBSdec3PmdYwTrMhI1MbipzEPERPDGaqm1YJV5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735925829; c=relaxed/simple; bh=0aoDYK4UYL8q/P6ld9tvR3N3YXNURQ74ijUSZOvJ4Lo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Mqw9DHZhi/BBITHGxD1pzdlmW+95DZT7oeEMF/e8QDu66MJZXEPO++7CaOKM9OvGRpAxq1oa2eIRhwWf6+N4BVcd0DTBpEogknPd9D/Lco87jg57rOkywekIXQVkNCmrQ0+jDAOOnSgo2BhXdZoM+5Hpu7yyqOlrvE1KthZa2Qs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ZLadcTwO; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--mmaurer.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ZLadcTwO" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-2f2a9f056a8so17448127a91.2 for ; Fri, 03 Jan 2025 09:37:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1735925826; x=1736530626; 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=pPAOcA/TjQgsnedeqz3UzFervLIillmWDYW0041VrHk=; b=ZLadcTwO0UKzs8Yege6IsA2V9PrzXNW0frrwnuqekTGDXtCLbAkSbXsTyZmlRN+Xnz cDZ3CwZYKOFAk/Y+v1mGgomlcBQIkfUAw8VPAQGYuJOXqaiAEEm7FA2sFRIlH8fYZgKg SSxZ+a9SisalKpORNg1MInUyIpPGlAk/HwIXGx7Be4DQ/5IcwtD3ixt/5TQuie11wHCM FG+wcXCIUb0zRZr3Bp7M/3ZF+3Ih1a5ydaXgmnGlXQ0MT3lax9R7IpldCZFZHurXTMz5 lxzig0wXDuVdPNSTPE+UdBUj7TuwGAGwK65q8/r/mc+KxY0sOSTh1yUIl15DYgrqWShs 0bHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1735925826; x=1736530626; 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=pPAOcA/TjQgsnedeqz3UzFervLIillmWDYW0041VrHk=; b=ORRG4LrHJAXoeQUgbgZRgAc5lCg7bUI5hY5g89k8HniDMZZkUB5jz7I7uNjT56WF0y W3Ka7qluDne/pEANU7Z6Zg70jElVqKmKAqaWfcBOe5K/XiM6VsyGyeLl0/SqKHU2BdNO LS7b9QEULqkOlB7fuCBHPf4vmJdit83kB1rZLFeh4WX10DCpJKydLLs0eX9HM+qOSI/F JyAPU6QiAeMbFQa1FW1/Qlgyodx2FTrsnKkbYjAt3LZlflrfbdhTF9zJjg9VMPgpG8IZ Ulj+0qXSrz3wOnPD1d046wWJG5n/YwQqhPNlWyAzloVbdsUR2G/Q0LYBa9GW/NZpOfrL JUng== X-Forwarded-Encrypted: i=1; AJvYcCXm5A+fKJE/QWUGcOmpseLoYX/46xvafLxPG5S84E5X1vxdmN04OyFn0+FCK3eezX3gm4tRIbyI5SjlNp8=@vger.kernel.org X-Gm-Message-State: AOJu0YxLd812PhyCyf/eiPDsq8DcicbKlwHVZGfPmzOG6/DwY0pUJsd0 vpB5I2Tkuw+a1py+ClFrRbrufOKFt3C3j7BjC2jK9zOXwQ9o3H3Q4PjGk6ATV+s050RdUiuKduX iKOkIFQ== X-Google-Smtp-Source: AGHT+IH4G9IGPCYS/DiDBgzOO4/p1Rg5UGQ6q71bGYKHVkeps/o1apPm0LKY4O+levY2mVbyNljVC8HHrWMu X-Received: from pjbqo13.prod.google.com ([2002:a17:90b:3dcd:b0:2d8:8340:8e46]) (user=mmaurer job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90a:d648:b0:2ee:bc1d:f98b with SMTP id 98e67ed59e1d1-2f452f013fbmr62331627a91.31.1735925826595; Fri, 03 Jan 2025 09:37:06 -0800 (PST) Date: Fri, 03 Jan 2025 17:37:01 +0000 In-Reply-To: <20250103-extended-modversions-v13-0-35d87c65ee04@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20250103-extended-modversions-v13-0-35d87c65ee04@google.com> X-Mailer: b4 0.15-dev Message-ID: <20250103-extended-modversions-v13-1-35d87c65ee04@google.com> Subject: [PATCH v13 1/5] modules: Support extended MODVERSIONS info From: Matthew Maurer To: Michael Ellerman , Nicholas Piggin , Christophe Leroy , Naveen N Rao , Madhavan Srinivasan , Luis Chamberlain , Petr Pavlu , Sami Tolvanen , Daniel Gomez , Masahiro Yamada , Nathan Chancellor , Nicolas Schier , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Jonathan Corbet Cc: 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, linux-doc@vger.kernel.org, Matthew Maurer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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. Any new fields will be ignored by old user tooling, unlike the current format where user tooling cannot tolerate adjustments to the format (for example making the name field longer). 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. Reviewed-by: Sami Tolvanen Signed-off-by: Matthew Maurer --- arch/powerpc/kernel/module_64.c | 24 ++++++++++- kernel/module/internal.h | 11 +++++ kernel/module/main.c | 92 +++++++++++++++++++++++++++++++++++++= ---- kernel/module/version.c | 45 ++++++++++++++++++++ 4 files changed, 162 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_6= 4.c index 45dac7b46aa3cdcb2058a2320b88c0d67e5586b3..34a5aec4908fba3b91a02e91426= 4cb525918942a 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -369,6 +369,24 @@ static void dedotify_versions(struct modversion_info *= vers, } } =20 +/* Same as normal versions, remove a leading dot if present. */ +static void dedotify_ext_version_names(char *str_seq, unsigned long size) +{ + unsigned long out =3D 0; + unsigned long in; + char last =3D '\0'; + + for (in =3D 0; in < size; in++) { + /* Skip one leading dot */ + if (last =3D=3D '\0' && str_seq[in] =3D=3D '.') + in++; + last =3D str_seq[in]; + str_seq[out++] =3D last; + } + /* Zero the trailing portion of the names table for robustness */ + memset(&str_seq[out], 0, size - out); +} + /* * Undefined symbols which refer to .funcname, hack to funcname. Make .TOC. * seem to be defined (value set later). @@ -438,10 +456,12 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, me->arch.toc_section =3D i; if (sechdrs[i].sh_addralign < 8) sechdrs[i].sh_addralign =3D 8; - } - else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")=3D=3D0) + } else if (strcmp(secstrings + sechdrs[i].sh_name, "__versions") =3D=3D = 0) dedotify_versions((void *)hdr + sechdrs[i].sh_offset, sechdrs[i].sh_size); + else if (strcmp(secstrings + sechdrs[i].sh_name, "__version_ext_names") = =3D=3D 0) + dedotify_ext_version_names((void *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size); =20 if (sechdrs[i].sh_type =3D=3D SHT_SYMTAB) dedotify((void *)hdr + sechdrs[i].sh_offset, diff --git a/kernel/module/internal.h b/kernel/module/internal.h index f10dc3ea7ff883b1c91e036b427cdb90502933b8..887838589020d95f8ca5e9751e6= 6e1c73af34db2 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; }; =20 @@ -389,6 +391,15 @@ void module_layout(struct module *mod, struct modversi= on_info *ver, struct kerne struct kernel_symbol *ks, struct tracepoint * const *tp); int check_modstruct_version(const struct load_info *info, struct module *m= od); int same_magic(const char *amagic, const char *bmagic, bool has_crcs); +struct modversion_info_ext { + size_t remaining; + const u32 *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 59ea20102b400b5acd68347f61eef48a677c77ce..6333af32ee652011f6623739f3d= b95f25a26c305 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2073,6 +2073,82 @@ static int elf_validity_cache_index_str(struct load_= info *info) return 0; } =20 +/** + * 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 popula= ted. + * @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 f= lags) +{ + 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 =3D 0; + info->index.vers_ext_crc =3D 0; + info->index.vers_ext_name =3D 0; + return 0; + } + + vers_ext_crc =3D find_sec(info, "__version_ext_crcs"); + vers_ext_name =3D find_sec(info, "__version_ext_names"); + + /* If we have one field, we must have the other */ + if (!!vers_ext_crc !=3D !!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 =3D info->sechdrs[vers_ext_crc].sh_size / sizeof(u32); + name =3D (void *)info->hdr + + info->sechdrs[vers_ext_name].sh_offset; + remaining_len =3D info->sechdrs[vers_ext_name].sh_size; + + while (crc_count--) { + name_size =3D strnlen(name, remaining_len) + 1; + if (name_size > remaining_len) { + pr_err("more extended version crcs than names"); + return -ENOEXEC; + } + remaining_len -=3D name_size; + name +=3D name_size; + } + } + + info->index.vers =3D find_sec(info, "__versions"); + info->index.vers_ext_crc =3D vers_ext_crc; + info->index.vers_ext_name =3D vers_ext_name; + return 0; +} + /** * elf_validity_cache_index() - Resolve, validate, cache section indices * @info: Load info to read from and update. @@ -2087,9 +2163,7 @@ static int elf_validity_cache_index_str(struct load_i= nfo *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. @@ -2112,11 +2186,9 @@ static int elf_validity_cache_index(struct load_info= *info, int flags) err =3D elf_validity_cache_index_str(info); if (err < 0) return err; - - if (flags & MODULE_INIT_IGNORE_MODVERSIONS) - info->index.vers =3D 0; /* Pretend no __versions section! */ - else - info->index.vers =3D find_sec(info, "__versions"); + err =3D elf_validity_cache_index_versions(info, flags); + if (err < 0) + return err; =20 info->index.pcpu =3D find_pcpusec(info); =20 @@ -2327,6 +2399,10 @@ static int rewrite_section_headers(struct load_info = *info, int flags) =20 /* Track but don't keep modinfo and version sections. */ info->sechdrs[info->index.vers].sh_flags &=3D ~(unsigned long)SHF_ALLOC; + info->sechdrs[info->index.vers_ext_crc].sh_flags &=3D + ~(unsigned long)SHF_ALLOC; + info->sechdrs[info->index.vers_ext_name].sh_flags &=3D + ~(unsigned long)SHF_ALLOC; info->sechdrs[info->index.info].sh_flags &=3D ~(unsigned long)SHF_ALLOC; =20 return 0; diff --git a/kernel/module/version.c b/kernel/module/version.c index 4e5731d403af20251cb2e3d6dbff8bc59109732b..3718a886832198504ebfd9b72e7= 7005efd3d3d21 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 =3D info->index.vers; unsigned int i, num_versions; struct modversion_info *versions; + struct modversion_info_ext version_ext; =20 /* Exporting module didn't supply crcs? OK, we're already tainted. */ if (!crc) return 1; =20 + /* 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) !=3D 0) + continue; + if (*version_ext.crc =3D=3D *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 =3D=3D 0) return try_to_force_load(mod, symname) =3D=3D 0; @@ -87,6 +104,34 @@ int same_magic(const char *amagic, const char *bmagic, return strcmp(amagic, bmagic) =3D=3D 0; } =20 +void modversion_ext_start(const struct load_info *info, + struct modversion_info_ext *start) +{ + unsigned int crc_idx =3D info->index.vers_ext_crc; + unsigned int name_idx =3D info->index.vers_ext_name; + Elf_Shdr *sechdrs =3D 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 =3D=3D 0 || name_idx =3D=3D 0) { + start->remaining =3D 0; + return; + } + + start->crc =3D (const u32 *)sechdrs[crc_idx].sh_addr; + start->name =3D (const char *)sechdrs[name_idx].sh_addr; + start->remaining =3D sechdrs[crc_idx].sh_size / sizeof(*start->crc); +} + +void modversion_ext_advance(struct modversion_info_ext *vers) +{ + vers->remaining--; + vers->crc++; + vers->name +=3D 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. --=20 2.47.1.613.gc27f4b7a9f-goog