public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/17] module: Introduce hash-based integrity checking
@ 2026-01-13 12:28 Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 01/17] module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y Thomas Weißschuh
                   ` (17 more replies)
  0 siblings, 18 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh, Coiby Xu,
	kernel test robot

The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.

The goal is to reach bit-for-bit reproducibility. Excluding certain
parts of the build output from the reproducibility analysis would be
error-prone and force each downstream consumer to introduce new tooling.

Introduce a new mechanism to ensure only well-known modules are loaded
by embedding a merkle tree root of all modules built as part of the full
kernel build into vmlinux.

Interest has been proclaimed by NixOS, Arch Linux, Proxmox, SUSE and the
general reproducible builds community.

Compatibility with IMA modsig is not provided yet. It is still unclear
to me if it should be hooked up transparently without any changes to the
policy or it should require new policy options.

Further improvements:
* Use MODULE_SIG_HASH for configuration
* UAPI for discovery?

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
Changes in v4:
- Use as Merkle tree over a linera list of hashes.
- Provide compatibilith with INSTALL_MOD_STRIP
- Rework commit messages.
- Use vmlinux.unstripped over plain "vmlinux".
- Link to v3: https://lore.kernel.org/r/20250429-module-hashes-v3-0-00e9258def9e@weissschuh.net

Changes in v3:
- Rebase on v6.15-rc1
- Use openssl to calculate hash
- Avoid warning if no modules are built
- Simplify module_integrity_check() a bit
- Make incompatibility with INSTALL_MOD_STRIP explicit
- Update docs
- Add IMA cleanups
- Link to v2: https://lore.kernel.org/r/20250120-module-hashes-v2-0-ba1184e27b7f@weissschuh.net

Changes in v2:
- Drop RFC state
- Mention interested parties in cover letter
- Expand Kconfig description
- Add compatibility with CONFIG_MODULE_SIG
- Parallelize module-hashes.sh
- Update Documentation/kbuild/reproducible-builds.rst
- Link to v1: https://lore.kernel.org/r/20241225-module-hashes-v1-0-d710ce7a3fd1@weissschuh.net

---
Coiby Xu (1):
      module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y

Thomas Weißschuh (16):
      powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
      ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
      module: Make mod_verify_sig() static
      module: Switch load_info::len to size_t
      kbuild: add stamp file for vmlinux BTF data
      kbuild: generate module BTF based on vmlinux.unstripped
      module: Deduplicate signature extraction
      module: Make module loading policy usable without MODULE_SIG
      module: Move integrity checks into dedicated function
      module: Move lockdown check into generic module loader
      module: Move signature splitting up
      module: Report signature type to users
      lockdown: Make the relationship to MODULE_SIG a dependency
      module: Introduce hash-based integrity checking
      kbuild: move handling of module stripping to Makefile.lib
      kbuild: make CONFIG_MODULE_HASHES compatible with module stripping

 .gitignore                                   |   2 +
 Documentation/kbuild/reproducible-builds.rst |   5 +-
 Makefile                                     |   8 +-
 arch/powerpc/kernel/ima_arch.c               |   3 +-
 include/asm-generic/vmlinux.lds.h            |  11 +
 include/linux/module.h                       |  20 +-
 include/linux/module_hashes.h                |  25 ++
 include/linux/module_signature.h             |   5 +-
 kernel/module/Kconfig                        |  29 +-
 kernel/module/Makefile                       |   1 +
 kernel/module/hashes.c                       |  92 ++++++
 kernel/module/hashes_root.c                  |   6 +
 kernel/module/internal.h                     |  13 +-
 kernel/module/main.c                         |  68 +++-
 kernel/module/signing.c                      |  83 +----
 kernel/module_signature.c                    |  49 ++-
 scripts/.gitignore                           |   1 +
 scripts/Makefile                             |   3 +
 scripts/Makefile.lib                         |  32 ++
 scripts/Makefile.modfinal                    |  28 +-
 scripts/Makefile.modinst                     |  46 +--
 scripts/Makefile.vmlinux                     |   6 +
 scripts/link-vmlinux.sh                      |  20 +-
 scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
 security/integrity/ima/ima_efi.c             |   6 +-
 security/integrity/ima/ima_modsig.c          |  28 +-
 security/lockdown/Kconfig                    |   2 +-
 27 files changed, 884 insertions(+), 175 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20241225-module-hashes-7a50a7cc2a30

Best regards,
-- 
Thomas Weißschuh <linux@weissschuh.net>


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 01/17] module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh, Coiby Xu,
	kernel test robot

From: Coiby Xu <coxu@redhat.com>

Currently if set_module_sig_enforced is called with CONFIG_MODULE_SIG=n
e.g. [1], it can lead to a linking error,

    ld: security/integrity/ima/ima_appraise.o: in function `ima_appraise_measurement':
    security/integrity/ima/ima_appraise.c:587:(.text+0xbbb): undefined reference to `set_module_sig_enforced'

This happens because the actual implementation of
set_module_sig_enforced comes from CONFIG_MODULE_SIG but both the
function declaration and the empty stub definition are tied to
CONFIG_MODULES.

So bind set_module_sig_enforced to CONFIG_MODULE_SIG instead. This
allows (future) users to call set_module_sig_enforced directly without
the "if IS_ENABLED(CONFIG_MODULE_SIG)" safeguard.

Note this issue hasn't caused a real problem because all current callers
of set_module_sig_enforced e.g. security/integrity/ima/ima_efi.c
use "if IS_ENABLED(CONFIG_MODULE_SIG)" safeguard.

[1] https://lore.kernel.org/lkml/20250928030358.3873311-1-coxu@redhat.com/

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202510030029.VRKgik99-lkp@intel.com/
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
Reviewed-by: Daniel Gomez <da.gomez@samsung.com>
Signed-off-by: Coiby Xu <coxu@redhat.com>
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>

---
From modules/modules-next
---
 include/linux/module.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index d80c3ea57472..f288ca5cd95b 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -770,8 +770,6 @@ static inline bool is_livepatch_module(struct module *mod)
 #endif
 }
 
-void set_module_sig_enforced(void);
-
 void module_for_each_mod(int(*func)(struct module *mod, void *data), void *data);
 
 #else /* !CONFIG_MODULES... */
@@ -866,10 +864,6 @@ static inline bool module_requested_async_probing(struct module *module)
 }
 
 
-static inline void set_module_sig_enforced(void)
-{
-}
-
 /* Dereference module function descriptor */
 static inline
 void *dereference_module_function_descriptor(struct module *mod, void *ptr)
@@ -925,6 +919,8 @@ static inline bool retpoline_module_ok(bool has_retpoline)
 #ifdef CONFIG_MODULE_SIG
 bool is_module_sig_enforced(void);
 
+void set_module_sig_enforced(void);
+
 static inline bool module_sig_ok(struct module *module)
 {
 	return module->sig_ok;
@@ -935,6 +931,10 @@ static inline bool is_module_sig_enforced(void)
 	return false;
 }
 
+static inline void set_module_sig_enforced(void)
+{
+}
+
 static inline bool module_sig_ok(struct module *module)
 {
 	return true;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 01/17] module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-30 20:43   ` Aaron Tomlin
                     ` (2 more replies)
  2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
                   ` (15 subsequent siblings)
  17 siblings, 3 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

When CONFIG_MODULE_SIG is disabled set_module_sig_enforced() is defined
as an empty stub, so the check is unnecessary.
The specific configuration option for set_module_sig_enforced() is
about to change and removing the check avoids some later churn.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 arch/powerpc/kernel/ima_arch.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/ima_arch.c b/arch/powerpc/kernel/ima_arch.c
index b7029beed847..690263bf4265 100644
--- a/arch/powerpc/kernel/ima_arch.c
+++ b/arch/powerpc/kernel/ima_arch.c
@@ -63,8 +63,7 @@ static const char *const secure_and_trusted_rules[] = {
 const char *const *arch_get_ima_policy(void)
 {
 	if (is_ppc_secureboot_enabled()) {
-		if (IS_ENABLED(CONFIG_MODULE_SIG))
-			set_module_sig_enforced();
+		set_module_sig_enforced();
 
 		if (is_ppc_trustedboot_enabled())
 			return secure_and_trusted_rules;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 01/17] module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-30 20:49   ` Aaron Tomlin
                     ` (2 more replies)
  2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
                   ` (14 subsequent siblings)
  17 siblings, 3 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

When configuration settings are disabled the guarded functions are
defined as empty stubs, so the check is unnecessary.
The specific configuration option for set_module_sig_enforced() is
about to change and removing the checks avoids some later churn.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 security/integrity/ima/ima_efi.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
index 138029bfcce1..a35dd166ad47 100644
--- a/security/integrity/ima/ima_efi.c
+++ b/security/integrity/ima/ima_efi.c
@@ -68,10 +68,8 @@ static const char * const sb_arch_rules[] = {
 const char * const *arch_get_ima_policy(void)
 {
 	if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
-		if (IS_ENABLED(CONFIG_MODULE_SIG))
-			set_module_sig_enforced();
-		if (IS_ENABLED(CONFIG_KEXEC_SIG))
-			set_kexec_sig_enforced();
+		set_module_sig_enforced();
+		set_kexec_sig_enforced();
 		return sb_arch_rules;
 	}
 	return NULL;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 04/17] module: Make mod_verify_sig() static
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (2 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-30 20:53   ` Aaron Tomlin
                     ` (2 more replies)
  2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
                   ` (13 subsequent siblings)
  17 siblings, 3 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

It is not used outside of signing.c.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 kernel/module/internal.h | 1 -
 kernel/module/signing.c  | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index 618202578b42..e68fbcd60c35 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -119,7 +119,6 @@ struct module_use {
 	struct module *source, *target;
 };
 
-int mod_verify_sig(const void *mod, struct load_info *info);
 int try_to_force_load(struct module *mod, const char *reason);
 bool find_symbol(struct find_symbol_arg *fsa);
 struct module *find_module_all(const char *name, size_t len, bool even_unformed);
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
index a2ff4242e623..fe3f51ac6199 100644
--- a/kernel/module/signing.c
+++ b/kernel/module/signing.c
@@ -40,7 +40,7 @@ void set_module_sig_enforced(void)
 /*
  * Verify the signature on a module.
  */
-int mod_verify_sig(const void *mod, struct load_info *info)
+static int mod_verify_sig(const void *mod, struct load_info *info)
 {
 	struct module_signature ms;
 	size_t sig_len, modlen = info->len;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (3 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-06  8:18   ` David Howells
                     ` (2 more replies)
  2026-01-13 12:28 ` [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data Thomas Weißschuh
                   ` (12 subsequent siblings)
  17 siblings, 3 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

Switching the types will make some later changes cleaner.
size_t is also the semantically correct type for this field.

As both 'size_t' and 'unsigned int' are always the same size, this
should be risk-free.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 kernel/module/internal.h | 2 +-
 kernel/module/main.c     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index e68fbcd60c35..037fbb3b7168 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -66,7 +66,7 @@ struct load_info {
 	/* pointer to module in temporary copy, freed at end of load_module() */
 	struct module *mod;
 	Elf_Ehdr *hdr;
-	unsigned long len;
+	size_t len;
 	Elf_Shdr *sechdrs;
 	char *secstrings, *strtab;
 	unsigned long symoffs, stroffs, init_typeoffs, core_typeoffs;
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 710ee30b3bea..a88f95a13e06 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1838,7 +1838,7 @@ static int validate_section_offset(const struct load_info *info, Elf_Shdr *shdr)
 static int elf_validity_ehdr(const struct load_info *info)
 {
 	if (info->len < sizeof(*(info->hdr))) {
-		pr_err("Invalid ELF header len %lu\n", info->len);
+		pr_err("Invalid ELF header len %zu\n", info->len);
 		return -ENOEXEC;
 	}
 	if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0) {

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (4 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-06 16:28   ` Nicolas Schier
  2026-03-10 21:36   ` Eric Biggers
  2026-01-13 12:28 ` [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped Thomas Weißschuh
                   ` (11 subsequent siblings)
  17 siblings, 2 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The upcoming module hashes functionality will build the modules in
between the generation of the BTF data and the final link of vmlinux.
Having a dependency from the modules on vmlinux would make this
impossible as it would mean having a cyclic dependency.
Break this cyclic dependency by introducing a new target.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 scripts/Makefile.modfinal | 4 ++--
 scripts/link-vmlinux.sh   | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 149e12ff5700..adfef1e002a9 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -56,8 +56,8 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
 	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
 
 # Re-generate module BTFs if either module's .ko or vmlinux changed
-%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
-	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
+%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/.tmp_vmlinux_btf.stamp) FORCE
+	+$(call if_changed_except,ld_ko_o,$(objtree)/.tmp_vmlinux_btf.stamp)
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 	+$(if $(newer-prereqs),$(call cmd,btf_ko))
 endif
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 4ab44c73da4d..8c98f8645a5c 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -111,6 +111,7 @@ vmlinux_link()
 gen_btf()
 {
 	local btf_data=${1}.btf.o
+	local btf_stamp=.tmp_vmlinux_btf.stamp
 
 	info BTF "${btf_data}"
 	LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
@@ -131,6 +132,11 @@ gen_btf()
 	fi
 	printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
 
+	info STAMP $btf_stamp
+	if ! cmp --silent $btf_data $btf_stamp; then
+		cp $btf_data $btf_stamp
+	fi
+
 	btf_vmlinux_bin_o=${btf_data}
 }
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (5 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-06 16:37   ` Nicolas Schier
  2026-02-20  9:29   ` Fwd: " Thomas Weißschuh
  2026-01-13 12:28 ` [PATCH v4 08/17] module: Deduplicate signature extraction Thomas Weißschuh
                   ` (10 subsequent siblings)
  17 siblings, 2 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The upcoming module hashes functionality will build the modules in
between the generation of the BTF data and the final link of vmlinux.
At this point vmlinux is not yet built and therefore can't be used for
module BTF generation. vmlinux.unstripped however is usable and
sufficient for BTF generation.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 scripts/Makefile.modfinal | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index adfef1e002a9..930db0524a0a 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -40,11 +40,11 @@ quiet_cmd_ld_ko_o = LD [M]  $@
 
 quiet_cmd_btf_ko = BTF [M] $@
       cmd_btf_ko = 							\
-	if [ ! -f $(objtree)/vmlinux ]; then				\
-		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
+	if [ ! -f $(objtree)/vmlinux.unstripped ]; then			\
+		printf "Skipping BTF generation for %s due to unavailability of vmlinux.unstripped\n" $@ 1>&2; \
 	else								\
-		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
-		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@;		\
+		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux.unstripped $@; \
+		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux.unstripped $@;	\
 	fi;
 
 # Same as newer-prereqs, but allows to exclude specified extra dependencies

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 08/17] module: Deduplicate signature extraction
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (6 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-27 15:20   ` Petr Pavlu
  2026-01-13 12:28 ` [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG Thomas Weißschuh
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The logic to extract the signature bits from a module file are
duplicated between the module core and IMA modsig appraisal.

Unify the implementation.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 include/linux/module_signature.h    |  4 +--
 kernel/module/signing.c             | 52 +++++++------------------------------
 kernel/module_signature.c           | 41 +++++++++++++++++++++++++++--
 security/integrity/ima/ima_modsig.c | 24 ++++-------------
 4 files changed, 56 insertions(+), 65 deletions(-)

diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
index 7eb4b00381ac..186a55effa30 100644
--- a/include/linux/module_signature.h
+++ b/include/linux/module_signature.h
@@ -40,7 +40,7 @@ struct module_signature {
 	__be32	sig_len;	/* Length of signature data */
 };
 
-int mod_check_sig(const struct module_signature *ms, size_t file_len,
-		  const char *name);
+int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
+		  size_t *sig_len, const u8 **sig, const char *name);
 
 #endif /* _LINUX_MODULE_SIGNATURE_H */
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
index fe3f51ac6199..6d64c0d18d0a 100644
--- a/kernel/module/signing.c
+++ b/kernel/module/signing.c
@@ -37,54 +37,22 @@ void set_module_sig_enforced(void)
 	sig_enforce = true;
 }
 
-/*
- * Verify the signature on a module.
- */
-static int mod_verify_sig(const void *mod, struct load_info *info)
-{
-	struct module_signature ms;
-	size_t sig_len, modlen = info->len;
-	int ret;
-
-	pr_devel("==>%s(,%zu)\n", __func__, modlen);
-
-	if (modlen <= sizeof(ms))
-		return -EBADMSG;
-
-	memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
-
-	ret = mod_check_sig(&ms, modlen, "module");
-	if (ret)
-		return ret;
-
-	sig_len = be32_to_cpu(ms.sig_len);
-	modlen -= sig_len + sizeof(ms);
-	info->len = modlen;
-
-	return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
-				      VERIFY_USE_SECONDARY_KEYRING,
-				      VERIFYING_MODULE_SIGNATURE,
-				      NULL, NULL);
-}
-
 int module_sig_check(struct load_info *info, int flags)
 {
-	int err = -ENODATA;
-	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
+	int err;
 	const char *reason;
 	const void *mod = info->hdr;
+	size_t sig_len;
+	const u8 *sig;
 	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
 				       MODULE_INIT_IGNORE_VERMAGIC);
-	/*
-	 * Do not allow mangled modules as a module with version information
-	 * removed is no longer the module that was signed.
-	 */
-	if (!mangled_module &&
-	    info->len > markerlen &&
-	    memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
-		/* We truncate the module to discard the signature */
-		info->len -= markerlen;
-		err = mod_verify_sig(mod, info);
+
+	err = mod_split_sig(info->hdr, &info->len, mangled_module, &sig_len, &sig, "module");
+	if (!err) {
+		err = verify_pkcs7_signature(mod, info->len, sig, sig_len,
+					     VERIFY_USE_SECONDARY_KEYRING,
+					     VERIFYING_MODULE_SIGNATURE,
+					     NULL, NULL);
 		if (!err) {
 			info->sig_ok = true;
 			return 0;
diff --git a/kernel/module_signature.c b/kernel/module_signature.c
index 00132d12487c..b2384a73524c 100644
--- a/kernel/module_signature.c
+++ b/kernel/module_signature.c
@@ -8,6 +8,7 @@
 
 #include <linux/errno.h>
 #include <linux/printk.h>
+#include <linux/string.h>
 #include <linux/module_signature.h>
 #include <asm/byteorder.h>
 
@@ -18,8 +19,8 @@
  * @file_len:	Size of the file to which @ms is appended.
  * @name:	What is being checked. Used for error messages.
  */
-int mod_check_sig(const struct module_signature *ms, size_t file_len,
-		  const char *name)
+static int mod_check_sig(const struct module_signature *ms, size_t file_len,
+			 const char *name)
 {
 	if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
 		return -EBADMSG;
@@ -44,3 +45,39 @@ int mod_check_sig(const struct module_signature *ms, size_t file_len,
 
 	return 0;
 }
+
+int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
+		  size_t *sig_len, const u8 **sig, const char *name)
+{
+	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
+	struct module_signature ms;
+	size_t modlen = *buf_len;
+	int ret;
+
+	/*
+	 * Do not allow mangled modules as a module with version information
+	 * removed is no longer the module that was signed.
+	 */
+	if (!mangled &&
+	    *buf_len > markerlen &&
+	    memcmp(buf + modlen - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
+		/* We truncate the module to discard the signature */
+		modlen -= markerlen;
+	}
+
+	if (modlen <= sizeof(ms))
+		return -EBADMSG;
+
+	memcpy(&ms, buf + (modlen - sizeof(ms)), sizeof(ms));
+
+	ret = mod_check_sig(&ms, modlen, name);
+	if (ret)
+		return ret;
+
+	*sig_len = be32_to_cpu(ms.sig_len);
+	modlen -= *sig_len + sizeof(ms);
+	*buf_len = modlen;
+	*sig = buf + modlen;
+
+	return 0;
+}
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index 3265d744d5ce..a57342d39b07 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -40,44 +40,30 @@ struct modsig {
 int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
 		    struct modsig **modsig)
 {
-	const size_t marker_len = strlen(MODULE_SIG_STRING);
-	const struct module_signature *sig;
+	size_t buf_len_sz = buf_len;
 	struct modsig *hdr;
 	size_t sig_len;
-	const void *p;
+	const u8 *sig;
 	int rc;
 
-	if (buf_len <= marker_len + sizeof(*sig))
-		return -ENOENT;
-
-	p = buf + buf_len - marker_len;
-	if (memcmp(p, MODULE_SIG_STRING, marker_len))
-		return -ENOENT;
-
-	buf_len -= marker_len;
-	sig = (const struct module_signature *)(p - sizeof(*sig));
-
-	rc = mod_check_sig(sig, buf_len, func_tokens[func]);
+	rc = mod_split_sig(buf, &buf_len_sz, true, &sig_len, &sig, func_tokens[func]);
 	if (rc)
 		return rc;
 
-	sig_len = be32_to_cpu(sig->sig_len);
-	buf_len -= sig_len + sizeof(*sig);
-
 	/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
 	hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL);
 	if (!hdr)
 		return -ENOMEM;
 
 	hdr->raw_pkcs7_len = sig_len;
-	hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
+	hdr->pkcs7_msg = pkcs7_parse_message(sig, sig_len);
 	if (IS_ERR(hdr->pkcs7_msg)) {
 		rc = PTR_ERR(hdr->pkcs7_msg);
 		kfree(hdr);
 		return rc;
 	}
 
-	memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
+	memcpy(hdr->raw_pkcs7, sig, sig_len);
 
 	/* We don't know the hash algorithm yet. */
 	hdr->hash_algo = HASH_ALGO__LAST;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (7 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 08/17] module: Deduplicate signature extraction Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-03-10 22:01   ` Eric Biggers
  2026-01-13 12:28 ` [PATCH v4 10/17] module: Move integrity checks into dedicated function Thomas Weißschuh
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The loading policy functionality will also be used by the hash-based
module validation. Split it out from CONFIG_MODULE_SIG so it is usable
by both.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 include/linux/module.h  |  8 ++++----
 kernel/module/Kconfig   |  5 ++++-
 kernel/module/main.c    | 26 +++++++++++++++++++++++++-
 kernel/module/signing.c | 21 ---------------------
 4 files changed, 33 insertions(+), 27 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index f288ca5cd95b..f9601cba47cd 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -444,7 +444,7 @@ struct module {
 	const u32 *gpl_crcs;
 	bool using_gplonly_symbols;
 
-#ifdef CONFIG_MODULE_SIG
+#ifdef CONFIG_MODULE_SIG_POLICY
 	/* Signature was verified. */
 	bool sig_ok;
 #endif
@@ -916,7 +916,7 @@ static inline bool retpoline_module_ok(bool has_retpoline)
 }
 #endif
 
-#ifdef CONFIG_MODULE_SIG
+#ifdef CONFIG_MODULE_SIG_POLICY
 bool is_module_sig_enforced(void);
 
 void set_module_sig_enforced(void);
@@ -925,7 +925,7 @@ static inline bool module_sig_ok(struct module *module)
 {
 	return module->sig_ok;
 }
-#else	/* !CONFIG_MODULE_SIG */
+#else	/* !CONFIG_MODULE_SIG_POLICY */
 static inline bool is_module_sig_enforced(void)
 {
 	return false;
@@ -939,7 +939,7 @@ static inline bool module_sig_ok(struct module *module)
 {
 	return true;
 }
-#endif	/* CONFIG_MODULE_SIG */
+#endif	/* CONFIG_MODULE_SIG_POLICY */
 
 #if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS)
 int module_kallsyms_on_each_symbol(const char *modname,
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index e8bb2c9d917e..db3b61fb3e73 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -270,9 +270,12 @@ config MODULE_SIG
 	  debuginfo strip done by some packagers (such as rpmbuild) and
 	  inclusion into an initramfs that wants the module size reduced.
 
+config MODULE_SIG_POLICY
+	def_bool MODULE_SIG
+
 config MODULE_SIG_FORCE
 	bool "Require modules to be validly signed"
-	depends on MODULE_SIG
+	depends on MODULE_SIG_POLICY
 	help
 	  Reject unsigned modules or signed modules for which we don't have a
 	  key.  Without this, such modules will simply taint the kernel.
diff --git a/kernel/module/main.c b/kernel/module/main.c
index a88f95a13e06..4442397a9f92 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2541,7 +2541,7 @@ static void module_augment_kernel_taints(struct module *mod, struct load_info *i
 				mod->name);
 		add_taint_module(mod, TAINT_TEST, LOCKDEP_STILL_OK);
 	}
-#ifdef CONFIG_MODULE_SIG
+#ifdef CONFIG_MODULE_SIG_POLICY
 	mod->sig_ok = info->sig_ok;
 	if (!mod->sig_ok) {
 		pr_notice_once("%s: module verification failed: signature "
@@ -3921,3 +3921,27 @@ static int module_debugfs_init(void)
 }
 module_init(module_debugfs_init);
 #endif
+
+#ifdef CONFIG_MODULE_SIG_POLICY
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "module."
+
+static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
+module_param(sig_enforce, bool_enable_only, 0644);
+
+/*
+ * Export sig_enforce kernel cmdline parameter to allow other subsystems rely
+ * on that instead of directly to CONFIG_MODULE_SIG_FORCE config.
+ */
+bool is_module_sig_enforced(void)
+{
+	return sig_enforce;
+}
+EXPORT_SYMBOL(is_module_sig_enforced);
+
+void set_module_sig_enforced(void)
+{
+	sig_enforce = true;
+}
+#endif
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
index 6d64c0d18d0a..66d90784de89 100644
--- a/kernel/module/signing.c
+++ b/kernel/module/signing.c
@@ -16,27 +16,6 @@
 #include <uapi/linux/module.h>
 #include "internal.h"
 
-#undef MODULE_PARAM_PREFIX
-#define MODULE_PARAM_PREFIX "module."
-
-static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
-module_param(sig_enforce, bool_enable_only, 0644);
-
-/*
- * Export sig_enforce kernel cmdline parameter to allow other subsystems rely
- * on that instead of directly to CONFIG_MODULE_SIG_FORCE config.
- */
-bool is_module_sig_enforced(void)
-{
-	return sig_enforce;
-}
-EXPORT_SYMBOL(is_module_sig_enforced);
-
-void set_module_sig_enforced(void)
-{
-	sig_enforce = true;
-}
-
 int module_sig_check(struct load_info *info, int flags)
 {
 	int err;

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 10/17] module: Move integrity checks into dedicated function
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (8 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-13 15:09   ` Nicolas Schier
  2026-03-10 22:06   ` Eric Biggers
  2026-01-13 12:28 ` [PATCH v4 11/17] module: Move lockdown check into generic module loader Thomas Weißschuh
                   ` (7 subsequent siblings)
  17 siblings, 2 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

With the addition of hash-based integrity checking, the configuration
matrix is easier to represent in a dedicated function and with explicit
usage of IS_ENABLED().

Drop the now unnecessary stub for module_sig_check().

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 kernel/module/internal.h |  7 -------
 kernel/module/main.c     | 18 ++++++++++++++----
 2 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index 037fbb3b7168..e053c29a5d08 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -337,14 +337,7 @@ int module_enforce_rwx_sections(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
 void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
 			       const char *secstrings);
 
-#ifdef CONFIG_MODULE_SIG
 int module_sig_check(struct load_info *info, int flags);
-#else /* !CONFIG_MODULE_SIG */
-static inline int module_sig_check(struct load_info *info, int flags)
-{
-	return 0;
-}
-#endif /* !CONFIG_MODULE_SIG */
 
 #ifdef CONFIG_DEBUG_KMEMLEAK
 void kmemleak_load_module(const struct module *mod, const struct load_info *info);
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 4442397a9f92..9c570078aa9c 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3344,6 +3344,16 @@ static int early_mod_check(struct load_info *info, int flags)
 	return err;
 }
 
+static int module_integrity_check(struct load_info *info, int flags)
+{
+	int err = 0;
+
+	if (IS_ENABLED(CONFIG_MODULE_SIG))
+		err = module_sig_check(info, flags);
+
+	return err;
+}
+
 /*
  * Allocate and load the module: note that size of section 0 is always
  * zero, and we rely on this for optional sections.
@@ -3357,18 +3367,18 @@ static int load_module(struct load_info *info, const char __user *uargs,
 	char *after_dashes;
 
 	/*
-	 * Do the signature check (if any) first. All that
-	 * the signature check needs is info->len, it does
+	 * Do the integrity checks (if any) first. All that
+	 * they need 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.
+	 * we even get to the integrity 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);
+	err = module_integrity_check(info, flags);
 	if (err)
 		goto free_copy;
 

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 11/17] module: Move lockdown check into generic module loader
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (9 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 10/17] module: Move integrity checks into dedicated function Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-13 15:14   ` Nicolas Schier
  2026-01-13 12:28 ` [PATCH v4 12/17] module: Move signature splitting up Thomas Weißschuh
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The lockdown check buried in module_sig_check() will not compose well
with the introduction of hash-based module validation.
Move it into module_integrity_check() which will work better.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 kernel/module/main.c    | 6 +++++-
 kernel/module/signing.c | 3 +--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 9c570078aa9c..c09b25c0166a 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3351,7 +3351,11 @@ static int module_integrity_check(struct load_info *info, int flags)
 	if (IS_ENABLED(CONFIG_MODULE_SIG))
 		err = module_sig_check(info, flags);
 
-	return err;
+	if (err)
+		return err;
+	if (info->sig_ok)
+		return 0;
+	return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
 }
 
 /*
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
index 66d90784de89..8a5f66389116 100644
--- a/kernel/module/signing.c
+++ b/kernel/module/signing.c
@@ -11,7 +11,6 @@
 #include <linux/module_signature.h>
 #include <linux/string.h>
 #include <linux/verification.h>
-#include <linux/security.h>
 #include <crypto/public_key.h>
 #include <uapi/linux/module.h>
 #include "internal.h"
@@ -68,5 +67,5 @@ int module_sig_check(struct load_info *info, int flags)
 		return -EKEYREJECTED;
 	}
 
-	return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
+	return 0;
 }

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 12/17] module: Move signature splitting up
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (10 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 11/17] module: Move lockdown check into generic module loader Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-29 14:41   ` Petr Pavlu
  2026-01-13 12:28 ` [PATCH v4 13/17] module: Report signature type to users Thomas Weißschuh
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The signature splitting will also be used by CONFIG_MODULE_HASHES.

Move it up the callchain, so the result can be reused.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 kernel/module/internal.h |  2 +-
 kernel/module/main.c     | 13 ++++++++++++-
 kernel/module/signing.c  | 21 +++++++--------------
 3 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index e053c29a5d08..e2d49122c2a1 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -337,7 +337,7 @@ int module_enforce_rwx_sections(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
 void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
 			       const char *secstrings);
 
-int module_sig_check(struct load_info *info, int flags);
+int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
 
 #ifdef CONFIG_DEBUG_KMEMLEAK
 void kmemleak_load_module(const struct module *mod, const struct load_info *info);
diff --git a/kernel/module/main.c b/kernel/module/main.c
index c09b25c0166a..d65bc300a78c 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3346,10 +3346,21 @@ static int early_mod_check(struct load_info *info, int flags)
 
 static int module_integrity_check(struct load_info *info, int flags)
 {
+	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
+				       MODULE_INIT_IGNORE_VERMAGIC);
+	size_t sig_len;
+	const u8 *sig;
 	int err = 0;
 
+	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
+		err = mod_split_sig(info->hdr, &info->len, mangled_module,
+				    &sig_len, &sig, "module");
+		if (err)
+			return err;
+	}
+
 	if (IS_ENABLED(CONFIG_MODULE_SIG))
-		err = module_sig_check(info, flags);
+		err = module_sig_check(info, sig, sig_len);
 
 	if (err)
 		return err;
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
index 8a5f66389116..86164761cac7 100644
--- a/kernel/module/signing.c
+++ b/kernel/module/signing.c
@@ -15,26 +15,19 @@
 #include <uapi/linux/module.h>
 #include "internal.h"
 
-int module_sig_check(struct load_info *info, int flags)
+int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len)
 {
 	int err;
 	const char *reason;
 	const void *mod = info->hdr;
-	size_t sig_len;
-	const u8 *sig;
-	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
-				       MODULE_INIT_IGNORE_VERMAGIC);
 
-	err = mod_split_sig(info->hdr, &info->len, mangled_module, &sig_len, &sig, "module");
+	err = verify_pkcs7_signature(mod, info->len, sig, sig_len,
+				     VERIFY_USE_SECONDARY_KEYRING,
+				     VERIFYING_MODULE_SIGNATURE,
+				     NULL, NULL);
 	if (!err) {
-		err = verify_pkcs7_signature(mod, info->len, sig, sig_len,
-					     VERIFY_USE_SECONDARY_KEYRING,
-					     VERIFYING_MODULE_SIGNATURE,
-					     NULL, NULL);
-		if (!err) {
-			info->sig_ok = true;
-			return 0;
-		}
+		info->sig_ok = true;
+		return 0;
 	}
 
 	/*

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 13/17] module: Report signature type to users
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (11 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 12/17] module: Move signature splitting up Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-29 14:44   ` Petr Pavlu
  2026-01-13 12:28 ` [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency Thomas Weißschuh
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The upcoming CONFIG_MODULE_HASHES will introduce a signature type.
This needs to be handled by callers differently than PKCS7 signatures.

Report the signature type to the caller and let them verify it.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 include/linux/module_signature.h    |  2 +-
 kernel/module/main.c                |  9 +++++++--
 kernel/module_signature.c           | 14 ++++----------
 security/integrity/ima/ima_modsig.c |  8 +++++++-
 4 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
index 186a55effa30..a45ce3b24403 100644
--- a/include/linux/module_signature.h
+++ b/include/linux/module_signature.h
@@ -41,6 +41,6 @@ struct module_signature {
 };
 
 int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
-		  size_t *sig_len, const u8 **sig, const char *name);
+		  enum pkey_id_type *sig_type, size_t *sig_len, const u8 **sig, const char *name);
 
 #endif /* _LINUX_MODULE_SIGNATURE_H */
diff --git a/kernel/module/main.c b/kernel/module/main.c
index d65bc300a78c..2a28a0ece809 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3348,19 +3348,24 @@ static int module_integrity_check(struct load_info *info, int flags)
 {
 	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
 				       MODULE_INIT_IGNORE_VERMAGIC);
+	enum pkey_id_type sig_type;
 	size_t sig_len;
 	const u8 *sig;
 	int err = 0;
 
 	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
 		err = mod_split_sig(info->hdr, &info->len, mangled_module,
-				    &sig_len, &sig, "module");
+				    &sig_type, &sig_len, &sig, "module");
 		if (err)
 			return err;
 	}
 
-	if (IS_ENABLED(CONFIG_MODULE_SIG))
+	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
 		err = module_sig_check(info, sig, sig_len);
+	} else {
+		pr_err("module: not signed with expected PKCS#7 message\n");
+		err = -ENOPKG;
+	}
 
 	if (err)
 		return err;
diff --git a/kernel/module_signature.c b/kernel/module_signature.c
index b2384a73524c..8e0ac9906c9c 100644
--- a/kernel/module_signature.c
+++ b/kernel/module_signature.c
@@ -19,18 +19,11 @@
  * @file_len:	Size of the file to which @ms is appended.
  * @name:	What is being checked. Used for error messages.
  */
-static int mod_check_sig(const struct module_signature *ms, size_t file_len,
-			 const char *name)
+static int mod_check_sig(const struct module_signature *ms, size_t file_len, const char *name)
 {
 	if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
 		return -EBADMSG;
 
-	if (ms->id_type != PKEY_ID_PKCS7) {
-		pr_err("%s: not signed with expected PKCS#7 message\n",
-		       name);
-		return -ENOPKG;
-	}
-
 	if (ms->algo != 0 ||
 	    ms->hash != 0 ||
 	    ms->signer_len != 0 ||
@@ -38,7 +31,7 @@ static int mod_check_sig(const struct module_signature *ms, size_t file_len,
 	    ms->__pad[0] != 0 ||
 	    ms->__pad[1] != 0 ||
 	    ms->__pad[2] != 0) {
-		pr_err("%s: PKCS#7 signature info has unexpected non-zero params\n",
+		pr_err("%s: signature info has unexpected non-zero params\n",
 		       name);
 		return -EBADMSG;
 	}
@@ -47,7 +40,7 @@ static int mod_check_sig(const struct module_signature *ms, size_t file_len,
 }
 
 int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
-		  size_t *sig_len, const u8 **sig, const char *name)
+		  enum pkey_id_type *sig_type, size_t *sig_len, const u8 **sig, const char *name)
 {
 	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
 	struct module_signature ms;
@@ -74,6 +67,7 @@ int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
 	if (ret)
 		return ret;
 
+	*sig_type = ms.id_type;
 	*sig_len = be32_to_cpu(ms.sig_len);
 	modlen -= *sig_len + sizeof(ms);
 	*buf_len = modlen;
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
index a57342d39b07..a05008324a10 100644
--- a/security/integrity/ima/ima_modsig.c
+++ b/security/integrity/ima/ima_modsig.c
@@ -41,15 +41,21 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
 		    struct modsig **modsig)
 {
 	size_t buf_len_sz = buf_len;
+	enum pkey_id_type sig_type;
 	struct modsig *hdr;
 	size_t sig_len;
 	const u8 *sig;
 	int rc;
 
-	rc = mod_split_sig(buf, &buf_len_sz, true, &sig_len, &sig, func_tokens[func]);
+	rc = mod_split_sig(buf, &buf_len_sz, true, &sig_type, &sig_len, &sig, func_tokens[func]);
 	if (rc)
 		return rc;
 
+	if (sig_type != PKEY_ID_PKCS7) {
+		pr_err("%s: not signed with expected PKCS#7 message\n", func_tokens[func]);
+		return -ENOPKG;
+	}
+
 	/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
 	hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL);
 	if (!hdr)

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (12 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 13/17] module: Report signature type to users Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-02-13 15:32   ` Nicolas Schier
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The new hash-based module integrity checking will also be able to
satisfy the requirements of lockdown.
Such an alternative is not representable with "select", so use
"depends on" instead.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 security/lockdown/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
index e84ddf484010..155959205b8e 100644
--- a/security/lockdown/Kconfig
+++ b/security/lockdown/Kconfig
@@ -1,7 +1,7 @@
 config SECURITY_LOCKDOWN_LSM
 	bool "Basic module for enforcing kernel lockdown"
 	depends on SECURITY
-	select MODULE_SIG if MODULES
+	depends on !MODULES || MODULE_SIG
 	help
 	  Build support for an LSM that enforces a coarse kernel lockdown
 	  behaviour.

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (13 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency Thomas Weißschuh
@ 2026-01-13 12:28 ` Thomas Weißschuh
  2026-01-13 14:56   ` Sebastian Andrzej Siewior
                     ` (4 more replies)
  2026-01-13 12:29 ` [PATCH v4 16/17] kbuild: move handling of module stripping to Makefile.lib Thomas Weißschuh
                   ` (2 subsequent siblings)
  17 siblings, 5 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:28 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.

The goal is to reach bit-for-bit reproducibility. Excluding certain
parts of the build output from the reproducibility analysis would be
error-prone and force each downstream consumer to introduce new tooling.

Introduce a new mechanism to ensure only well-known modules are loaded
by embedding a merkle tree root of all modules built as part of the full
kernel build into vmlinux.

Non-builtin modules can be validated as before through signatures.

Normally the .ko module files depend on a fully built vmlinux to be
available for modpost validation and BTF generation. With
CONFIG_MODULE_HASHES, vmlinux now depends on the modules
to build a merkle tree. This introduces a dependency cycle which is
impossible to satisfy. Work around this by building the modules during
link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
but before the final module hashes are

The PKCS7 format which is used for regular module signatures can not
represent Merkle proofs, so a new kind of module signature is
introduced. As this signature type is only ever used for builtin
modules, no compatibility issues can arise.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 .gitignore                                   |   1 +
 Documentation/kbuild/reproducible-builds.rst |   5 +-
 Makefile                                     |   8 +-
 include/asm-generic/vmlinux.lds.h            |  11 +
 include/linux/module_hashes.h                |  25 ++
 include/linux/module_signature.h             |   1 +
 kernel/module/Kconfig                        |  21 +-
 kernel/module/Makefile                       |   1 +
 kernel/module/hashes.c                       |  92 ++++++
 kernel/module/hashes_root.c                  |   6 +
 kernel/module/internal.h                     |   1 +
 kernel/module/main.c                         |   4 +-
 scripts/.gitignore                           |   1 +
 scripts/Makefile                             |   3 +
 scripts/Makefile.modfinal                    |  11 +
 scripts/Makefile.modinst                     |  13 +
 scripts/Makefile.vmlinux                     |   5 +
 scripts/link-vmlinux.sh                      |  14 +-
 scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
 security/lockdown/Kconfig                    |   2 +-
 20 files changed, 685 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3a7241c941f5..299c54083672 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@
 *.lz4
 *.lzma
 *.lzo
+*.merkle
 *.mod
 *.mod.c
 *.o
diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst
index 96d208e578cd..bfde81e47b2d 100644
--- a/Documentation/kbuild/reproducible-builds.rst
+++ b/Documentation/kbuild/reproducible-builds.rst
@@ -82,7 +82,10 @@ generate a different temporary key for each build, resulting in the
 modules being unreproducible.  However, including a signing key with
 your source would presumably defeat the purpose of signing modules.
 
-One approach to this is to divide up the build process so that the
+Instead ``CONFIG_MODULE_HASHES`` can be used to embed a static list
+of valid modules to load.
+
+Another approach to this is to divide up the build process so that the
 unreproducible parts can be treated as sources:
 
 1. Generate a persistent signing key.  Add the certificate for the key
diff --git a/Makefile b/Makefile
index e404e4767944..841772a5a260 100644
--- a/Makefile
+++ b/Makefile
@@ -1588,8 +1588,10 @@ endif
 # is an exception.
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 KBUILD_BUILTIN := y
+ifndef CONFIG_MODULE_HASHES
 modules: vmlinux
 endif
+endif
 
 modules: modules_prepare
 
@@ -1981,7 +1983,11 @@ modules.order: $(build-dir)
 # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
 # This is solely useful to speed up test compiles.
 modules: modpost
-ifneq ($(KBUILD_MODPOST_NOFINAL),1)
+ifdef CONFIG_MODULE_HASHES
+ifeq ($(MODULE_HASHES_MODPOST_FINAL), 1)
+	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
+endif
+else ifneq ($(KBUILD_MODPOST_NOFINAL),1)
 	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
 endif
 
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 8ca130af301f..d3846845e37b 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -508,6 +508,8 @@
 									\
 	PRINTK_INDEX							\
 									\
+	MODULE_HASHES							\
+									\
 	/* Kernel symbol table: Normal symbols */			\
 	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\
 		__start___ksymtab = .;					\
@@ -918,6 +920,15 @@
 #define PRINTK_INDEX
 #endif
 
+#ifdef CONFIG_MODULE_HASHES
+#define MODULE_HASHES							\
+	.module_hashes : AT(ADDR(.module_hashes) - LOAD_OFFSET) {	\
+		KEEP(*(SORT(.module_hashes)))				\
+	}
+#else
+#define MODULE_HASHES
+#endif
+
 /*
  * Discard .note.GNU-stack, which is emitted as PROGBITS by the compiler.
  * Otherwise, the type of .notes section would become PROGBITS instead of NOTES.
diff --git a/include/linux/module_hashes.h b/include/linux/module_hashes.h
new file mode 100644
index 000000000000..de61072627cc
--- /dev/null
+++ b/include/linux/module_hashes.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_MODULE_HASHES_H
+#define _LINUX_MODULE_HASHES_H
+
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+#include <crypto/sha2.h>
+
+#define __module_hashes_section __section(".module_hashes")
+#define MODULE_HASHES_HASH_SIZE SHA256_DIGEST_SIZE
+
+struct module_hashes_proof {
+	__be32 pos;
+	u8 hash_sigs[][MODULE_HASHES_HASH_SIZE];
+} __packed;
+
+struct module_hashes_root {
+	u32 levels;
+	u8 hash[MODULE_HASHES_HASH_SIZE];
+};
+
+extern const struct module_hashes_root module_hashes_root;
+
+#endif /* _LINUX_MODULE_HASHES_H */
diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
index a45ce3b24403..3b510651830d 100644
--- a/include/linux/module_signature.h
+++ b/include/linux/module_signature.h
@@ -18,6 +18,7 @@ enum pkey_id_type {
 	PKEY_ID_PGP,		/* OpenPGP generated key ID */
 	PKEY_ID_X509,		/* X.509 arbitrary subjectKeyIdentifier */
 	PKEY_ID_PKCS7,		/* Signature in PKCS#7 message */
+	PKEY_ID_MERKLE,		/* Merkle proof for modules */
 };
 
 /*
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index db3b61fb3e73..c00ca830330c 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -271,7 +271,7 @@ config MODULE_SIG
 	  inclusion into an initramfs that wants the module size reduced.
 
 config MODULE_SIG_POLICY
-	def_bool MODULE_SIG
+	def_bool MODULE_SIG || MODULE_HASHES
 
 config MODULE_SIG_FORCE
 	bool "Require modules to be validly signed"
@@ -289,7 +289,7 @@ config MODULE_SIG_ALL
 	  modules must be signed manually, using the scripts/sign-file tool.
 
 comment "Do not forget to sign required modules with scripts/sign-file"
-	depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL
+	depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL && !MODULE_HASHES
 
 choice
 	prompt "Hash algorithm to sign modules"
@@ -408,6 +408,23 @@ config MODULE_DECOMPRESS
 
 	  If unsure, say N.
 
+config MODULE_HASHES
+	bool "Module hash validation"
+	depends on !MODULE_SIG_ALL
+	depends on !IMA_APPRAISE_MODSIG
+	select MODULE_SIG_FORMAT
+	select CRYPTO_LIB_SHA256
+	help
+	  Validate modules by their hashes.
+	  Only modules built together with the main kernel image can be
+	  validated that way.
+
+	  This is a reproducible-build compatible alternative to a build-time
+	  generated module keyring, as enabled by
+	  CONFIG_MODULE_SIG_KEY=certs/signing_key.pem.
+
+	  Also see the warning in MODULE_SIG about stripping modules.
+
 config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
 	bool "Allow loading of modules with missing namespace imports"
 	help
diff --git a/kernel/module/Makefile b/kernel/module/Makefile
index d9e8759a7b05..dd37aaf4a61a 100644
--- a/kernel/module/Makefile
+++ b/kernel/module/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_KGDB_KDB) += kdb.o
 obj-$(CONFIG_MODVERSIONS) += version.o
 obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
 obj-$(CONFIG_MODULE_STATS) += stats.o
+obj-$(CONFIG_MODULE_HASHES) += hashes.o hashes_root.o
diff --git a/kernel/module/hashes.c b/kernel/module/hashes.c
new file mode 100644
index 000000000000..23ca9f66652f
--- /dev/null
+++ b/kernel/module/hashes.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Module hash-based integrity checker
+ *
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ */
+
+#define pr_fmt(fmt) "module/hash: " fmt
+
+#include <linux/module_hashes.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+
+#include <crypto/sha2.h>
+
+#include "internal.h"
+
+static __init __maybe_unused int module_hashes_init(void)
+{
+	pr_debug("root: levels=%u hash=%*phN\n",
+		 module_hashes_root.levels,
+		 (int)sizeof(module_hashes_root.hash), module_hashes_root.hash);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_MODULE_DEBUG)
+early_initcall(module_hashes_init);
+#endif
+
+static void hash_entry(const void *left, const void *right, void *out)
+{
+	struct sha256_ctx ctx;
+	u8 magic = 0x02;
+
+	sha256_init(&ctx);
+	sha256_update(&ctx, &magic, sizeof(magic));
+	sha256_update(&ctx, left, MODULE_HASHES_HASH_SIZE);
+	sha256_update(&ctx, right, MODULE_HASHES_HASH_SIZE);
+	sha256_final(&ctx, out);
+}
+
+static void hash_data(const void *d, size_t len, unsigned int pos, void *out)
+{
+	struct sha256_ctx ctx;
+	u8 magic = 0x01;
+	__be32 pos_be;
+
+	pos_be = cpu_to_be32(pos);
+
+	sha256_init(&ctx);
+	sha256_update(&ctx, &magic, sizeof(magic));
+	sha256_update(&ctx, (const u8 *)&pos_be, sizeof(pos_be));
+	sha256_update(&ctx, d, len);
+	sha256_final(&ctx, out);
+}
+
+static bool module_hashes_verify_proof(u32 pos, const u8 hash_sigs[][MODULE_HASHES_HASH_SIZE],
+				       u8 *cur)
+{
+	for (unsigned int i = 0; i < module_hashes_root.levels; i++, pos >>= 1) {
+		if ((pos & 1) == 0)
+			hash_entry(cur, hash_sigs[i], cur);
+		else
+			hash_entry(hash_sigs[i], cur, cur);
+	}
+
+	return !memcmp(cur, module_hashes_root.hash, MODULE_HASHES_HASH_SIZE);
+}
+
+int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len)
+{
+	u8 modhash[MODULE_HASHES_HASH_SIZE];
+	const struct module_hashes_proof *proof;
+	size_t proof_size;
+	u32 pos;
+
+	proof_size = struct_size(proof, hash_sigs, module_hashes_root.levels);
+
+	if (sig_len != proof_size)
+		return -ENOPKG;
+
+	proof = (const struct module_hashes_proof *)sig;
+	pos = get_unaligned_be32(&proof->pos);
+
+	hash_data(info->hdr, info->len, pos, &modhash);
+
+	if (module_hashes_verify_proof(pos, proof->hash_sigs, modhash))
+		info->sig_ok = true;
+
+	return 0;
+}
diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
new file mode 100644
index 000000000000..1abfcd3aa679
--- /dev/null
+++ b/kernel/module/hashes_root.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module_hashes.h>
+
+/* Blank dummy data. Will be overridden by link-vmlinux.sh */
+const struct module_hashes_root module_hashes_root __module_hashes_section = {};
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index e2d49122c2a1..e22837d3ac76 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
 			       const char *secstrings);
 
 int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
+int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
 
 #ifdef CONFIG_DEBUG_KMEMLEAK
 void kmemleak_load_module(const struct module *mod, const struct load_info *info);
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 2a28a0ece809..fa30b6387936 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
 
 	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
 		err = module_sig_check(info, sig, sig_len);
+	} else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
+		err = module_hash_check(info, sig, sig_len);
 	} else {
-		pr_err("module: not signed with expected PKCS#7 message\n");
+		pr_err("module: not signed with signature mechanism\n");
 		err = -ENOPKG;
 	}
 
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e..8dad9b0d3b2d 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -5,6 +5,7 @@
 /insert-sys-cert
 /kallsyms
 /module.lds
+/modules-merkle-tree
 /recordmcount
 /rustdoc_test_builder
 /rustdoc_test_gen
diff --git a/scripts/Makefile b/scripts/Makefile
index 0941e5ce7b57..f539e4d93af7 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -11,6 +11,7 @@ hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT)		+= sign-file
 hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE)	+= insert-sys-cert
 hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)		+= rustdoc_test_builder
 hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)		+= rustdoc_test_gen
+hostprogs-always-$(CONFIG_MODULE_HASHES)		+= modules-merkle-tree
 hostprogs-always-$(CONFIG_TRACEPOINTS)			+= tracepoint-update
 
 sorttable-objs := sorttable.o elf-parse.o
@@ -36,6 +37,8 @@ HOSTLDLIBS_sorttable = -lpthread
 HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
 HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
 HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+HOSTCFLAGS_modules-merkle-tree.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTLDLIBS_modules-merkle-tree = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
 
 ifdef CONFIG_UNWINDER_ORC
 ifeq ($(ARCH),x86_64)
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 930db0524a0a..5b8e94170beb 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -63,7 +63,18 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 endif
 	+$(call cmd,check_tracepoint)
 
+quiet_cmd_merkle = MERKLE  $@
+      cmd_merkle = $(objtree)/scripts/modules-merkle-tree $@ .ko
+
+.tmp_module_hashes.c: $(modules:%.o=%.ko) $(objtree)/scripts/modules-merkle-tree FORCE
+	$(call cmd,merkle)
+
+ifdef CONFIG_MODULE_HASHES
+__modfinal: .tmp_module_hashes.c
+endif
+
 targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
+targets += $(modules:%.o=%.merkle) .tmp_module_hashes.c
 
 # Add FORCE to the prerequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 9ba45e5b32b1..ba4343b40497 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -79,6 +79,12 @@ quiet_cmd_install = INSTALL $@
 # as the options to the strip command.
 ifdef INSTALL_MOD_STRIP
 
+ifdef CONFIG_MODULE_HASHES
+ifeq ($(KBUILD_EXTMOD),)
+$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
+endif
+endif
+
 ifeq ($(INSTALL_MOD_STRIP),1)
 strip-option := --strip-debug
 else
@@ -116,6 +122,13 @@ quiet_cmd_sign :=
       cmd_sign := :
 endif
 
+ifeq ($(KBUILD_EXTMOD),)
+ifdef CONFIG_MODULE_HASHES
+quiet_cmd_sign = MERKLE [M] $@
+      cmd_sign = cat $(objtree)/$*.merkle >> $@
+endif
+endif
+
 # Create necessary directories
 $(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
 
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index cd788cac9d91..f4e38b953b01 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -78,6 +78,11 @@ ifdef CONFIG_BUILDTIME_TABLE_SORT
 vmlinux.unstripped: scripts/sorttable
 endif
 
+ifdef CONFIG_MODULE_HASHES
+vmlinux.unstripped: $(objtree)/scripts/modules-merkle-tree
+vmlinux.unstripped: modules.order
+endif
+
 # vmlinux
 # ---------------------------------------------------------------------------
 
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 8c98f8645a5c..bfeff1f5753d 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -103,7 +103,7 @@ vmlinux_link()
 	${ld} ${ldflags} -o ${output}					\
 		${wl}--whole-archive ${objs} ${wl}--no-whole-archive	\
 		${wl}--start-group ${libs} ${wl}--end-group		\
-		${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
+		${kallsymso} ${btf_vmlinux_bin_o} ${module_hashes_o} ${arch_vmlinux_o} ${ldlibs}
 }
 
 # generate .BTF typeinfo from DWARF debuginfo
@@ -212,6 +212,7 @@ fi
 
 btf_vmlinux_bin_o=
 kallsymso=
+module_hashes_o=
 strip_debug=
 generate_map=
 
@@ -315,6 +316,17 @@ if is_enabled CONFIG_BUILDTIME_TABLE_SORT; then
 	fi
 fi
 
+if is_enabled CONFIG_MODULE_HASHES; then
+	info MAKE modules
+	${MAKE} -f Makefile MODULE_HASHES_MODPOST_FINAL=1 modules
+	module_hashes_o=.tmp_module_hashes.o
+	info CC ${module_hashes_o}
+	${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} \
+		${KBUILD_CFLAGS_KERNEL} -fno-lto -c -o "${module_hashes_o}" ".tmp_module_hashes.c"
+	${OBJCOPY} --dump-section .module_hashes=.tmp_module_hashes.bin ${module_hashes_o}
+	${OBJCOPY} --update-section .module_hashes=.tmp_module_hashes.bin ${VMLINUX}
+fi
+
 # step a (see comment above)
 if is_enabled CONFIG_KALLSYMS; then
 	if ! cmp -s System.map "${kallsyms_sysmap}"; then
diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
new file mode 100644
index 000000000000..a6ec0e21213b
--- /dev/null
+++ b/scripts/modules-merkle-tree.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Compute hashes for modules files and build a merkle tree.
+ *
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ *
+ */
+#define _GNU_SOURCE 1
+#include <arpa/inet.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#include "ssl-common.h"
+
+static int hash_size;
+static EVP_MD_CTX *ctx;
+
+struct module_signature {
+	uint8_t		algo;		/* Public-key crypto algorithm [0] */
+	uint8_t		hash;		/* Digest algorithm [0] */
+	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
+	uint8_t		signer_len;	/* Length of signer's name [0] */
+	uint8_t		key_id_len;	/* Length of key identifier [0] */
+	uint8_t		__pad[3];
+	uint32_t	sig_len;	/* Length of signature data */
+};
+
+#define PKEY_ID_MERKLE 3
+
+static const char magic_number[] = "~Module signature appended~\n";
+
+struct file_entry {
+	char *name;
+	unsigned int pos;
+	unsigned char hash[EVP_MAX_MD_SIZE];
+};
+
+static struct file_entry *fh_list;
+static size_t num_files;
+
+struct leaf_hash {
+	unsigned char hash[EVP_MAX_MD_SIZE];
+};
+
+struct mtree {
+	struct leaf_hash **l;
+	unsigned int *entries;
+	unsigned int levels;
+};
+
+static inline void *xcalloc(size_t n, size_t size)
+{
+	void *p;
+
+	p = calloc(n, size);
+	if (!p)
+		errx(1, "Memory allocation failed");
+
+	return p;
+}
+
+static void *xmalloc(size_t size)
+{
+	void *p;
+
+	p = malloc(size);
+	if (!p)
+		errx(1, "Memory allocation failed");
+
+	return p;
+}
+
+static inline void *xreallocarray(void *oldp, size_t n, size_t size)
+{
+	void *p;
+
+	p = reallocarray(oldp, n, size);
+	if (!p)
+		errx(1, "Memory allocation failed");
+
+	return p;
+}
+
+static inline char *xasprintf(const char *fmt, ...)
+{
+	va_list ap;
+	char *strp;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = vasprintf(&strp, fmt, ap);
+	va_end(ap);
+	if (ret == -1)
+		err(1, "Memory allocation failed");
+
+	return strp;
+}
+
+static unsigned int get_pow2(unsigned int val)
+{
+	return 31 - __builtin_clz(val);
+}
+
+static unsigned int roundup_pow2(unsigned int val)
+{
+	return 1 << (get_pow2(val - 1) + 1);
+}
+
+static unsigned int log2_roundup(unsigned int val)
+{
+	return get_pow2(roundup_pow2(val));
+}
+
+static void hash_data(void *p, unsigned int pos, size_t size, void *ret_hash)
+{
+	unsigned char magic = 0x01;
+	unsigned int pos_be;
+
+	pos_be = htonl(pos);
+
+	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+	ERR(EVP_DigestUpdate(ctx, &pos_be, sizeof(pos_be)) != 1, "EVP_DigestUpdate(pos)");
+	ERR(EVP_DigestUpdate(ctx, p, size) != 1, "EVP_DigestUpdate(data)");
+	ERR(EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_entry(void *left, void *right, void *ret_hash)
+{
+	int hash_size = EVP_MD_CTX_get_size_ex(ctx);
+	unsigned char magic = 0x02;
+
+	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+	ERR(EVP_DigestUpdate(ctx, left, hash_size) != 1, "EVP_DigestUpdate(left)");
+	ERR(EVP_DigestUpdate(ctx, right, hash_size) != 1, "EVP_DigestUpdate(right)");
+	ERR(EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_file(struct file_entry *fe)
+{
+	struct stat sb;
+	int fd, ret;
+	void *mem;
+
+	fd = open(fe->name, O_RDONLY);
+	if (fd < 0)
+		err(1, "Failed to open %s", fe->name);
+
+	ret = fstat(fd, &sb);
+	if (ret)
+		err(1, "Failed to stat %s", fe->name);
+
+	mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	close(fd);
+
+	if (mem == MAP_FAILED)
+		err(1, "Failed to mmap %s", fe->name);
+
+	hash_data(mem, fe->pos, sb.st_size, fe->hash);
+
+	munmap(mem, sb.st_size);
+}
+
+static struct mtree *build_merkle(struct file_entry *fh, size_t num)
+{
+	struct mtree *mt;
+	unsigned int le;
+
+	if (!num)
+		return NULL;
+
+	mt = xmalloc(sizeof(*mt));
+	mt->levels = log2_roundup(num);
+
+	mt->l = xcalloc(sizeof(*mt->l), mt->levels);
+
+	mt->entries = xcalloc(sizeof(*mt->entries), mt->levels);
+	le = num / 2;
+	if (num & 1)
+		le++;
+	mt->entries[0] = le;
+	mt->l[0] = xcalloc(sizeof(**mt->l), le);
+
+	/* First level of pairs */
+	for (unsigned int i = 0; i < num; i += 2) {
+		if (i == num - 1) {
+			/* Odd number of files, no pair. Hash with itself */
+			hash_entry(fh[i].hash, fh[i].hash, mt->l[0][i / 2].hash);
+		} else {
+			hash_entry(fh[i].hash, fh[i + 1].hash, mt->l[0][i / 2].hash);
+		}
+	}
+	for (unsigned int i = 1; i < mt->levels; i++) {
+		int odd = 0;
+
+		if (le & 1) {
+			le++;
+			odd++;
+		}
+
+		mt->entries[i] = le / 2;
+		mt->l[i] = xcalloc(sizeof(**mt->l), le);
+
+		for (unsigned int n = 0; n < le; n += 2) {
+			if (n == le - 2 && odd) {
+				/* Odd number of pairs, no pair. Hash with itself */
+				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n].hash,
+					   mt->l[i][n / 2].hash);
+			} else {
+				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n + 1].hash,
+					   mt->l[i][n / 2].hash);
+			}
+		}
+		le =  mt->entries[i];
+	}
+	return mt;
+}
+
+static void free_mtree(struct mtree *mt)
+{
+	if (!mt)
+		return;
+
+	for (unsigned int i = 0; i < mt->levels; i++)
+		free(mt->l[i]);
+
+	free(mt->l);
+	free(mt->entries);
+	free(mt);
+}
+
+static void write_be_int(int fd, unsigned int v)
+{
+	unsigned int be_val = htonl(v);
+
+	if (write(fd, &be_val, sizeof(be_val)) != sizeof(be_val))
+		err(1, "Failed writing to file");
+}
+
+static void write_hash(int fd, const void *h)
+{
+	ssize_t wr;
+
+	wr = write(fd, h, hash_size);
+	if (wr != hash_size)
+		err(1, "Failed writing to file");
+}
+
+static void build_proof(struct mtree *mt, unsigned int n, int fd)
+{
+	unsigned char cur[EVP_MAX_MD_SIZE];
+	unsigned char tmp[EVP_MAX_MD_SIZE];
+	struct file_entry *fe, *fe_sib;
+
+	fe = &fh_list[n];
+
+	if ((n & 1) == 0) {
+		/* No pair, hash with itself */
+		if (n + 1 == num_files)
+			fe_sib = fe;
+		else
+			fe_sib = &fh_list[n + 1];
+	} else {
+		fe_sib = &fh_list[n - 1];
+	}
+	/* First comes the node position into the file */
+	write_be_int(fd, n);
+
+	if ((n & 1) == 0)
+		hash_entry(fe->hash, fe_sib->hash, cur);
+	else
+		hash_entry(fe_sib->hash, fe->hash, cur);
+
+	/* Next is the sibling hash, followed by hashes in the tree */
+	write_hash(fd, fe_sib->hash);
+
+	for (unsigned int i = 0; i < mt->levels - 1; i++) {
+		n >>= 1;
+		if ((n & 1) == 0) {
+			void *h;
+
+			/* No pair, hash with itself */
+			if (n + 1 == mt->entries[i])
+				h = cur;
+			else
+				h = mt->l[i][n + 1].hash;
+
+			hash_entry(cur, h, tmp);
+			write_hash(fd, h);
+		} else {
+			hash_entry(mt->l[i][n - 1].hash, cur, tmp);
+			write_hash(fd, mt->l[i][n - 1].hash);
+		}
+		memcpy(cur, tmp, hash_size);
+	}
+
+	 /* After all that, the end hash should match the root hash */
+	if (memcmp(cur, mt->l[mt->levels - 1][0].hash, hash_size))
+		errx(1, "hash mismatch");
+}
+
+static void append_module_signature_magic(int fd, unsigned int sig_len)
+{
+	struct module_signature sig_info = {
+		.id_type	= PKEY_ID_MERKLE,
+		.sig_len	= htonl(sig_len),
+	};
+
+	if (write(fd, &sig_info, sizeof(sig_info)) < 0)
+		err(1, "write(sig_info) failed");
+
+	if (write(fd, &magic_number, sizeof(magic_number) - 1) < 0)
+		err(1, "write(magic_number) failed");
+}
+
+static void write_merkle_root(struct mtree *mt, const char *fp)
+{
+	char buf[1024];
+	unsigned int levels;
+	unsigned char *h;
+	FILE *f;
+
+	if (mt) {
+		levels = mt->levels;
+		h = mt->l[mt->levels - 1][0].hash;
+	} else {
+		levels = 0;
+		h = xcalloc(1, hash_size);
+	}
+
+	f = fopen(fp, "w");
+	if (!f)
+		err(1, "Failed to create %s", buf);
+
+	fprintf(f, "#include <linux/module_hashes.h>\n\n");
+	fprintf(f, "const struct module_hashes_root module_hashes_root __module_hashes_section = {\n");
+
+	fprintf(f, "\t.levels = %u,\n", levels);
+	fprintf(f, "\t.hash = {");
+	for (unsigned int i = 0; i < hash_size; i++) {
+		char *space = "";
+
+		if (!(i % 8))
+			fprintf(f, "\n\t\t");
+
+		if ((i + 1) % 8)
+			space = " ";
+
+		fprintf(f, "0x%02x,%s", h[i], space);
+	}
+	fprintf(f, "\n\t},");
+
+	fprintf(f, "\n};\n");
+	fclose(f);
+
+	if (!mt)
+		free(h);
+}
+
+static char *xstrdup_replace_suffix(const char *str, const char *new_suffix)
+{
+	const char *current_suffix;
+	size_t base_len;
+
+	current_suffix = strchr(str, '.');
+	if (!current_suffix)
+		errx(1, "No existing suffix in '%s'", str);
+
+	base_len = current_suffix - str;
+
+	return xasprintf("%.*s%s", (int)base_len, str, new_suffix);
+}
+
+static void read_modules_order(const char *fname, const char *suffix)
+{
+	char line[PATH_MAX];
+	FILE *in;
+
+	in = fopen(fname, "r");
+	if (!in)
+		err(1, "fopen(%s)", fname);
+
+	while (fgets(line, PATH_MAX, in)) {
+		struct file_entry *entry;
+
+		fh_list = xreallocarray(fh_list, num_files + 1, sizeof(*fh_list));
+		entry = &fh_list[num_files];
+
+		entry->pos = num_files;
+		entry->name = xstrdup_replace_suffix(line, suffix);
+		hash_file(entry);
+
+		num_files++;
+	}
+
+	fclose(in);
+}
+
+static __attribute__((noreturn))
+void format(void)
+{
+	fprintf(stderr,
+		"Usage: scripts/modules-merkle-tree <root definition>\n");
+	exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+	const EVP_MD *hash_evp;
+	struct mtree *mt;
+
+	if (argc != 3)
+		format();
+
+	hash_evp = EVP_get_digestbyname("sha256");
+	ERR(!hash_evp, "EVP_get_digestbyname");
+
+	ctx = EVP_MD_CTX_new();
+	ERR(!ctx, "EVP_MD_CTX_new()");
+
+	hash_size = EVP_MD_get_size(hash_evp);
+	ERR(hash_size <= 0, "EVP_get_digestbyname");
+
+	if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
+		ERR(1, "EVP_DigestInit_ex()");
+
+	read_modules_order("modules.order", argv[2]);
+
+	mt = build_merkle(fh_list, num_files);
+	write_merkle_root(mt, argv[1]);
+	for (unsigned int i = 0; i < num_files; i++) {
+		char *signame;
+		int fd;
+
+		signame = xstrdup_replace_suffix(fh_list[i].name, ".merkle");
+
+		fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+		if (fd < 0)
+			err(1, "Can't create %s", signame);
+
+		build_proof(mt, i, fd);
+		append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));
+		close(fd);
+	}
+
+	free_mtree(mt);
+	for (unsigned int i = 0; i < num_files; i++)
+		free(fh_list[i].name);
+	free(fh_list);
+
+	EVP_MD_CTX_free(ctx);
+	return 0;
+}
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
index 155959205b8e..60b240e3ef1f 100644
--- a/security/lockdown/Kconfig
+++ b/security/lockdown/Kconfig
@@ -1,7 +1,7 @@
 config SECURITY_LOCKDOWN_LSM
 	bool "Basic module for enforcing kernel lockdown"
 	depends on SECURITY
-	depends on !MODULES || MODULE_SIG
+	depends on !MODULES || MODULE_SIG || MODULE_HASHES
 	help
 	  Build support for an LSM that enforces a coarse kernel lockdown
 	  behaviour.

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 16/17] kbuild: move handling of module stripping to Makefile.lib
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (14 preceding siblings ...)
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
@ 2026-01-13 12:29 ` Thomas Weißschuh
  2026-01-13 12:29 ` [PATCH v4 17/17] kbuild: make CONFIG_MODULE_HASHES compatible with module stripping Thomas Weißschuh
  2026-01-31  7:36 ` [PATCH v4 00/17] module: Introduce hash-based integrity checking Mihai-Drosi Câju
  17 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:29 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

To allow CONFIG_MODULE_HASHES in combination with INSTALL_MOD_STRIP,
this logc will also be used by Makefile.modfinal.

Move it to a shared location to enable reuse.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 scripts/Makefile.lib     | 32 ++++++++++++++++++++++++++++++++
 scripts/Makefile.modinst | 37 +++++--------------------------------
 2 files changed, 37 insertions(+), 32 deletions(-)

diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 28a1c08e3b22..7fcf3c43e408 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -474,6 +474,38 @@ define sed-offsets
 	s:->::; p;}'
 endef
 
+#
+# Module Installation
+#
+quiet_cmd_install_mod = INSTALL $@
+      cmd_install_mod = cp $< $@
+
+# Module Strip
+# ---------------------------------------------------------------------------
+#
+# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
+# are installed. If INSTALL_MOD_STRIP is '1', then the default option
+# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
+# as the options to the strip command.
+ifeq ($(INSTALL_MOD_STRIP),1)
+mod-strip-option := --strip-debug
+else
+mod-strip-option := $(INSTALL_MOD_STRIP)
+endif
+
+# Strip
+ifdef INSTALL_MOD_STRIP
+
+quiet_cmd_strip_mod = STRIP   $@
+      cmd_strip_mod = $(STRIP) $(mod-strip-option) $@
+
+else
+
+quiet_cmd_strip_mod =
+      cmd_strip_mod = :
+
+endif
+
 # Use filechk to avoid rebuilds when a header changes, but the resulting file
 # does not
 define filechk_offsets
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index ba4343b40497..07380c7233a0 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -8,6 +8,7 @@ __modinst:
 
 include $(objtree)/include/config/auto.conf
 include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
 
 install-y :=
 
@@ -36,7 +37,7 @@ install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
 install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
 
 $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
-	$(call cmd,install)
+	$(call cmd,install_mod)
 
 endif
 
@@ -65,40 +66,12 @@ install-$(CONFIG_MODULES) += $(modules)
 __modinst: $(install-y)
 	@:
 
-#
-# Installation
-#
-quiet_cmd_install = INSTALL $@
-      cmd_install = cp $< $@
-
-# Strip
-#
-# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
-# are installed. If INSTALL_MOD_STRIP is '1', then the default option
-# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
-# as the options to the strip command.
-ifdef INSTALL_MOD_STRIP
-
 ifdef CONFIG_MODULE_HASHES
 ifeq ($(KBUILD_EXTMOD),)
+ifdef INSTALL_MOD_STRIP
 $(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
 endif
 endif
-
-ifeq ($(INSTALL_MOD_STRIP),1)
-strip-option := --strip-debug
-else
-strip-option := $(INSTALL_MOD_STRIP)
-endif
-
-quiet_cmd_strip = STRIP   $@
-      cmd_strip = $(STRIP) $(strip-option) $@
-
-else
-
-quiet_cmd_strip =
-      cmd_strip = :
-
 endif
 
 #
@@ -133,8 +106,8 @@ endif
 $(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
 
 $(dst)/%.ko: %.ko FORCE
-	$(call cmd,install)
-	$(call cmd,strip)
+	$(call cmd,install_mod)
+	$(call cmd,strip_mod)
 	$(call cmd,sign)
 
 ifdef CONFIG_MODULES

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* [PATCH v4 17/17] kbuild: make CONFIG_MODULE_HASHES compatible with module stripping
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (15 preceding siblings ...)
  2026-01-13 12:29 ` [PATCH v4 16/17] kbuild: move handling of module stripping to Makefile.lib Thomas Weißschuh
@ 2026-01-13 12:29 ` Thomas Weißschuh
  2026-01-31  7:36 ` [PATCH v4 00/17] module: Introduce hash-based integrity checking Mihai-Drosi Câju
  17 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-01-13 12:29 UTC (permalink / raw)
  To: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng, Nicolas Schier,
	Christophe Leroy
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, Thomas Weißschuh

CONFIG_MODULE_HASHES needs to process the modules at build time in the
exact form they will be loaded at runtime. If the modules are stripped
afterwards they will not be loadable anymore.

Also evaluate INSTALL_MOD_STRIP at build time and build the hashes based
on modules stripped this way.

If users specify inconsistent values of INSTALL_MOD_STRIP between build
and installation time, an error is reported.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 .gitignore                | 1 +
 kernel/module/Kconfig     | 5 +++++
 scripts/Makefile.modfinal | 9 +++++++--
 scripts/Makefile.modinst  | 4 ++--
 scripts/Makefile.vmlinux  | 1 +
 5 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index 299c54083672..900251c72ade 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@
 *.gz
 *.i
 *.ko
+*.ko.stripped
 *.lex.c
 *.ll
 *.lst
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index c00ca830330c..9fd34765ce2c 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -425,6 +425,11 @@ config MODULE_HASHES
 
 	  Also see the warning in MODULE_SIG about stripping modules.
 
+# To validate the consistency of INSTALL_MOD_STRIP for MODULE_HASHES
+config MODULE_INSTALL_STRIP
+	string
+	default "$(INSTALL_MOD_STRIP)"
+
 config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
 	bool "Allow loading of modules with missing namespace imports"
 	help
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 5b8e94170beb..890724edac69 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -63,10 +63,14 @@ ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 endif
 	+$(call cmd,check_tracepoint)
 
+%.ko.stripped: %.ko $(wildcard include/config/MODULE_INSTALL_STRIP)
+	$(call cmd,install_mod)
+	$(call cmd,strip_mod)
+
 quiet_cmd_merkle = MERKLE  $@
-      cmd_merkle = $(objtree)/scripts/modules-merkle-tree $@ .ko
+      cmd_merkle = $(objtree)/scripts/modules-merkle-tree $@ $(if $(CONFIG_MODULE_INSTALL_STRIP),.ko.stripped,.ko)
 
-.tmp_module_hashes.c: $(modules:%.o=%.ko) $(objtree)/scripts/modules-merkle-tree FORCE
+.tmp_module_hashes.c: $(if $(CONFIG_MODULE_INSTALL_STRIP),$(modules:%.o=%.ko.stripped),$(modules:%.o=%.ko)) $(objtree)/scripts/modules-merkle-tree $(wildcard include/config/MODULE_INSTALL_STRIP) FORCE
 	$(call cmd,merkle)
 
 ifdef CONFIG_MODULE_HASHES
@@ -75,6 +79,7 @@ endif
 
 targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
 targets += $(modules:%.o=%.merkle) .tmp_module_hashes.c
+targets += $(modules:%.o=%.ko.stripped)
 
 # Add FORCE to the prerequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 07380c7233a0..45606f994ad9 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -68,8 +68,8 @@ __modinst: $(install-y)
 
 ifdef CONFIG_MODULE_HASHES
 ifeq ($(KBUILD_EXTMOD),)
-ifdef INSTALL_MOD_STRIP
-$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
+ifneq ($(INSTALL_MOD_STRIP),$(CONFIG_MODULE_INSTALL_STRIP))
+$(error Inconsistent values for INSTALL_MOD_STRIP between build and installation)
 endif
 endif
 endif
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index f4e38b953b01..4ce849f6253a 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -81,6 +81,7 @@ endif
 ifdef CONFIG_MODULE_HASHES
 vmlinux.unstripped: $(objtree)/scripts/modules-merkle-tree
 vmlinux.unstripped: modules.order
+vmlinux.unstripped: $(wildcard include/config/MODULE_INSTALL_STRIP)
 endif
 
 # vmlinux

-- 
2.52.0


^ permalink raw reply related	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
@ 2026-01-13 14:56   ` Sebastian Andrzej Siewior
  2026-01-30 17:06   ` Petr Pavlu
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 80+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-01-13 14:56 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-01-13 13:28:59 [+0100], Thomas Weißschuh wrote:
> --- /dev/null
> +++ b/scripts/modules-merkle-tree.c
> @@ -0,0 +1,467 @@
> +static void build_proof(struct mtree *mt, unsigned int n, int fd)
> +{
> +	unsigned char cur[EVP_MAX_MD_SIZE];
> +	unsigned char tmp[EVP_MAX_MD_SIZE];

This and a few other instances below could be optimized to avoid
hashing. I probably forgot to let you know.
-> https://git.kernel.org/pub/scm/linux/kernel/git/bigeasy/mtree-hashed-mods.git/commit/?id=10b565c123c731da37befe862de13678b7c54877

> +	struct file_entry *fe, *fe_sib;
> +
> +	fe = &fh_list[n];
> +
> +	if ((n & 1) == 0) {
> +		/* No pair, hash with itself */
> +		if (n + 1 == num_files)
> +			fe_sib = fe;
> +		else
> +			fe_sib = &fh_list[n + 1];
> +	} else {
> +		fe_sib = &fh_list[n - 1];
> +	}
> +	/* First comes the node position into the file */
> +	write_be_int(fd, n);
> +
> +	if ((n & 1) == 0)
> +		hash_entry(fe->hash, fe_sib->hash, cur);
> +	else
> +		hash_entry(fe_sib->hash, fe->hash, cur);
> +
> +	/* Next is the sibling hash, followed by hashes in the tree */
> +	write_hash(fd, fe_sib->hash);
> +
> +	for (unsigned int i = 0; i < mt->levels - 1; i++) {
> +		n >>= 1;
> +		if ((n & 1) == 0) {
> +			void *h;
> +
> +			/* No pair, hash with itself */
> +			if (n + 1 == mt->entries[i])
> +				h = cur;
> +			else
> +				h = mt->l[i][n + 1].hash;
> +
> +			hash_entry(cur, h, tmp);
> +			write_hash(fd, h);
> +		} else {
> +			hash_entry(mt->l[i][n - 1].hash, cur, tmp);
> +			write_hash(fd, mt->l[i][n - 1].hash);
> +		}
> +		memcpy(cur, tmp, hash_size);
> +	}
> +
> +	 /* After all that, the end hash should match the root hash */
> +	if (memcmp(cur, mt->l[mt->levels - 1][0].hash, hash_size))
> +		errx(1, "hash mismatch");
> +}

Sebastian

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 08/17] module: Deduplicate signature extraction
  2026-01-13 12:28 ` [PATCH v4 08/17] module: Deduplicate signature extraction Thomas Weißschuh
@ 2026-01-27 15:20   ` Petr Pavlu
  2026-02-03 12:41     ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Petr Pavlu @ 2026-01-27 15:20 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> The logic to extract the signature bits from a module file are
> duplicated between the module core and IMA modsig appraisal.
> 
> Unify the implementation.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  include/linux/module_signature.h    |  4 +--
>  kernel/module/signing.c             | 52 +++++++------------------------------
>  kernel/module_signature.c           | 41 +++++++++++++++++++++++++++--
>  security/integrity/ima/ima_modsig.c | 24 ++++-------------
>  4 files changed, 56 insertions(+), 65 deletions(-)
> 
> diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
> index 7eb4b00381ac..186a55effa30 100644
> --- a/include/linux/module_signature.h
> +++ b/include/linux/module_signature.h
> @@ -40,7 +40,7 @@ struct module_signature {
>  	__be32	sig_len;	/* Length of signature data */
>  };
>  
> -int mod_check_sig(const struct module_signature *ms, size_t file_len,
> -		  const char *name);
> +int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
> +		  size_t *sig_len, const u8 **sig, const char *name);
>  
>  #endif /* _LINUX_MODULE_SIGNATURE_H */
> diff --git a/kernel/module/signing.c b/kernel/module/signing.c
> index fe3f51ac6199..6d64c0d18d0a 100644
> --- a/kernel/module/signing.c
> +++ b/kernel/module/signing.c
> @@ -37,54 +37,22 @@ void set_module_sig_enforced(void)
>  	sig_enforce = true;
>  }
>  
> -/*
> - * Verify the signature on a module.
> - */
> -static int mod_verify_sig(const void *mod, struct load_info *info)
> -{
> -	struct module_signature ms;
> -	size_t sig_len, modlen = info->len;
> -	int ret;
> -
> -	pr_devel("==>%s(,%zu)\n", __func__, modlen);
> -
> -	if (modlen <= sizeof(ms))
> -		return -EBADMSG;
> -
> -	memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
> -
> -	ret = mod_check_sig(&ms, modlen, "module");
> -	if (ret)
> -		return ret;
> -
> -	sig_len = be32_to_cpu(ms.sig_len);
> -	modlen -= sig_len + sizeof(ms);
> -	info->len = modlen;
> -
> -	return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
> -				      VERIFY_USE_SECONDARY_KEYRING,
> -				      VERIFYING_MODULE_SIGNATURE,
> -				      NULL, NULL);
> -}
> -
>  int module_sig_check(struct load_info *info, int flags)
>  {
> -	int err = -ENODATA;
> -	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
> +	int err;
>  	const char *reason;
>  	const void *mod = info->hdr;
> +	size_t sig_len;
> +	const u8 *sig;
>  	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
>  				       MODULE_INIT_IGNORE_VERMAGIC);
> -	/*
> -	 * Do not allow mangled modules as a module with version information
> -	 * removed is no longer the module that was signed.
> -	 */
> -	if (!mangled_module &&
> -	    info->len > markerlen &&
> -	    memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
> -		/* We truncate the module to discard the signature */
> -		info->len -= markerlen;
> -		err = mod_verify_sig(mod, info);
> +
> +	err = mod_split_sig(info->hdr, &info->len, mangled_module, &sig_len, &sig, "module");
> +	if (!err) {
> +		err = verify_pkcs7_signature(mod, info->len, sig, sig_len,
> +					     VERIFY_USE_SECONDARY_KEYRING,
> +					     VERIFYING_MODULE_SIGNATURE,
> +					     NULL, NULL);
>  		if (!err) {
>  			info->sig_ok = true;
>  			return 0;

The patch looks to modify the behavior when mangled_module is true.

Previously, module_sig_check() didn't attempt to extract the signature
in such a case and treated the module as unsigned. The err remained set
to -ENODATA and the function subsequently consulted module_sig_check()
and security_locked_down() to determine an appropriate result.

Newly, module_sig_check() calls mod_split_sig(), which skips the
extraction of the marker ("~Module signature appended~\n") from the end
of the module and instead attempts to read it as an actual
module_signature. The value is then passed to mod_check_sig() which
should return -EBADMSG. The error is propagated to module_sig_check()
and treated as fatal, without consulting module_sig_check() and
security_locked_down().

I think the mangled_module flag should not be passed to mod_split_sig()
and it should be handled solely by module_sig_check().

> diff --git a/kernel/module_signature.c b/kernel/module_signature.c
> index 00132d12487c..b2384a73524c 100644
> --- a/kernel/module_signature.c
> +++ b/kernel/module_signature.c
> @@ -8,6 +8,7 @@
>  
>  #include <linux/errno.h>
>  #include <linux/printk.h>
> +#include <linux/string.h>
>  #include <linux/module_signature.h>
>  #include <asm/byteorder.h>
>  
> @@ -18,8 +19,8 @@
>   * @file_len:	Size of the file to which @ms is appended.
>   * @name:	What is being checked. Used for error messages.
>   */
> -int mod_check_sig(const struct module_signature *ms, size_t file_len,
> -		  const char *name)
> +static int mod_check_sig(const struct module_signature *ms, size_t file_len,
> +			 const char *name)
>  {
>  	if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
>  		return -EBADMSG;
> @@ -44,3 +45,39 @@ int mod_check_sig(const struct module_signature *ms, size_t file_len,
>  
>  	return 0;
>  }
> +
> +int mod_split_sig(const void *buf, size_t *buf_len, bool mangled,
> +		  size_t *sig_len, const u8 **sig, const char *name)
> +{
> +	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
> +	struct module_signature ms;
> +	size_t modlen = *buf_len;
> +	int ret;
> +
> +	/*
> +	 * Do not allow mangled modules as a module with version information
> +	 * removed is no longer the module that was signed.
> +	 */
> +	if (!mangled &&
> +	    *buf_len > markerlen &&
> +	    memcmp(buf + modlen - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
> +		/* We truncate the module to discard the signature */
> +		modlen -= markerlen;
> +	}
> +
> +	if (modlen <= sizeof(ms))
> +		return -EBADMSG;
> +
> +	memcpy(&ms, buf + (modlen - sizeof(ms)), sizeof(ms));
> +
> +	ret = mod_check_sig(&ms, modlen, name);
> +	if (ret)
> +		return ret;
> +
> +	*sig_len = be32_to_cpu(ms.sig_len);
> +	modlen -= *sig_len + sizeof(ms);
> +	*buf_len = modlen;
> +	*sig = buf + modlen;
> +
> +	return 0;
> +}
> diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
> index 3265d744d5ce..a57342d39b07 100644
> --- a/security/integrity/ima/ima_modsig.c
> +++ b/security/integrity/ima/ima_modsig.c
> @@ -40,44 +40,30 @@ struct modsig {
>  int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
>  		    struct modsig **modsig)
>  {
> -	const size_t marker_len = strlen(MODULE_SIG_STRING);
> -	const struct module_signature *sig;
> +	size_t buf_len_sz = buf_len;
>  	struct modsig *hdr;
>  	size_t sig_len;
> -	const void *p;
> +	const u8 *sig;
>  	int rc;
>  
> -	if (buf_len <= marker_len + sizeof(*sig))
> -		return -ENOENT;
> -
> -	p = buf + buf_len - marker_len;
> -	if (memcmp(p, MODULE_SIG_STRING, marker_len))
> -		return -ENOENT;
> -
> -	buf_len -= marker_len;
> -	sig = (const struct module_signature *)(p - sizeof(*sig));
> -
> -	rc = mod_check_sig(sig, buf_len, func_tokens[func]);
> +	rc = mod_split_sig(buf, &buf_len_sz, true, &sig_len, &sig, func_tokens[func]);

Passing mangled=true to mod_split_sig() seems incorrect here. It causes
that the function doesn't properly extract the signature marker at the
end of the module, no?

>  	if (rc)
>  		return rc;
>  
> -	sig_len = be32_to_cpu(sig->sig_len);
> -	buf_len -= sig_len + sizeof(*sig);
> -
>  	/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
>  	hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL);
>  	if (!hdr)
>  		return -ENOMEM;
>  
>  	hdr->raw_pkcs7_len = sig_len;
> -	hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
> +	hdr->pkcs7_msg = pkcs7_parse_message(sig, sig_len);
>  	if (IS_ERR(hdr->pkcs7_msg)) {
>  		rc = PTR_ERR(hdr->pkcs7_msg);
>  		kfree(hdr);
>  		return rc;
>  	}
>  
> -	memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
> +	memcpy(hdr->raw_pkcs7, sig, sig_len);
>  
>  	/* We don't know the hash algorithm yet. */
>  	hdr->hash_algo = HASH_ALGO__LAST;
> 

-- 
Thanks,
Petr

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 12/17] module: Move signature splitting up
  2026-01-13 12:28 ` [PATCH v4 12/17] module: Move signature splitting up Thomas Weißschuh
@ 2026-01-29 14:41   ` Petr Pavlu
  2026-02-03 12:42     ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Petr Pavlu @ 2026-01-29 14:41 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> The signature splitting will also be used by CONFIG_MODULE_HASHES.
> 
> Move it up the callchain, so the result can be reused.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> [...]
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index c09b25c0166a..d65bc300a78c 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -3346,10 +3346,21 @@ static int early_mod_check(struct load_info *info, int flags)
>  
>  static int module_integrity_check(struct load_info *info, int flags)
>  {
> +	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
> +				       MODULE_INIT_IGNORE_VERMAGIC);
> +	size_t sig_len;
> +	const u8 *sig;
>  	int err = 0;
>  
> +	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
> +		err = mod_split_sig(info->hdr, &info->len, mangled_module,
> +				    &sig_len, &sig, "module");
> +		if (err)
> +			return err;
> +	}
> +
>  	if (IS_ENABLED(CONFIG_MODULE_SIG))
> -		err = module_sig_check(info, flags);
> +		err = module_sig_check(info, sig, sig_len);
>  
>  	if (err)
>  		return err;

I suggest moving the IS_ENABLED(CONFIG_MODULE_SIG) block under the
new IS_ENABLED(CONFIG_MODULE_SIG_POLICY) section. I realize that
CONFIG_MODULE_SIG implies CONFIG_MODULE_SIG_POLICY, but I believe this
change makes it more apparent that this it the case. Otherwise, one
might for example wonder if sig_len in the module_sig_check() call can
be undefined.

	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
		err = mod_split_sig(info->hdr, &info->len, mangled_module,
				    &sig_len, &sig, "module");
		if (err)
			return err;

		if (IS_ENABLED(CONFIG_MODULE_SIG))
			err = module_sig_check(info, sig, sig_len);
	}

-- 
Thanks,
Petr

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 13/17] module: Report signature type to users
  2026-01-13 12:28 ` [PATCH v4 13/17] module: Report signature type to users Thomas Weißschuh
@ 2026-01-29 14:44   ` Petr Pavlu
  2026-02-03 12:44     ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Petr Pavlu @ 2026-01-29 14:44 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> The upcoming CONFIG_MODULE_HASHES will introduce a signature type.
> This needs to be handled by callers differently than PKCS7 signatures.
> 
> Report the signature type to the caller and let them verify it.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> [...]
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index d65bc300a78c..2a28a0ece809 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -3348,19 +3348,24 @@ static int module_integrity_check(struct load_info *info, int flags)
>  {
>  	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
>  				       MODULE_INIT_IGNORE_VERMAGIC);
> +	enum pkey_id_type sig_type;
>  	size_t sig_len;
>  	const u8 *sig;
>  	int err = 0;
>  
>  	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
>  		err = mod_split_sig(info->hdr, &info->len, mangled_module,
> -				    &sig_len, &sig, "module");
> +				    &sig_type, &sig_len, &sig, "module");
>  		if (err)
>  			return err;
>  	}
>  
> -	if (IS_ENABLED(CONFIG_MODULE_SIG))
> +	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
>  		err = module_sig_check(info, sig, sig_len);
> +	} else {
> +		pr_err("module: not signed with expected PKCS#7 message\n");
> +		err = -ENOPKG;
> +	}

The new else branch means that if the user chooses not to configure any
module integrity policy, they will no longer be able to load any
modules. I think this entire if-else part should be moved under the
IS_ENABLED(CONFIG_MODULE_SIG_POLICY) block above, as I'm mentioning on
patch #12.

-- 
Thanks,
Petr

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
  2026-01-13 14:56   ` Sebastian Andrzej Siewior
@ 2026-01-30 17:06   ` Petr Pavlu
  2026-02-03 12:55     ` Thomas Weißschuh
  2026-02-03 12:19   ` Petr Pavlu
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 80+ messages in thread
From: Petr Pavlu @ 2026-01-30 17:06 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> Normally the .ko module files depend on a fully built vmlinux to be
> available for modpost validation and BTF generation. With
> CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> to build a merkle tree. This introduces a dependency cycle which is
> impossible to satisfy. Work around this by building the modules during
> link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> but before the final module hashes are

I wonder if this dependency cycle could be resolved by utilizing the
split into vmlinux.unstripped and vmlinux that occurred last year.

The idea is to create the following ordering: vmlinux.unstripped ->
modules -> vmlinux, and to patch in .module_hashes only when building
the final vmlinux.

This would require the following:
* Split scripts/Makefile.vmlinux into two Makefiles, one that builds the
  current vmlinux.unstripped and the second one that builds the final
  vmlinux from it.
* Modify the top Makefile to recognize vmlinux.unstripped and update the
  BTF generation rule 'modules: vmlinux' to
  'modules: vmlinux.unstripped'.
* Add the 'vmlinux: modules' ordering in the top Makefile for
  CONFIG_MODULE_HASHES=y.
* Remove the patching of vmlinux.unstripped in scripts/link-vmlinux.sh
  and instead move it into scripts/Makefile.vmlinux when running objcopy
  to produce the final vmlinux.

I think this approach has two main advantages:
* CONFIG_MODULE_HASHES can be made orthogonal to
  CONFIG_DEBUG_INFO_BTF_MODULES.
* All dependencies are expressed at the Makefile level instead of having
  scripts/link-vmlinux.sh invoke 'make -f Makefile modules'.

Below is a rough prototype that applies on top of this series. It is a
bit verbose due to the splitting of part of scripts/Makefile.vmlinux
into scripts/Makefile.vmlinux_unstripped.

-- 
Thanks,
Petr


diff --git a/Makefile b/Makefile
index 841772a5a260..19a3beb82fa7 100644
--- a/Makefile
+++ b/Makefile
@@ -1259,7 +1259,7 @@ vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
 vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
 	@:
 
-PHONY += vmlinux
+PHONY += vmlinux.unstripped vmlinux
 # LDFLAGS_vmlinux in the top Makefile defines linker flags for the top vmlinux,
 # not for decompressors. LDFLAGS_vmlinux in arch/*/boot/compressed/Makefile is
 # unrelated; the decompressors just happen to have the same base name,
@@ -1270,9 +1270,11 @@ PHONY += vmlinux
 #   https://savannah.gnu.org/bugs/?61463
 # For Make > 4.4, the following simple code will work:
 #  vmlinux: private export LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
-vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
-vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
-vmlinux: vmlinux.o $(KBUILD_LDS) modpost
+vmlinux.unstripped: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
+vmlinux.unstripped: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
+vmlinux.unstripped: vmlinux.o $(KBUILD_LDS) modpost
+	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_unstripped
+vmlinux: vmlinux.unstripped
 	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux
 
 # The actual objects are generated when descending,
@@ -1541,7 +1543,7 @@ all: dtbs
 endif
 
 ifdef CONFIG_GENERIC_BUILTIN_DTB
-vmlinux: dtbs
+vmlinux.unstripped: dtbs
 endif
 
 endif
@@ -1588,9 +1590,11 @@ endif
 # is an exception.
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 KBUILD_BUILTIN := y
-ifndef CONFIG_MODULE_HASHES
-modules: vmlinux
+modules: vmlinux.unstripped
 endif
+
+ifdef CONFIG_MODULE_HASHES
+vmlinux: modules
 endif
 
 modules: modules_prepare
@@ -1983,11 +1987,7 @@ modules.order: $(build-dir)
 # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
 # This is solely useful to speed up test compiles.
 modules: modpost
-ifdef CONFIG_MODULE_HASHES
-ifeq ($(MODULE_HASHES_MODPOST_FINAL), 1)
-	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
-endif
-else ifneq ($(KBUILD_MODPOST_NOFINAL),1)
+ifneq ($(KBUILD_MODPOST_NOFINAL),1)
 	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
 endif
 
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 890724edac69..213e21ecfe0d 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -55,7 +55,7 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
 	$(cmd);                                                              \
 	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
 
-# Re-generate module BTFs if either module's .ko or vmlinux changed
+# Re-generate module BTFs if either module's .ko or vmlinux.unstripped changed
 %.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/.tmp_vmlinux_btf.stamp) FORCE
 	+$(call if_changed_except,ld_ko_o,$(objtree)/.tmp_vmlinux_btf.stamp)
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 4ce849f6253a..8c2a938c88ab 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -15,78 +15,24 @@ targets :=
 %.o: %.S FORCE
 	$(call if_changed_rule,as_o_S)
 
-# Built-in dtb
-# ---------------------------------------------------------------------------
-
-quiet_cmd_wrap_dtbs = WRAP    $@
-      cmd_wrap_dtbs = {							\
-	echo '\#include <asm-generic/vmlinux.lds.h>';			\
-	echo '.section .dtb.init.rodata,"a"';				\
-	while read dtb; do						\
-		symbase=__dtb_$$(basename -s .dtb "$${dtb}" | tr - _);	\
-		echo '.balign STRUCT_ALIGNMENT';			\
-		echo ".global $${symbase}_begin";			\
-		echo "$${symbase}_begin:";				\
-		echo '.incbin "'$$dtb'" ';				\
-		echo ".global $${symbase}_end";				\
-		echo "$${symbase}_end:";				\
-	done < $<;							\
-	} > $@
-
-.builtin-dtbs.S: .builtin-dtbs-list FORCE
-	$(call if_changed,wrap_dtbs)
-
-quiet_cmd_gen_dtbs_list = GEN     $@
-      cmd_gen_dtbs_list = \
-	$(if $(CONFIG_BUILTIN_DTB_NAME), echo "arch/$(SRCARCH)/boot/dts/$(CONFIG_BUILTIN_DTB_NAME).dtb",:) > $@
-
-.builtin-dtbs-list: arch/$(SRCARCH)/boot/dts/dtbs-list FORCE
-	$(call if_changed,$(if $(CONFIG_BUILTIN_DTB_ALL),copy,gen_dtbs_list))
-
-targets += .builtin-dtbs-list
-
-ifdef CONFIG_GENERIC_BUILTIN_DTB
-targets += .builtin-dtbs.S .builtin-dtbs.o
-vmlinux.unstripped: .builtin-dtbs.o
-endif
-
-# vmlinux.unstripped
+# vmlinux
 # ---------------------------------------------------------------------------
 
-ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
-vmlinux.unstripped: arch/$(SRCARCH)/tools/vmlinux.arch.o
-
-arch/$(SRCARCH)/tools/vmlinux.arch.o: vmlinux.o FORCE
-	$(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools $@
-endif
-
-ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
-
-# Final link of vmlinux with optional arch pass after final link
-cmd_link_vmlinux =							\
-	$< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@";	\
-	$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
+ifdef CONFIG_MODULE_HASHES
+targets += .tmp_module_hashes.o
+.tmp_module_hashes.o: .tmp_module_hashes.c FORCE
 
-targets += vmlinux.unstripped .vmlinux.export.o
-vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o .vmlinux.export.o $(KBUILD_LDS) FORCE
-	+$(call if_changed_dep,link_vmlinux)
-ifdef CONFIG_DEBUG_INFO_BTF
-vmlinux.unstripped: $(RESOLVE_BTFIDS)
-endif
+quiet_cmd_module_hashes = OBJCOPY $@
+      cmd_module_hashes = $(OBJCOPY) --dump-section .module_hashes=$@ $<
 
-ifdef CONFIG_BUILDTIME_TABLE_SORT
-vmlinux.unstripped: scripts/sorttable
-endif
+targets += .tmp_module_hashes.bin
+.tmp_module_hashes.bin: .tmp_module_hashes.o FORCE
+	$(call if_changed,module_hashes)
 
-ifdef CONFIG_MODULE_HASHES
-vmlinux.unstripped: $(objtree)/scripts/modules-merkle-tree
-vmlinux.unstripped: modules.order
-vmlinux.unstripped: $(wildcard include/config/MODULE_INSTALL_STRIP)
+vmlinux: .tmp_module_hashes.bin
+patch-module-hashes := --update-section .module_hashes=.tmp_module_hashes.bin
 endif
 
-# vmlinux
-# ---------------------------------------------------------------------------
-
 remove-section-y                                   := .modinfo
 remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn'
 # for compatibility with binutils < 2.32
@@ -98,70 +44,15 @@ remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
 # To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
 # it is necessary to remove the PT_LOAD flag from the segment.
 quiet_cmd_strip_relocs = OBJCOPY $@
-      cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
-                         $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
+      cmd_script_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
+                          $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) \
+                                     $(remove-symbols) \
+                                     $(patch-module-hashes) $@
 
 targets += vmlinux
 vmlinux: vmlinux.unstripped FORCE
 	$(call if_changed,strip_relocs)
 
-# modules.builtin.modinfo
-# ---------------------------------------------------------------------------
-
-# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
-# tools that expect vmlinux to have sufficiently aligned sections but the
-# additional bytes used for padding .modinfo to satisfy this requirement break
-# certain versions of kmod with
-#
-#   depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
-#
-# Strip the trailing padding bytes after extracting .modinfo to comply with
-# what kmod expects to parse.
-quiet_cmd_modules_builtin_modinfo = GEN     $@
-      cmd_modules_builtin_modinfo = $(cmd_objcopy); \
-                                    sed -i 's/\x00\+$$/\x00/g' $@
-
-OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
-
-targets += modules.builtin.modinfo
-modules.builtin.modinfo: vmlinux.unstripped FORCE
-	$(call if_changed,modules_builtin_modinfo)
-
-# modules.builtin
-# ---------------------------------------------------------------------------
-
-__default: modules.builtin
-
-# The second line aids cases where multiple modules share the same object.
-
-quiet_cmd_modules_builtin = GEN     $@
-      cmd_modules_builtin = \
-	tr '\0' '\n' < $< | \
-	sed -n 's/^[[:alnum:]:_]*\.file=//p' | \
-	tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@
-
-targets += modules.builtin
-modules.builtin: modules.builtin.modinfo FORCE
-	$(call if_changed,modules_builtin)
-
-# modules.builtin.ranges
-# ---------------------------------------------------------------------------
-ifdef CONFIG_BUILTIN_MODULE_RANGES
-__default: modules.builtin.ranges
-
-quiet_cmd_modules_builtin_ranges = GEN     $@
-      cmd_modules_builtin_ranges = gawk -f $(real-prereqs) > $@
-
-targets += modules.builtin.ranges
-modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
-			modules.builtin vmlinux.map vmlinux.o.map FORCE
-	$(call if_changed,modules_builtin_ranges)
-
-vmlinux.map: vmlinux.unstripped
-	@:
-
-endif
-
 # Add FORCE to the prerequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
 
diff --git a/scripts/Makefile.vmlinux_unstripped b/scripts/Makefile.vmlinux_unstripped
new file mode 100644
index 000000000000..914ee6f3b935
--- /dev/null
+++ b/scripts/Makefile.vmlinux_unstripped
@@ -0,0 +1,159 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+PHONY := __default
+__default: vmlinux.unstripped
+
+include include/config/auto.conf
+include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
+
+targets :=
+
+%.o: %.c FORCE
+	$(call if_changed_rule,cc_o_c)
+
+%.o: %.S FORCE
+	$(call if_changed_rule,as_o_S)
+
+# Built-in dtb
+# ---------------------------------------------------------------------------
+
+quiet_cmd_wrap_dtbs = WRAP    $@
+      cmd_wrap_dtbs = {							\
+	echo '\#include <asm-generic/vmlinux.lds.h>';			\
+	echo '.section .dtb.init.rodata,"a"';				\
+	while read dtb; do						\
+		symbase=__dtb_$$(basename -s .dtb "$${dtb}" | tr - _);	\
+		echo '.balign STRUCT_ALIGNMENT';			\
+		echo ".global $${symbase}_begin";			\
+		echo "$${symbase}_begin:";				\
+		echo '.incbin "'$$dtb'" ';				\
+		echo ".global $${symbase}_end";				\
+		echo "$${symbase}_end:";				\
+	done < $<;							\
+	} > $@
+
+.builtin-dtbs.S: .builtin-dtbs-list FORCE
+	$(call if_changed,wrap_dtbs)
+
+quiet_cmd_gen_dtbs_list = GEN     $@
+      cmd_gen_dtbs_list = \
+	$(if $(CONFIG_BUILTIN_DTB_NAME), echo "arch/$(SRCARCH)/boot/dts/$(CONFIG_BUILTIN_DTB_NAME).dtb",:) > $@
+
+.builtin-dtbs-list: arch/$(SRCARCH)/boot/dts/dtbs-list FORCE
+	$(call if_changed,$(if $(CONFIG_BUILTIN_DTB_ALL),copy,gen_dtbs_list))
+
+targets += .builtin-dtbs-list
+
+ifdef CONFIG_GENERIC_BUILTIN_DTB
+targets += .builtin-dtbs.S .builtin-dtbs.o
+vmlinux.unstripped: .builtin-dtbs.o
+endif
+
+# vmlinux.unstripped
+# ---------------------------------------------------------------------------
+
+ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
+vmlinux.unstripped: arch/$(SRCARCH)/tools/vmlinux.arch.o
+
+arch/$(SRCARCH)/tools/vmlinux.arch.o: vmlinux.o FORCE
+	$(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools $@
+endif
+
+ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
+
+# Final link of vmlinux with optional arch pass after final link
+cmd_link_vmlinux =							\
+	$< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@";	\
+	$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
+
+targets += vmlinux.unstripped .vmlinux.export.o
+vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o .vmlinux.export.o $(KBUILD_LDS) FORCE
+	+$(call if_changed_dep,link_vmlinux)
+ifdef CONFIG_DEBUG_INFO_BTF
+vmlinux.unstripped: $(RESOLVE_BTFIDS)
+endif
+
+ifdef CONFIG_BUILDTIME_TABLE_SORT
+vmlinux.unstripped: scripts/sorttable
+endif
+
+ifdef CONFIG_MODULE_HASHES
+vmlinux.unstripped: $(objtree)/scripts/modules-merkle-tree
+vmlinux.unstripped: modules.order
+vmlinux.unstripped: $(wildcard include/config/MODULE_INSTALL_STRIP)
+endif
+
+# modules.builtin.modinfo
+# ---------------------------------------------------------------------------
+
+# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
+# tools that expect vmlinux to have sufficiently aligned sections but the
+# additional bytes used for padding .modinfo to satisfy this requirement break
+# certain versions of kmod with
+#
+#   depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
+#
+# Strip the trailing padding bytes after extracting .modinfo to comply with
+# what kmod expects to parse.
+quiet_cmd_modules_builtin_modinfo = GEN     $@
+      cmd_modules_builtin_modinfo = $(cmd_objcopy); \
+                                    sed -i 's/\x00\+$$/\x00/g' $@
+
+OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
+
+targets += modules.builtin.modinfo
+modules.builtin.modinfo: vmlinux.unstripped FORCE
+	$(call if_changed,modules_builtin_modinfo)
+
+# modules.builtin
+# ---------------------------------------------------------------------------
+
+__default: modules.builtin
+
+# The second line aids cases where multiple modules share the same object.
+
+quiet_cmd_modules_builtin = GEN     $@
+      cmd_modules_builtin = \
+	tr '\0' '\n' < $< | \
+	sed -n 's/^[[:alnum:]:_]*\.file=//p' | \
+	tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@
+
+targets += modules.builtin
+modules.builtin: modules.builtin.modinfo FORCE
+	$(call if_changed,modules_builtin)
+
+# modules.builtin.ranges
+# ---------------------------------------------------------------------------
+ifdef CONFIG_BUILTIN_MODULE_RANGES
+__default: modules.builtin.ranges
+
+quiet_cmd_modules_builtin_ranges = GEN     $@
+      cmd_modules_builtin_ranges = gawk -f $(real-prereqs) > $@
+
+targets += modules.builtin.ranges
+modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
+			modules.builtin vmlinux.map vmlinux.o.map FORCE
+	$(call if_changed,modules_builtin_ranges)
+
+vmlinux.map: vmlinux.unstripped
+	@:
+
+endif
+
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+FORCE:
+
+# Read all saved command lines and dependencies for the $(targets) we
+# may be building above, using $(if_changed{,_dep}). As an
+# optimization, we don't need to read them if the target does not
+# exist, we will rebuild anyway in that case.
+
+existing-targets := $(wildcard $(sort $(targets)))
+
+-include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+
+.PHONY: $(PHONY)
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index bfeff1f5753d..80cb09707426 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -316,17 +316,6 @@ if is_enabled CONFIG_BUILDTIME_TABLE_SORT; then
 	fi
 fi
 
-if is_enabled CONFIG_MODULE_HASHES; then
-	info MAKE modules
-	${MAKE} -f Makefile MODULE_HASHES_MODPOST_FINAL=1 modules
-	module_hashes_o=.tmp_module_hashes.o
-	info CC ${module_hashes_o}
-	${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} \
-		${KBUILD_CFLAGS_KERNEL} -fno-lto -c -o "${module_hashes_o}" ".tmp_module_hashes.c"
-	${OBJCOPY} --dump-section .module_hashes=.tmp_module_hashes.bin ${module_hashes_o}
-	${OBJCOPY} --update-section .module_hashes=.tmp_module_hashes.bin ${VMLINUX}
-fi
-
 # step a (see comment above)
 if is_enabled CONFIG_KALLSYMS; then
 	if ! cmp -s System.map "${kallsyms_sysmap}"; then

^ permalink raw reply related	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
  2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
@ 2026-01-30 20:43   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Aaron Tomlin @ 2026-01-30 20:43 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

[-- Attachment #1: Type: text/plain, Size: 1163 bytes --]

On Tue, Jan 13, 2026 at 01:28:46PM +0100, Thomas Weißschuh wrote:
> When CONFIG_MODULE_SIG is disabled set_module_sig_enforced() is defined
> as an empty stub, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the check avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  arch/powerpc/kernel/ima_arch.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/ima_arch.c b/arch/powerpc/kernel/ima_arch.c
> index b7029beed847..690263bf4265 100644
> --- a/arch/powerpc/kernel/ima_arch.c
> +++ b/arch/powerpc/kernel/ima_arch.c
> @@ -63,8 +63,7 @@ static const char *const secure_and_trusted_rules[] = {
>  const char *const *arch_get_ima_policy(void)
>  {
>  	if (is_ppc_secureboot_enabled()) {
> -		if (IS_ENABLED(CONFIG_MODULE_SIG))
> -			set_module_sig_enforced();
> +		set_module_sig_enforced();
>  
>  		if (is_ppc_trustedboot_enabled())
>  			return secure_and_trusted_rules;
> 
> -- 
> 2.52.0
> 

Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>

-- 
Aaron Tomlin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
  2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
@ 2026-01-30 20:49   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Aaron Tomlin @ 2026-01-30 20:49 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

[-- Attachment #1: Type: text/plain, Size: 1280 bytes --]

On Tue, Jan 13, 2026 at 01:28:47PM +0100, Thomas Weißschuh wrote:
> When configuration settings are disabled the guarded functions are
> defined as empty stubs, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the checks avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  security/integrity/ima/ima_efi.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
> index 138029bfcce1..a35dd166ad47 100644
> --- a/security/integrity/ima/ima_efi.c
> +++ b/security/integrity/ima/ima_efi.c
> @@ -68,10 +68,8 @@ static const char * const sb_arch_rules[] = {
>  const char * const *arch_get_ima_policy(void)
>  {
>  	if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
> -		if (IS_ENABLED(CONFIG_MODULE_SIG))
> -			set_module_sig_enforced();
> -		if (IS_ENABLED(CONFIG_KEXEC_SIG))
> -			set_kexec_sig_enforced();
> +		set_module_sig_enforced();
> +		set_kexec_sig_enforced();
>  		return sb_arch_rules;
>  	}
>  	return NULL;
> 
> -- 
> 2.52.0
> 

Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>

-- 
Aaron Tomlin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 04/17] module: Make mod_verify_sig() static
  2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
@ 2026-01-30 20:53   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:12   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Aaron Tomlin @ 2026-01-30 20:53 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

[-- Attachment #1: Type: text/plain, Size: 1444 bytes --]

On Tue, Jan 13, 2026 at 01:28:48PM +0100, Thomas Weißschuh wrote:
> It is not used outside of signing.c.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  kernel/module/internal.h | 1 -
>  kernel/module/signing.c  | 2 +-
>  2 files changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index 618202578b42..e68fbcd60c35 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -119,7 +119,6 @@ struct module_use {
>  	struct module *source, *target;
>  };
>  
> -int mod_verify_sig(const void *mod, struct load_info *info);
>  int try_to_force_load(struct module *mod, const char *reason);
>  bool find_symbol(struct find_symbol_arg *fsa);
>  struct module *find_module_all(const char *name, size_t len, bool even_unformed);
> diff --git a/kernel/module/signing.c b/kernel/module/signing.c
> index a2ff4242e623..fe3f51ac6199 100644
> --- a/kernel/module/signing.c
> +++ b/kernel/module/signing.c
> @@ -40,7 +40,7 @@ void set_module_sig_enforced(void)
>  /*
>   * Verify the signature on a module.
>   */
> -int mod_verify_sig(const void *mod, struct load_info *info)
> +static int mod_verify_sig(const void *mod, struct load_info *info)
>  {
>  	struct module_signature ms;
>  	size_t sig_len, modlen = info->len;
> 
> -- 
> 2.52.0
> 

Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>

-- 
Aaron Tomlin

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                   ` (16 preceding siblings ...)
  2026-01-13 12:29 ` [PATCH v4 17/17] kbuild: make CONFIG_MODULE_HASHES compatible with module stripping Thomas Weißschuh
@ 2026-01-31  7:36 ` Mihai-Drosi Câju
  2026-02-01 16:22   ` Thomas Weißschuh
  2026-02-01 17:09   ` David Howells
  17 siblings, 2 replies; 80+ messages in thread
From: Mihai-Drosi Câju @ 2026-01-31  7:36 UTC (permalink / raw)
  To: linux
  Cc: arnd, arnout, atomlin, bigeasy, chleroy, christian, corbet, coxu,
	da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
	f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
	linux-integrity, linux-kbuild, linux-kernel, linux-modules,
	linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcaju95,
	mcgrof, mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier,
	npiggin, nsc, paul, petr.pavlu, roberto.sassu, samitolvanen,
	serge, xiujianfeng, zohar

> The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.

I think there is a middle ground where the module signing key is generated
using a key derivation function that has as an input a deterministic value
on the build host, such as /etc/machine-id . The problem with this approach
is that only hosts knowing the value will be able to reproduce the build.

Maybe this is a solution to NixOS secret management? Introduce minimal
impurity as a cryptographic seed and derive the rest of the secrets using
something like Argon2(seed, key_uuid).

There might be another approach to code integrity rather than step-by-step
reproducibility. One may exploit the very cryptographic primitives that make
reproducibility hard to ensure that reproducibility is most  likely valid.

For example, the module signing issue, the build host publishes four artifacts:
* The source-code
* The compiled and signed binary
* The build environment
* Its public key

Now, we don't need to sign with the private key to know that building the source
code using the specific build environment and signing the result with the private
key will result in the claimed binary. We can just compile and verify with the
public key.

So a traditional workflow would be:
compiled_module + module_signature == module

In this case we build the module, sign it with whatever key, distribute the
builds and the private key to whoever wants to reproduce the build. Or we build
locally and the key stays with the end-user.

While the cryptographic approach would be:
verify(compiled_code, module.signature) is True

In this case we distribute the builds, source code and the public key. While
everyone can ensure that the compiled code is the result of the build
environment and source code. The signature is verified using cryptographic
means.

As long as no one cracks RSA or an algorithm of our choosing/has an absurd
amount of luck, the cryptographic approach would be just as good as the traditional
approach at ensuring that a program has stopped with a certain output.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-01-31  7:36 ` [PATCH v4 00/17] module: Introduce hash-based integrity checking Mihai-Drosi Câju
@ 2026-02-01 16:22   ` Thomas Weißschuh
  2026-02-01 17:09   ` David Howells
  1 sibling, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-01 16:22 UTC (permalink / raw)
  To: Mihai-Drosi Câju
  Cc: arnd, arnout, atomlin, bigeasy, chleroy, christian, corbet, coxu,
	da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
	f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
	linux-integrity, linux-kbuild, linux-kernel, linux-modules,
	linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcgrof,
	mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier, npiggin,
	nsc, paul, petr.pavlu, roberto.sassu, samitolvanen, serge,
	xiujianfeng, zohar

Hi Mihai-Drosi,

thanks for taking an interest into these patches!

On 2026-01-31 09:36:36+0200, Mihai-Drosi Câju wrote:
> > The current signature-based module integrity checking has some drawbacks
> > in combination with reproducible builds. Either the module signing key
> > is generated at build time, which makes the build unreproducible, or a
> > static signing key is used, which precludes rebuilds by third parties
> > and makes the whole build and packaging process much more complicated.
> 
> I think there is a middle ground where the module signing key is generated
> using a key derivation function that has as an input a deterministic value
> on the build host, such as /etc/machine-id . The problem with this approach
> is that only hosts knowing the value will be able to reproduce the build.

The goal is to make the distro kernel packages rebuildable by the
general public. Any involvement of secret values will break this goal.

> Maybe this is a solution to NixOS secret management? Introduce minimal
> impurity as a cryptographic seed and derive the rest of the secrets using
> something like Argon2(seed, key_uuid).

I am not familiar with NixOS and its secret management.
This patchset serves a wider audience.

> There might be another approach to code integrity rather than step-by-step
> reproducibility. One may exploit the very cryptographic primitives that make
> reproducibility hard to ensure that reproducibility is most  likely valid.
> 
> For example, the module signing issue, the build host publishes four artifacts:
> * The source-code
> * The compiled and signed binary
> * The build environment
> * Its public key
> 
> Now, we don't need to sign with the private key to know that building the source
> code using the specific build environment and signing the result with the private
> key will result in the claimed binary. We can just compile and verify with the
> public key.

This could work if the goal is only to verify the reproducibility of a
single, signed-en-bloc artifact. But we also need to handle vmlinux which
contains the corresponding public key. It would need different handling.
We can add some special logic to strip that public key before
comparision. But then vmlinux might be compressed or wrapped in some
other format. Another whole collection of special logic.

(...)


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-01-31  7:36 ` [PATCH v4 00/17] module: Introduce hash-based integrity checking Mihai-Drosi Câju
  2026-02-01 16:22   ` Thomas Weißschuh
@ 2026-02-01 17:09   ` David Howells
  2026-02-01 20:12     ` Eric Biggers
  2026-02-03  8:18     ` James Bottomley
  1 sibling, 2 replies; 80+ messages in thread
From: David Howells @ 2026-02-01 17:09 UTC (permalink / raw)
  To: =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=
  Cc: dhowells, linux, arnd, arnout, atomlin, bigeasy, chleroy,
	christian, corbet, coxu, da.gomez, da.gomez, dmitry.kasatkin,
	eric.snowberg, f.gruenbichler, jmorris, kpcyrd, linux-arch,
	linux-doc, linux-integrity, linux-kbuild, linux-kernel,
	linux-modules, linux-security-module, linuxppc-dev, lkp, maddy,
	mattia, mcgrof, mpe, nathan, naveen, nicolas.bouchinet,
	nicolas.schier, npiggin, nsc, paul, petr.pavlu, roberto.sassu,
	samitolvanen, serge, xiujianfeng, zohar

Mihai-Drosi Câju <mcaju95@gmail.com> wrote:

> > The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.

There is another issue too: If you have a static private key that you use to
sign modules (and probably other things), someone will likely give you a GPL
request to get it.

One advantage of using a transient key every build and deleting it after is
that no one has the key.

One other thing to remember: security is *meant* to get in the way.  That's
the whole point of it.

However, IANAL.

David


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-01 17:09   ` David Howells
@ 2026-02-01 20:12     ` Eric Biggers
  2026-02-02  9:21       ` David Howells
  2026-02-03  8:18     ` James Bottomley
  1 sibling, 1 reply; 80+ messages in thread
From: Eric Biggers @ 2026-02-01 20:12 UTC (permalink / raw)
  To: David Howells
  Cc: =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=, linux, arnd, arnout, atomlin,
	bigeasy, chleroy, christian, corbet, coxu, da.gomez, da.gomez,
	dmitry.kasatkin, eric.snowberg, f.gruenbichler, jmorris, kpcyrd,
	linux-arch, linux-doc, linux-integrity, linux-kbuild,
	linux-kernel, linux-modules, linux-security-module, linuxppc-dev,
	lkp, maddy, mattia, mcgrof, mpe, nathan, naveen,
	nicolas.bouchinet, nicolas.schier, npiggin, nsc, paul, petr.pavlu,
	roberto.sassu, samitolvanen, serge, xiujianfeng, zohar

On Sun, Feb 01, 2026 at 05:09:48PM +0000, David Howells wrote:
> Mihai-Drosi Câju <mcaju95@gmail.com> wrote:
> 
> > > The current signature-based module integrity checking has some drawbacks
> > in combination with reproducible builds. Either the module signing key
> > is generated at build time, which makes the build unreproducible, or a
> > static signing key is used, which precludes rebuilds by third parties
> > and makes the whole build and packaging process much more complicated.
> 
> There is another issue too: If you have a static private key that you use to
> sign modules (and probably other things), someone will likely give you a GPL
> request to get it.
> 
> One advantage of using a transient key every build and deleting it after is
> that no one has the key.
> 
> One other thing to remember: security is *meant* to get in the way.  That's
> the whole point of it.
> 
> However, IANAL.
> 
> David

It sounds like hash-based module authentication is just better, then.
If the full set of authentic modules is known at kernel build time, then
signatures are unnecessary to verify their authenticity: a list of
hashes built into the kernel image is perfectly sufficient.

(This patchset actually gets a little fancy and makes it a Merkle tree
root.  But it could be simplified to just a list of hashes.)

With that being the case, why is there still effort being put into
adding more features to module signing?  I would think efforts should be
focused on hash-based module authentication, i.e. this patchset.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-01 20:12     ` Eric Biggers
@ 2026-02-02  9:21       ` David Howells
  2026-02-02 18:30         ` Eric Biggers
  0 siblings, 1 reply; 80+ messages in thread
From: David Howells @ 2026-02-02  9:21 UTC (permalink / raw)
  To: Eric Biggers
  Cc: dhowells, =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=, linux, arnd,
	arnout, atomlin, bigeasy, chleroy, christian, corbet, coxu,
	da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
	f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
	linux-integrity, linux-kbuild, linux-kernel, linux-modules,
	linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcgrof,
	mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier, npiggin,
	nsc, paul, petr.pavlu, roberto.sassu, samitolvanen, serge,
	xiujianfeng, zohar

Eric Biggers <ebiggers@kernel.org> wrote:

> With that being the case, why is there still effort being put into
> adding more features to module signing?  I would think efforts should be
> focused on hash-based module authentication, i.e. this patchset.

Because it's not just signing of modules and it's not just modules built with
the kernel.  Also a hash table just of module hashes built into the core
kernel image will increase the size of the kernel by around a third of a meg
(on Fedora 43 and assuming SHA512) with uncompressible data.

David


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-02  9:21       ` David Howells
@ 2026-02-02 18:30         ` Eric Biggers
  2026-02-02 18:38           ` David Howells
  0 siblings, 1 reply; 80+ messages in thread
From: Eric Biggers @ 2026-02-02 18:30 UTC (permalink / raw)
  To: David Howells
  Cc: =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=, linux, arnd, arnout, atomlin,
	bigeasy, chleroy, christian, corbet, coxu, da.gomez, da.gomez,
	dmitry.kasatkin, eric.snowberg, f.gruenbichler, jmorris, kpcyrd,
	linux-arch, linux-doc, linux-integrity, linux-kbuild,
	linux-kernel, linux-modules, linux-security-module, linuxppc-dev,
	lkp, maddy, mattia, mcgrof, mpe, nathan, naveen,
	nicolas.bouchinet, nicolas.schier, npiggin, nsc, paul, petr.pavlu,
	roberto.sassu, samitolvanen, serge, xiujianfeng, zohar

On Mon, Feb 02, 2026 at 09:21:19AM +0000, David Howells wrote:
> Eric Biggers <ebiggers@kernel.org> wrote:
> 
> > With that being the case, why is there still effort being put into
> > adding more features to module signing?  I would think efforts should be
> > focused on hash-based module authentication, i.e. this patchset.
> 
> Because it's not just signing of modules

Module signing is indeed about the signing of modules.

> and it's not just modules built with the kernel.

Could you give more details on this use case and why it needs
signatures, as opposed to e.g. loading an additional Merkle tree root
into the kernel to add to the set of allowed modules?

> Also a hash table just of module hashes built into the core
> kernel image will increase the size of the kernel by around a third of a meg
> (on Fedora 43 and assuming SHA512) with uncompressible data.

This patchset already optimizes it to use Merkle tree proofs instead.
While I'm a bit skeptical of the complexity myself (and distros
shouldn't be shipping such an excessively large number of modules in the
first place), if it's indeed needed it's already been solved.  It's
still much simpler than the PKCS#7 signature mess.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-02 18:30         ` Eric Biggers
@ 2026-02-02 18:38           ` David Howells
  2026-02-02 18:47             ` Eric Biggers
  0 siblings, 1 reply; 80+ messages in thread
From: David Howells @ 2026-02-02 18:38 UTC (permalink / raw)
  To: Eric Biggers
  Cc: dhowells, =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=, linux, arnd,
	arnout, atomlin, bigeasy, chleroy, christian, corbet, coxu,
	da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
	f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
	linux-integrity, linux-kbuild, linux-kernel, linux-modules,
	linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcgrof,
	mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier, npiggin,
	nsc, paul, petr.pavlu, roberto.sassu, samitolvanen, serge,
	xiujianfeng, zohar

Eric Biggers <ebiggers@kernel.org> wrote:

> On Mon, Feb 02, 2026 at 09:21:19AM +0000, David Howells wrote:
> > Eric Biggers <ebiggers@kernel.org> wrote:
> > 
> > > With that being the case, why is there still effort being put into
> > > adding more features to module signing?  I would think efforts should be
> > > focused on hash-based module authentication, i.e. this patchset.
> > 
> > Because it's not just signing of modules
> 
> Module signing is indeed about the signing of modules.

The signature verification stuff in the kernel isn't just used for modules.
kexec, for instance; wifi restriction database for another.

> > and it's not just modules built with the kernel.
> 
> Could you give more details on this use case and why it needs
> signatures, as opposed to e.g. loading an additional Merkle tree root
> into the kernel to add to the set of allowed modules?

Because we don't want to, for example, include all the nvidia drivers in our
kernel SRPM.

David


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-02 18:38           ` David Howells
@ 2026-02-02 18:47             ` Eric Biggers
  0 siblings, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-02-02 18:47 UTC (permalink / raw)
  To: David Howells
  Cc: =?UTF-8?q?Mihai-Drosi=20C=C3=A2ju?=, linux, arnd, arnout, atomlin,
	bigeasy, chleroy, christian, corbet, coxu, da.gomez, da.gomez,
	dmitry.kasatkin, eric.snowberg, f.gruenbichler, jmorris, kpcyrd,
	linux-arch, linux-doc, linux-integrity, linux-kbuild,
	linux-kernel, linux-modules, linux-security-module, linuxppc-dev,
	lkp, maddy, mattia, mcgrof, mpe, nathan, naveen,
	nicolas.bouchinet, nicolas.schier, npiggin, nsc, paul, petr.pavlu,
	roberto.sassu, samitolvanen, serge, xiujianfeng, zohar

On Mon, Feb 02, 2026 at 06:38:51PM +0000, David Howells wrote:
> > Could you give more details on this use case and why it needs
> > signatures, as opposed to e.g. loading an additional Merkle tree root
> > into the kernel to add to the set of allowed modules?
> 
> Because we don't want to, for example, include all the nvidia drivers in our
> kernel SRPM.

That doesn't answer my question.  Are you trying to say these modules
need to be built later *and* signed using the original signing key?

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-01 17:09   ` David Howells
  2026-02-01 20:12     ` Eric Biggers
@ 2026-02-03  8:18     ` James Bottomley
  2026-02-03  8:22       ` David Howells
  1 sibling, 1 reply; 80+ messages in thread
From: James Bottomley @ 2026-02-03  8:18 UTC (permalink / raw)
  To: David Howells, Mihai-Drosi Câju
  Cc: linux, arnd, arnout, atomlin, bigeasy, chleroy, christian, corbet,
	coxu, da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
	f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
	linux-integrity, linux-kbuild, linux-kernel, linux-modules,
	linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcgrof,
	mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier, npiggin,
	nsc, paul, petr.pavlu, roberto.sassu, samitolvanen, serge,
	xiujianfeng, zohar

On Sun, 2026-02-01 at 17:09 +0000, David Howells wrote:
> Mihai-Drosi Câju <mcaju95@gmail.com> wrote:
> 
> > > The current signature-based module integrity checking has some
> > > drawbacks
> > in combination with reproducible builds. Either the module signing
> > key is generated at build time, which makes the build
> > unreproducible, or a static signing key is used, which precludes
> > rebuilds by third parties and makes the whole build and packaging
> > process much more complicated.
> 
> There is another issue too: If you have a static private key that you
> use to sign modules (and probably other things), someone will likely
> give you a GPL request to get it.

The SFC just lost that exact point in the Vizio trial, so I think
you're wrong on this under US law at least.  There's no general ability
under GPLv2 to demand long lived signing keys.

Regards,

James


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
  2026-02-03  8:18     ` James Bottomley
@ 2026-02-03  8:22       ` David Howells
  0 siblings, 0 replies; 80+ messages in thread
From: David Howells @ 2026-02-03  8:22 UTC (permalink / raw)
  To: James Bottomley
  Cc: dhowells, Mihai-Drosi Câju, linux, arnd, arnout, atomlin,
	bigeasy, chleroy, christian, corbet, coxu, da.gomez, da.gomez,
	dmitry.kasatkin, eric.snowberg, f.gruenbichler, jmorris, kpcyrd,
	linux-arch, linux-doc, linux-integrity, linux-kbuild,
	linux-kernel, linux-modules, linux-security-module, linuxppc-dev,
	lkp, maddy, mattia, mcgrof, mpe, nathan, naveen,
	nicolas.bouchinet, nicolas.schier, npiggin, nsc, paul, petr.pavlu,
	roberto.sassu, samitolvanen, serge, xiujianfeng, zohar


James Bottomley <James.Bottomley@HansenPartnership.com> wrote:

> > There is another issue too: If you have a static private key that you
> > use to sign modules (and probably other things), someone will likely
> > give you a GPL request to get it.
> 
> The SFC just lost that exact point in the Vizio trial, so I think
> you're wrong on this under US law at least.  There's no general ability
> under GPLv2 to demand long lived signing keys.

Cool :-).  I just know that I've been sent GPL requests for kernel keys.

David


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
  2026-01-13 14:56   ` Sebastian Andrzej Siewior
  2026-01-30 17:06   ` Petr Pavlu
@ 2026-02-03 12:19   ` Petr Pavlu
  2026-02-03 12:59     ` Thomas Weißschuh
  2026-03-11  1:18     ` Eric Biggers
  2026-02-21 21:38   ` Nicolas Schier
  2026-03-11  1:12   ` Eric Biggers
  4 siblings, 2 replies; 80+ messages in thread
From: Petr Pavlu @ 2026-02-03 12:19 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.
> 
> The goal is to reach bit-for-bit reproducibility. Excluding certain
> parts of the build output from the reproducibility analysis would be
> error-prone and force each downstream consumer to introduce new tooling.
> 
> Introduce a new mechanism to ensure only well-known modules are loaded
> by embedding a merkle tree root of all modules built as part of the full
> kernel build into vmlinux.
> 
> Non-builtin modules can be validated as before through signatures.
> 
> Normally the .ko module files depend on a fully built vmlinux to be
> available for modpost validation and BTF generation. With
> CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> to build a merkle tree. This introduces a dependency cycle which is
> impossible to satisfy. Work around this by building the modules during
> link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> but before the final module hashes are
> 
> The PKCS7 format which is used for regular module signatures can not
> represent Merkle proofs, so a new kind of module signature is
> introduced. As this signature type is only ever used for builtin
> modules, no compatibility issues can arise.

Nit: The description uses the term "builtin modules" in a misleading
way. Typically, "builtin modules" refers to modules that are linked
directly into vmlinux. However, this text uses the term to refer to
loadable modules that are built together with the main kernel image,
which is something different.

> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> new file mode 100644
> index 000000000000..a6ec0e21213b
> --- /dev/null
> +++ b/scripts/modules-merkle-tree.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Compute hashes for modules files and build a merkle tree.
> + *
> + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> + *
> + */
> +#define _GNU_SOURCE 1
> +#include <arpa/inet.h>
> +#include <err.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include <openssl/evp.h>
> +#include <openssl/err.h>
> +
> +#include "ssl-common.h"
> +
> +static int hash_size;
> +static EVP_MD_CTX *ctx;
> +
> +struct module_signature {
> +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> +	uint8_t		hash;		/* Digest algorithm [0] */
> +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> +	uint8_t		signer_len;	/* Length of signer's name [0] */
> +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> +	uint8_t		__pad[3];
> +	uint32_t	sig_len;	/* Length of signature data */
> +};
> +
> +#define PKEY_ID_MERKLE 3
> +
> +static const char magic_number[] = "~Module signature appended~\n";

It might make sense to put these common structures into a file under
scripts/include/ so they can be shared by both scripts/sign-file.c and
scripts/modules-merkle-tree.c.

> +
> +struct file_entry {
> +	char *name;
> +	unsigned int pos;
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> +	struct leaf_hash **l;
> +	unsigned int *entries;
> +	unsigned int levels;
> +};
> +
> +static inline void *xcalloc(size_t n, size_t size)
> +{
> +	void *p;
> +
> +	p = calloc(n, size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static void *xmalloc(size_t size)
> +{
> +	void *p;
> +
> +	p = malloc(size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static inline void *xreallocarray(void *oldp, size_t n, size_t size)
> +{
> +	void *p;
> +
> +	p = reallocarray(oldp, n, size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static inline char *xasprintf(const char *fmt, ...)
> +{
> +	va_list ap;
> +	char *strp;
> +	int ret;
> +
> +	va_start(ap, fmt);
> +	ret = vasprintf(&strp, fmt, ap);
> +	va_end(ap);
> +	if (ret == -1)
> +		err(1, "Memory allocation failed");
> +
> +	return strp;
> +}

I believe it is preferable to use xmalloc() and related functions from
scripts/include/xalloc.h, instead of defining your own variants. If
something is missing in xalloc.h, it can be extended.

> +
> +static unsigned int get_pow2(unsigned int val)
> +{
> +	return 31 - __builtin_clz(val);
> +}
> +
> +static unsigned int roundup_pow2(unsigned int val)
> +{
> +	return 1 << (get_pow2(val - 1) + 1);
> +}
> +
> +static unsigned int log2_roundup(unsigned int val)
> +{
> +	return get_pow2(roundup_pow2(val));
> +}

In the edge case when the kernel is built with only one module, the code
calls log2_roundup(1) -> roundup_pow2(1) -> get_pow2(0) ->
__builtin_clz(0). The return value of __builtin_clz() is undefined if
the input is zero.

> +
> +static void hash_data(void *p, unsigned int pos, size_t size, void *ret_hash)
> +{
> +	unsigned char magic = 0x01;
> +	unsigned int pos_be;
> +
> +	pos_be = htonl(pos);
> +
> +	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
> +	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
> +	ERR(EVP_DigestUpdate(ctx, &pos_be, sizeof(pos_be)) != 1, "EVP_DigestUpdate(pos)");
> +	ERR(EVP_DigestUpdate(ctx, p, size) != 1, "EVP_DigestUpdate(data)");
> +	ERR(EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1, "EVP_DigestFinal_ex()");
> +}
> +
> +static void hash_entry(void *left, void *right, void *ret_hash)
> +{
> +	int hash_size = EVP_MD_CTX_get_size_ex(ctx);

Nit: The local variable hash_size can be removed, as the static variable
with the same name should hold the same value.

> +	unsigned char magic = 0x02;
> +
> +	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
> +	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
> +	ERR(EVP_DigestUpdate(ctx, left, hash_size) != 1, "EVP_DigestUpdate(left)");
> +	ERR(EVP_DigestUpdate(ctx, right, hash_size) != 1, "EVP_DigestUpdate(right)");
> +	ERR(EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1, "EVP_DigestFinal_ex()");
> +}
> +
> +static void hash_file(struct file_entry *fe)
> +{
> +	struct stat sb;
> +	int fd, ret;
> +	void *mem;
> +
> +	fd = open(fe->name, O_RDONLY);
> +	if (fd < 0)
> +		err(1, "Failed to open %s", fe->name);
> +
> +	ret = fstat(fd, &sb);
> +	if (ret)
> +		err(1, "Failed to stat %s", fe->name);
> +
> +	mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
> +	close(fd);
> +
> +	if (mem == MAP_FAILED)
> +		err(1, "Failed to mmap %s", fe->name);

Nit: The err() call should be moved immediately after mmap(). In theory,
the interleaving close() could change the errno value, resulting in
err() printing a misleading error message.

> +
> +	hash_data(mem, fe->pos, sb.st_size, fe->hash);
> +
> +	munmap(mem, sb.st_size);
> +}
> +
> +static struct mtree *build_merkle(struct file_entry *fh, size_t num)
> +{
> +	struct mtree *mt;
> +	unsigned int le;
> +
> +	if (!num)
> +		return NULL;
> +
> +	mt = xmalloc(sizeof(*mt));
> +	mt->levels = log2_roundup(num);
> +
> +	mt->l = xcalloc(sizeof(*mt->l), mt->levels);
> +
> +	mt->entries = xcalloc(sizeof(*mt->entries), mt->levels);
> +	le = num / 2;
> +	if (num & 1)
> +		le++;
> +	mt->entries[0] = le;
> +	mt->l[0] = xcalloc(sizeof(**mt->l), le);
> +
> +	/* First level of pairs */
> +	for (unsigned int i = 0; i < num; i += 2) {
> +		if (i == num - 1) {
> +			/* Odd number of files, no pair. Hash with itself */
> +			hash_entry(fh[i].hash, fh[i].hash, mt->l[0][i / 2].hash);
> +		} else {
> +			hash_entry(fh[i].hash, fh[i + 1].hash, mt->l[0][i / 2].hash);
> +		}
> +	}
> +	for (unsigned int i = 1; i < mt->levels; i++) {
> +		int odd = 0;
> +
> +		if (le & 1) {
> +			le++;
> +			odd++;
> +		}
> +
> +		mt->entries[i] = le / 2;
> +		mt->l[i] = xcalloc(sizeof(**mt->l), le);

l[i] is overallocated. It needs only 'le / 2' entries.

> +
> +		for (unsigned int n = 0; n < le; n += 2) {
> +			if (n == le - 2 && odd) {
> +				/* Odd number of pairs, no pair. Hash with itself */
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n].hash,
> +					   mt->l[i][n / 2].hash);
> +			} else {
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n + 1].hash,
> +					   mt->l[i][n / 2].hash);
> +			}
> +		}
> +		le =  mt->entries[i];

Nit: It might be helpful to write both the first-level and other-level
loops in the same style to make them easier to understand, perhaps by
clearly separating the number of entries at each level. I suggest
something like the following:

static struct mtree *build_merkle(struct file_entry *fh, size_t num_files)
{
	struct mtree *mt;
	unsigned int num_cur_le, num_prev_le;

	if (!num_files)
		return NULL;

	mt = xmalloc(sizeof(*mt));
	mt->levels = log2_roundup(num_files);

	mt->l = xcalloc(sizeof(*mt->l), mt->levels);

	mt->entries = xcalloc(sizeof(*mt->entries), mt->levels);
	num_cur_le = (num_files + 1) / 2;
	mt->entries[0] = num_cur_le;
	mt->l[0] = xcalloc(sizeof(**mt->l), num_cur_le);

	/* First level of pairs */
	for (unsigned int i = 0; i < num_files; i += 2) {
		/* Hash the pair, or the last file with itself if it's odd. */
		void *right = i + 1 < num_files ? fh[i + 1].hash : fh[i].hash;
		hash_entry(fh[i].hash, right, mt->l[0][i / 2].hash);
	}

	for (unsigned int i = 1; i < mt->levels; i++) {
		num_prev_le = num_cur_le;

		num_cur_le = (num_prev_le + 1) / 2;
		mt->entries[i] = num_cur_le;
		mt->l[i] = xcalloc(sizeof(**mt->l), num_cur_le);

		for (unsigned int n = 0; n < num_prev_le; n += 2) {
			/* Hash the pair, or the last with itself if it's odd. */
			void *right = n + 1 < num_prev_le ?
					      mt->l[i - 1][n + 1].hash :
					      mt->l[i - 1][n].hash;
			hash_entry(mt->l[i - 1][n].hash, right,
				   mt->l[i][n / 2].hash);
		}
	}
	return mt;
}

> +	}
> +	return mt;
> +}
> +
> +static void free_mtree(struct mtree *mt)
> +{
> +	if (!mt)
> +		return;
> +
> +	for (unsigned int i = 0; i < mt->levels; i++)
> +		free(mt->l[i]);
> +
> +	free(mt->l);
> +	free(mt->entries);
> +	free(mt);
> +}
> +
> +static void write_be_int(int fd, unsigned int v)
> +{
> +	unsigned int be_val = htonl(v);
> +
> +	if (write(fd, &be_val, sizeof(be_val)) != sizeof(be_val))
> +		err(1, "Failed writing to file");
> +}
> +
> +static void write_hash(int fd, const void *h)
> +{
> +	ssize_t wr;
> +
> +	wr = write(fd, h, hash_size);
> +	if (wr != hash_size)
> +		err(1, "Failed writing to file");
> +}

Nit: This could be

if (write(fd, h, hash_size) != hash_size)

to keep the style of write_be_int() and write_hash() consistent.

> +
> +static void build_proof(struct mtree *mt, unsigned int n, int fd)
> +{
> +	unsigned char cur[EVP_MAX_MD_SIZE];
> +	unsigned char tmp[EVP_MAX_MD_SIZE];
> +	struct file_entry *fe, *fe_sib;
> +
> +	fe = &fh_list[n];
> +
> +	if ((n & 1) == 0) {
> +		/* No pair, hash with itself */
> +		if (n + 1 == num_files)
> +			fe_sib = fe;
> +		else
> +			fe_sib = &fh_list[n + 1];
> +	} else {
> +		fe_sib = &fh_list[n - 1];
> +	}
> +	/* First comes the node position into the file */
> +	write_be_int(fd, n);
> +
> +	if ((n & 1) == 0)
> +		hash_entry(fe->hash, fe_sib->hash, cur);
> +	else
> +		hash_entry(fe_sib->hash, fe->hash, cur);
> +
> +	/* Next is the sibling hash, followed by hashes in the tree */
> +	write_hash(fd, fe_sib->hash);
> +
> +	for (unsigned int i = 0; i < mt->levels - 1; i++) {
> +		n >>= 1;
> +		if ((n & 1) == 0) {
> +			void *h;
> +
> +			/* No pair, hash with itself */
> +			if (n + 1 == mt->entries[i])
> +				h = cur;
> +			else
> +				h = mt->l[i][n + 1].hash;
> +
> +			hash_entry(cur, h, tmp);
> +			write_hash(fd, h);
> +		} else {
> +			hash_entry(mt->l[i][n - 1].hash, cur, tmp);
> +			write_hash(fd, mt->l[i][n - 1].hash);
> +		}
> +		memcpy(cur, tmp, hash_size);
> +	}
> +
> +	 /* After all that, the end hash should match the root hash */
> +	if (memcmp(cur, mt->l[mt->levels - 1][0].hash, hash_size))
> +		errx(1, "hash mismatch");
> +}
> +
> +static void append_module_signature_magic(int fd, unsigned int sig_len)
> +{
> +	struct module_signature sig_info = {
> +		.id_type	= PKEY_ID_MERKLE,
> +		.sig_len	= htonl(sig_len),
> +	};
> +
> +	if (write(fd, &sig_info, sizeof(sig_info)) < 0)
> +		err(1, "write(sig_info) failed");
> +
> +	if (write(fd, &magic_number, sizeof(magic_number) - 1) < 0)
> +		err(1, "write(magic_number) failed");

Nit: Checking that the written size exactly matches the size of the
input data would be safer and consistent with other uses of write() in
write_be_int() and write_hash(). Additionally, it would be good to make
the error messages consistent in all cases.

> +}
> +
> +static void write_merkle_root(struct mtree *mt, const char *fp)
> +{
> +	char buf[1024];
> +	unsigned int levels;
> +	unsigned char *h;
> +	FILE *f;
> +
> +	if (mt) {
> +		levels = mt->levels;
> +		h = mt->l[mt->levels - 1][0].hash;
> +	} else {
> +		levels = 0;
> +		h = xcalloc(1, hash_size);
> +	}
> +
> +	f = fopen(fp, "w");
> +	if (!f)
> +		err(1, "Failed to create %s", buf);

The last parameter to err() should be fp. The buf variable is then
unused and can be removed.

> +
> +	fprintf(f, "#include <linux/module_hashes.h>\n\n");
> +	fprintf(f, "const struct module_hashes_root module_hashes_root __module_hashes_section = {\n");
> +
> +	fprintf(f, "\t.levels = %u,\n", levels);
> +	fprintf(f, "\t.hash = {");
> +	for (unsigned int i = 0; i < hash_size; i++) {
> +		char *space = "";
> +
> +		if (!(i % 8))
> +			fprintf(f, "\n\t\t");
> +
> +		if ((i + 1) % 8)
> +			space = " ";
> +
> +		fprintf(f, "0x%02x,%s", h[i], space);
> +	}
> +	fprintf(f, "\n\t},");
> +
> +	fprintf(f, "\n};\n");
> +	fclose(f);

Is it ok not to check the return values when writing to this output
file? Other code checks that its output was successful.

> +
> +	if (!mt)
> +		free(h);
> +}
> +
> +static char *xstrdup_replace_suffix(const char *str, const char *new_suffix)
> +{
> +	const char *current_suffix;
> +	size_t base_len;
> +
> +	current_suffix = strchr(str, '.');

It is safer to use strrchr() in case the module path happens to contain
a dot.

> +	if (!current_suffix)
> +		errx(1, "No existing suffix in '%s'", str);
> +
> +	base_len = current_suffix - str;
> +
> +	return xasprintf("%.*s%s", (int)base_len, str, new_suffix);
> +}
> +
> +static void read_modules_order(const char *fname, const char *suffix)
> +{
> +	char line[PATH_MAX];

<limits.h> should be included at the top to provide the definition of
PATH_MAX.

> +	FILE *in;
> +
> +	in = fopen(fname, "r");
> +	if (!in)
> +		err(1, "fopen(%s)", fname);

Nit: The error message could be "Failed to open %s" to maintain
consistency with a similar error in write_merkle_root().

> +
> +	while (fgets(line, PATH_MAX, in)) {
> +		struct file_entry *entry;
> +
> +		fh_list = xreallocarray(fh_list, num_files + 1, sizeof(*fh_list));

It might be useful to not reallocate this array for each file, although
I don't immediately see that it contributes any significant time to the
runtime.

> +		entry = &fh_list[num_files];
> +
> +		entry->pos = num_files;
> +		entry->name = xstrdup_replace_suffix(line, suffix);
> +		hash_file(entry);
> +
> +		num_files++;
> +	}
> +
> +	fclose(in);
> +}
> +
> +static __attribute__((noreturn))
> +void format(void)
> +{
> +	fprintf(stderr,
> +		"Usage: scripts/modules-merkle-tree <root definition>\n");

The usage string should mention the second parameter, which is the
module suffix.

> +	exit(2);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	const EVP_MD *hash_evp;
> +	struct mtree *mt;
> +
> +	if (argc != 3)
> +		format();
> +
> +	hash_evp = EVP_get_digestbyname("sha256");
> +	ERR(!hash_evp, "EVP_get_digestbyname");
> +
> +	ctx = EVP_MD_CTX_new();
> +	ERR(!ctx, "EVP_MD_CTX_new()");
> +
> +	hash_size = EVP_MD_get_size(hash_evp);
> +	ERR(hash_size <= 0, "EVP_get_digestbyname");
> +
> +	if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
> +		ERR(1, "EVP_DigestInit_ex()");
> +
> +	read_modules_order("modules.order", argv[2]);
> +
> +	mt = build_merkle(fh_list, num_files);
> +	write_merkle_root(mt, argv[1]);
> +	for (unsigned int i = 0; i < num_files; i++) {
> +		char *signame;
> +		int fd;
> +
> +		signame = xstrdup_replace_suffix(fh_list[i].name, ".merkle");
> +
> +		fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
> +		if (fd < 0)
> +			err(1, "Can't create %s", signame);
> +
> +		build_proof(mt, i, fd);
> +		append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));
> +		close(fd);

The return code of close() should be checked, otherwise it is
meaningless to check the write() calls in
append_module_signature_magic().

> +	}
> +
> +	free_mtree(mt);
> +	for (unsigned int i = 0; i < num_files; i++)
> +		free(fh_list[i].name);
> +	free(fh_list);
> +
> +	EVP_MD_CTX_free(ctx);
> +	return 0;
> +}

-- 
Thanks,
Petr

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 08/17] module: Deduplicate signature extraction
  2026-01-27 15:20   ` Petr Pavlu
@ 2026-02-03 12:41     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-03 12:41 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-01-27 16:20:15+0100, Petr Pavlu wrote:
> On 1/13/26 1:28 PM, Thomas Weißschuh wrote:

(...)

> >  int module_sig_check(struct load_info *info, int flags)
> >  {
> > -	int err = -ENODATA;
> > -	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
> > +	int err;
> >  	const char *reason;
> >  	const void *mod = info->hdr;
> > +	size_t sig_len;
> > +	const u8 *sig;
> >  	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
> >  				       MODULE_INIT_IGNORE_VERMAGIC);
> > -	/*
> > -	 * Do not allow mangled modules as a module with version information
> > -	 * removed is no longer the module that was signed.
> > -	 */
> > -	if (!mangled_module &&
> > -	    info->len > markerlen &&
> > -	    memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
> > -		/* We truncate the module to discard the signature */
> > -		info->len -= markerlen;
> > -		err = mod_verify_sig(mod, info);
> > +
> > +	err = mod_split_sig(info->hdr, &info->len, mangled_module, &sig_len, &sig, "module");
> > +	if (!err) {
> > +		err = verify_pkcs7_signature(mod, info->len, sig, sig_len,
> > +					     VERIFY_USE_SECONDARY_KEYRING,
> > +					     VERIFYING_MODULE_SIGNATURE,
> > +					     NULL, NULL);
> >  		if (!err) {
> >  			info->sig_ok = true;
> >  			return 0;
> 
> The patch looks to modify the behavior when mangled_module is true.
> 
> Previously, module_sig_check() didn't attempt to extract the signature
> in such a case and treated the module as unsigned. The err remained set
> to -ENODATA and the function subsequently consulted module_sig_check()
> and security_locked_down() to determine an appropriate result.
> 
> Newly, module_sig_check() calls mod_split_sig(), which skips the
> extraction of the marker ("~Module signature appended~\n") from the end
> of the module and instead attempts to read it as an actual
> module_signature. The value is then passed to mod_check_sig() which
> should return -EBADMSG. The error is propagated to module_sig_check()
> and treated as fatal, without consulting module_sig_check() and
> security_locked_down().
> 
> I think the mangled_module flag should not be passed to mod_split_sig()
> and it should be handled solely by module_sig_check().

Ack.

(...)

> > diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
> > index 3265d744d5ce..a57342d39b07 100644
> > --- a/security/integrity/ima/ima_modsig.c
> > +++ b/security/integrity/ima/ima_modsig.c
> > @@ -40,44 +40,30 @@ struct modsig {
> >  int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
> >  		    struct modsig **modsig)
> >  {
> > -	const size_t marker_len = strlen(MODULE_SIG_STRING);
> > -	const struct module_signature *sig;
> > +	size_t buf_len_sz = buf_len;
> >  	struct modsig *hdr;
> >  	size_t sig_len;
> > -	const void *p;
> > +	const u8 *sig;
> >  	int rc;
> >  
> > -	if (buf_len <= marker_len + sizeof(*sig))
> > -		return -ENOENT;
> > -
> > -	p = buf + buf_len - marker_len;
> > -	if (memcmp(p, MODULE_SIG_STRING, marker_len))
> > -		return -ENOENT;
> > -
> > -	buf_len -= marker_len;
> > -	sig = (const struct module_signature *)(p - sizeof(*sig));
> > -
> > -	rc = mod_check_sig(sig, buf_len, func_tokens[func]);
> > +	rc = mod_split_sig(buf, &buf_len_sz, true, &sig_len, &sig, func_tokens[func]);
> 
> Passing mangled=true to mod_split_sig() seems incorrect here. It causes
> that the function doesn't properly extract the signature marker at the
> end of the module, no?

Indeed, thanks.
 
I am thinking about dropping this patch from the series for now.
It was meant for IMA modsig compatibility, which is not part of the
series anymore.

> >  	if (rc)
> >  		return rc;
> >  
> > -	sig_len = be32_to_cpu(sig->sig_len);
> > -	buf_len -= sig_len + sizeof(*sig);
> > -
> >  	/* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
> >  	hdr = kzalloc(struct_size(hdr, raw_pkcs7, sig_len), GFP_KERNEL);
> >  	if (!hdr)
> >  		return -ENOMEM;
> >  
> >  	hdr->raw_pkcs7_len = sig_len;
> > -	hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
> > +	hdr->pkcs7_msg = pkcs7_parse_message(sig, sig_len);
> >  	if (IS_ERR(hdr->pkcs7_msg)) {
> >  		rc = PTR_ERR(hdr->pkcs7_msg);
> >  		kfree(hdr);
> >  		return rc;
> >  	}
> >  
> > -	memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
> > +	memcpy(hdr->raw_pkcs7, sig, sig_len);
> >  
> >  	/* We don't know the hash algorithm yet. */
> >  	hdr->hash_algo = HASH_ALGO__LAST;
> > 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 12/17] module: Move signature splitting up
  2026-01-29 14:41   ` Petr Pavlu
@ 2026-02-03 12:42     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-03 12:42 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-01-29 15:41:43+0100, Petr Pavlu wrote:
> On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > The signature splitting will also be used by CONFIG_MODULE_HASHES.
> > 
> > Move it up the callchain, so the result can be reused.
> > 
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > ---
> > [...]
> > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > index c09b25c0166a..d65bc300a78c 100644
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -3346,10 +3346,21 @@ static int early_mod_check(struct load_info *info, int flags)
> >  
> >  static int module_integrity_check(struct load_info *info, int flags)
> >  {
> > +	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
> > +				       MODULE_INIT_IGNORE_VERMAGIC);
> > +	size_t sig_len;
> > +	const u8 *sig;
> >  	int err = 0;
> >  
> > +	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
> > +		err = mod_split_sig(info->hdr, &info->len, mangled_module,
> > +				    &sig_len, &sig, "module");
> > +		if (err)
> > +			return err;
> > +	}
> > +
> >  	if (IS_ENABLED(CONFIG_MODULE_SIG))
> > -		err = module_sig_check(info, flags);
> > +		err = module_sig_check(info, sig, sig_len);
> >  
> >  	if (err)
> >  		return err;
> 
> I suggest moving the IS_ENABLED(CONFIG_MODULE_SIG) block under the
> new IS_ENABLED(CONFIG_MODULE_SIG_POLICY) section. I realize that
> CONFIG_MODULE_SIG implies CONFIG_MODULE_SIG_POLICY, but I believe this
> change makes it more apparent that this it the case. Otherwise, one
> might for example wonder if sig_len in the module_sig_check() call can
> be undefined.
> 
> 	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
> 		err = mod_split_sig(info->hdr, &info->len, mangled_module,
> 				    &sig_len, &sig, "module");
> 		if (err)
> 			return err;
> 
> 		if (IS_ENABLED(CONFIG_MODULE_SIG))
> 			err = module_sig_check(info, sig, sig_len);
> 	}

Ack.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 13/17] module: Report signature type to users
  2026-01-29 14:44   ` Petr Pavlu
@ 2026-02-03 12:44     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-03 12:44 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-01-29 15:44:31+0100, Petr Pavlu wrote:
> On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > The upcoming CONFIG_MODULE_HASHES will introduce a signature type.
> > This needs to be handled by callers differently than PKCS7 signatures.
> > 
> > Report the signature type to the caller and let them verify it.
> > 
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > ---
> > [...]
> > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > index d65bc300a78c..2a28a0ece809 100644
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -3348,19 +3348,24 @@ static int module_integrity_check(struct load_info *info, int flags)
> >  {
> >  	bool mangled_module = flags & (MODULE_INIT_IGNORE_MODVERSIONS |
> >  				       MODULE_INIT_IGNORE_VERMAGIC);
> > +	enum pkey_id_type sig_type;
> >  	size_t sig_len;
> >  	const u8 *sig;
> >  	int err = 0;
> >  
> >  	if (IS_ENABLED(CONFIG_MODULE_SIG_POLICY)) {
> >  		err = mod_split_sig(info->hdr, &info->len, mangled_module,
> > -				    &sig_len, &sig, "module");
> > +				    &sig_type, &sig_len, &sig, "module");
> >  		if (err)
> >  			return err;
> >  	}
> >  
> > -	if (IS_ENABLED(CONFIG_MODULE_SIG))
> > +	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> >  		err = module_sig_check(info, sig, sig_len);
> > +	} else {
> > +		pr_err("module: not signed with expected PKCS#7 message\n");
> > +		err = -ENOPKG;
> > +	}
> 
> The new else branch means that if the user chooses not to configure any
> module integrity policy, they will no longer be able to load any
> modules. I think this entire if-else part should be moved under the
> IS_ENABLED(CONFIG_MODULE_SIG_POLICY) block above, as I'm mentioning on
> patch #12.

Ack.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-30 17:06   ` Petr Pavlu
@ 2026-02-03 12:55     ` Thomas Weißschuh
  2026-02-06 17:12       ` Nicolas Schier
  2026-02-19 14:27       ` Nicolas Schier
  0 siblings, 2 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-03 12:55 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-01-30 18:06:20+0100, Petr Pavlu wrote:
> On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > Normally the .ko module files depend on a fully built vmlinux to be
> > available for modpost validation and BTF generation. With
> > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > to build a merkle tree. This introduces a dependency cycle which is
> > impossible to satisfy. Work around this by building the modules during
> > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > but before the final module hashes are
> 
> I wonder if this dependency cycle could be resolved by utilizing the
> split into vmlinux.unstripped and vmlinux that occurred last year.
> 
> The idea is to create the following ordering: vmlinux.unstripped ->
> modules -> vmlinux, and to patch in .module_hashes only when building
> the final vmlinux.
> 
> This would require the following:
> * Split scripts/Makefile.vmlinux into two Makefiles, one that builds the
>   current vmlinux.unstripped and the second one that builds the final
>   vmlinux from it.
> * Modify the top Makefile to recognize vmlinux.unstripped and update the
>   BTF generation rule 'modules: vmlinux' to
>   'modules: vmlinux.unstripped'.
> * Add the 'vmlinux: modules' ordering in the top Makefile for
>   CONFIG_MODULE_HASHES=y.
> * Remove the patching of vmlinux.unstripped in scripts/link-vmlinux.sh
>   and instead move it into scripts/Makefile.vmlinux when running objcopy
>   to produce the final vmlinux.
> 
> I think this approach has two main advantages:
> * CONFIG_MODULE_HASHES can be made orthogonal to
>   CONFIG_DEBUG_INFO_BTF_MODULES.
> * All dependencies are expressed at the Makefile level instead of having
>   scripts/link-vmlinux.sh invoke 'make -f Makefile modules'.
> 
> Below is a rough prototype that applies on top of this series. It is a
> bit verbose due to the splitting of part of scripts/Makefile.vmlinux
> into scripts/Makefile.vmlinux_unstripped.

That looks like a feasible alternative. Before adopting it, I'd like to
hear the preference of the kbuild folks.

> diff --git a/Makefile b/Makefile
> index 841772a5a260..19a3beb82fa7 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1259,7 +1259,7 @@ vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
>  vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
>  	@:
>  
> -PHONY += vmlinux
> +PHONY += vmlinux.unstripped vmlinux
>  # LDFLAGS_vmlinux in the top Makefile defines linker flags for the top vmlinux,
>  # not for decompressors. LDFLAGS_vmlinux in arch/*/boot/compressed/Makefile is
>  # unrelated; the decompressors just happen to have the same base name,
> @@ -1270,9 +1270,11 @@ PHONY += vmlinux
>  #   https://savannah.gnu.org/bugs/?61463
>  # For Make > 4.4, the following simple code will work:
>  #  vmlinux: private export LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> -vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> -vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> -vmlinux: vmlinux.o $(KBUILD_LDS) modpost
> +vmlinux.unstripped: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> +vmlinux.unstripped: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> +vmlinux.unstripped: vmlinux.o $(KBUILD_LDS) modpost
> +	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_unstripped
> +vmlinux: vmlinux.unstripped
>  	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux

Maybe we could keep them together in a single Makefile,
and instead have different targets in it.

(...)

> @@ -98,70 +44,15 @@ remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
>  # To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
>  # it is necessary to remove the PT_LOAD flag from the segment.
>  quiet_cmd_strip_relocs = OBJCOPY $@
> -      cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> -                         $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
> +      cmd_script_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> +                          $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) \
> +                                     $(remove-symbols) \
> +                                     $(patch-module-hashes) $@

cmd_script_relocs -> cmd_strip_relocs

(...)

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-03 12:19   ` Petr Pavlu
@ 2026-02-03 12:59     ` Thomas Weißschuh
  2026-03-11  1:18     ` Eric Biggers
  1 sibling, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-03 12:59 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Sami Tolvanen,
	Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
	Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Naveen N Rao, Mimi Zohar, Roberto Sassu,
	Dmitry Kasatkin, Eric Snowberg, Nicolas Schier, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Schier,
	Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
	Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
	Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
	linux-kernel, linux-arch, linux-modules, linux-security-module,
	linux-doc, linuxppc-dev, linux-integrity

On 2026-02-03 13:19:20+0100, Petr Pavlu wrote:
> On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > The current signature-based module integrity checking has some drawbacks
> > in combination with reproducible builds. Either the module signing key
> > is generated at build time, which makes the build unreproducible, or a
> > static signing key is used, which precludes rebuilds by third parties
> > and makes the whole build and packaging process much more complicated.
> > 
> > The goal is to reach bit-for-bit reproducibility. Excluding certain
> > parts of the build output from the reproducibility analysis would be
> > error-prone and force each downstream consumer to introduce new tooling.
> > 
> > Introduce a new mechanism to ensure only well-known modules are loaded
> > by embedding a merkle tree root of all modules built as part of the full
> > kernel build into vmlinux.
> > 
> > Non-builtin modules can be validated as before through signatures.
> > 
> > Normally the .ko module files depend on a fully built vmlinux to be
> > available for modpost validation and BTF generation. With
> > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > to build a merkle tree. This introduces a dependency cycle which is
> > impossible to satisfy. Work around this by building the modules during
> > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > but before the final module hashes are
> > 
> > The PKCS7 format which is used for regular module signatures can not
> > represent Merkle proofs, so a new kind of module signature is
> > introduced. As this signature type is only ever used for builtin
> > modules, no compatibility issues can arise.
> 
> Nit: The description uses the term "builtin modules" in a misleading
> way. Typically, "builtin modules" refers to modules that are linked
> directly into vmlinux. However, this text uses the term to refer to
> loadable modules that are built together with the main kernel image,
> which is something different.

Agreed. I'll go through everything again, to consistently use "in-tree".

(...)

> > +
> > +	while (fgets(line, PATH_MAX, in)) {
> > +		struct file_entry *entry;
> > +
> > +		fh_list = xreallocarray(fh_list, num_files + 1, sizeof(*fh_list));
> 
> It might be useful to not reallocate this array for each file, although
> I don't immediately see that it contributes any significant time to the
> runtime.

The libc implementation should optimize this internally to not actually
grow one elemet at a time. I'd like to keep this as-is.

(...)

Ack to everything else.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
@ 2026-02-06  8:18   ` David Howells
  2026-02-06  8:34     ` Thomas Weißschuh
  2026-02-06  8:30   ` Nicolas Schier
  2026-02-06  9:09   ` Christophe Leroy (CS GROUP)
  2 siblings, 1 reply; 80+ messages in thread
From: David Howells @ 2026-02-06  8:18 UTC (permalink / raw)
  To: =?utf-8?q?Thomas_Wei=C3=9Fschuh?=
  Cc: dhowells, Nathan Chancellor, Arnd Bergmann, Luis Chamberlain,
	Petr Pavlu, Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

Thomas Weißschuh <linux@weissschuh.net> wrote:

> As both 'size_t' and 'unsigned int' are always the same size, this
> should be risk-free.

Did you mean 'unsigned long'?

David


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
  2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
  2026-01-30 20:43   ` Aaron Tomlin
@ 2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06  8:25 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:46PM +0100, Thomas Weißschuh wrote:
> When CONFIG_MODULE_SIG is disabled set_module_sig_enforced() is defined
> as an empty stub, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the check avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  arch/powerpc/kernel/ima_arch.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
  2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
  2026-01-30 20:49   ` Aaron Tomlin
@ 2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06  8:25 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:47PM +0100, Thomas Weißschuh wrote:
> When configuration settings are disabled the guarded functions are
> defined as empty stubs, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the checks avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  security/integrity/ima/ima_efi.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 04/17] module: Make mod_verify_sig() static
  2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
  2026-01-30 20:53   ` Aaron Tomlin
@ 2026-02-06  8:25   ` Nicolas Schier
  2026-03-10 21:12   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06  8:25 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:48PM +0100, Thomas Weißschuh wrote:
> It is not used outside of signing.c.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  kernel/module/internal.h | 1 -
>  kernel/module/signing.c  | 2 +-
>  2 files changed, 1 insertion(+), 2 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
  2026-02-06  8:18   ` David Howells
@ 2026-02-06  8:30   ` Nicolas Schier
  2026-02-06  8:38     ` Thomas Weißschuh
  2026-02-06  9:09   ` Christophe Leroy (CS GROUP)
  2 siblings, 1 reply; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06  8:30 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:49PM +0100, Thomas Weißschuh wrote:
> Switching the types will make some later changes cleaner.
> size_t is also the semantically correct type for this field.
> 
> As both 'size_t' and 'unsigned int' are always the same size, this
> should be risk-free.

include/uapi/asm-generic/posix_types.h states:
| * Most 32 bit architectures use "unsigned int" size_t,
| * and all 64 bit architectures use "unsigned long" size_t.

Is that statement wrong?  Or did I mix up the context?


Kind regards,
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-02-06  8:18   ` David Howells
@ 2026-02-06  8:34     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-06  8:34 UTC (permalink / raw)
  To: David Howells
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-02-06 08:18:53+0000, David Howells wrote:
> Thomas Weißschuh <linux@weissschuh.net> wrote:
> 
> > As both 'size_t' and 'unsigned int' are always the same size, this
> > should be risk-free.
> 
> Did you mean 'unsigned long'?

Indeed, I'll fix it for the next revision.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-02-06  8:30   ` Nicolas Schier
@ 2026-02-06  8:38     ` Thomas Weißschuh
  2026-02-06  8:55       ` Nicolas Schier
  0 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-06  8:38 UTC (permalink / raw)
  To: Nicolas Schier, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Petr Pavlu, Sami Tolvanen, Daniel Gomez,
	Paul Moore, James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Daniel Gomez, Aaron Tomlin,
	Christophe Leroy (CS GROUP), Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-02-06 09:30:08+0100, Nicolas Schier wrote:
> On Tue, Jan 13, 2026 at 01:28:49PM +0100, Thomas Weißschuh wrote:
> > Switching the types will make some later changes cleaner.
> > size_t is also the semantically correct type for this field.
> > 
> > As both 'size_t' and 'unsigned int' are always the same size, this
> > should be risk-free.
> 
> include/uapi/asm-generic/posix_types.h states:
> | * Most 32 bit architectures use "unsigned int" size_t,
> | * and all 64 bit architectures use "unsigned long" size_t.
> 
> Is that statement wrong?  Or did I mix up the context?

That statement is correct. But as both 'unsigned int' and 'unsigned
long' are 32-bit wide on 32-bit Linux platforms they are compatible.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-02-06  8:38     ` Thomas Weißschuh
@ 2026-02-06  8:55       ` Nicolas Schier
  0 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06  8:55 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Fri, Feb 06, 2026 at 09:38:07AM +0100, Thomas Weißschuh wrote:
> On 2026-02-06 09:30:08+0100, Nicolas Schier wrote:
> > On Tue, Jan 13, 2026 at 01:28:49PM +0100, Thomas Weißschuh wrote:
> > > Switching the types will make some later changes cleaner.
> > > size_t is also the semantically correct type for this field.
> > > 
> > > As both 'size_t' and 'unsigned int' are always the same size, this
> > > should be risk-free.
> > 
> > include/uapi/asm-generic/posix_types.h states:
> > | * Most 32 bit architectures use "unsigned int" size_t,
> > | * and all 64 bit architectures use "unsigned long" size_t.
> > 
> > Is that statement wrong?  Or did I mix up the context?
> 
> That statement is correct. But as both 'unsigned int' and 'unsigned
> long' are 32-bit wide on 32-bit Linux platforms they are compatible.
> 
> 
> Thomas

sure, thanks!

Acked-by: Nicolas Schier <nsc@kernel.org>

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
  2026-02-06  8:18   ` David Howells
  2026-02-06  8:30   ` Nicolas Schier
@ 2026-02-06  9:09   ` Christophe Leroy (CS GROUP)
  2026-02-06  9:18     ` Thomas Weißschuh
  2 siblings, 1 reply; 80+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-02-06  9:09 UTC (permalink / raw)
  To: Thomas Weißschuh, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Petr Pavlu, Sami Tolvanen, Daniel Gomez,
	Paul Moore, James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Nicolas Schier, Daniel Gomez, Aaron Tomlin,
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng
  Cc: Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity



Le 13/01/2026 à 13:28, Thomas Weißschuh a écrit :
> Switching the types will make some later changes cleaner.
> size_t is also the semantically correct type for this field.
> 
> As both 'size_t' and 'unsigned int' are always the same size, this
> should be risk-free.

Are you sure ?

Some architectures have size_t as 'unsigned int', some have 'unsigned 
long', some have 'unsigned long long'

https://elixir.bootlin.com/linux/v6.19-rc5/source/arch/s390/include/uapi/asm/posix_types.h#L16
https://elixir.bootlin.com/linux/v6.19-rc5/source/arch/sparc/include/uapi/asm/posix_types.h#L35
https://elixir.bootlin.com/linux/v6.19-rc5/source/arch/xtensa/include/uapi/asm/posix_types.h#L26
https://elixir.bootlin.com/linux/v6.19-rc5/source/include/uapi/asm-generic/posix_types.h#L68
https://elixir.bootlin.com/linux/v6.19-rc5/source/include/uapi/asm-generic/posix_types.h#L72
https://elixir.bootlin.com/linux/v6.19-rc5/source/arch/sparc/include/uapi/asm/posix_types.h#L23
https://elixir.bootlin.com/linux/v6.19-rc5/source/arch/x86/include/uapi/asm/posix_types_x32.h#L15
https://elixir.bootlin.com/linux/v6.19-rc5/source/include/uapi/asm-generic/posix_types.h#L16

Christophe

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 05/17] module: Switch load_info::len to size_t
  2026-02-06  9:09   ` Christophe Leroy (CS GROUP)
@ 2026-02-06  9:18     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-06  9:18 UTC (permalink / raw)
  To: Christophe Leroy (CS GROUP)
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Nicolas Schier, Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-02-06 10:09:12+0100, Christophe Leroy (CS GROUP) wrote:
> 
> 
> Le 13/01/2026 à 13:28, Thomas Weißschuh a écrit :
> > Switching the types will make some later changes cleaner.
> > size_t is also the semantically correct type for this field.
> > 
> > As both 'size_t' and 'unsigned int' are always the same size, this
> > should be risk-free.

> Are you sure ?

As mentioned before by David [0], this should have been 'unsigned long'
instead of 'unsigned int'. Which is also what the diff shows.

> Some architectures have size_t as 'unsigned int', some have 'unsigned long',
> some have 'unsigned long long'

(...)

[0] https://lore.kernel.org/lkml/2919071.1770365933@warthog.procyon.org.uk/

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data
  2026-01-13 12:28 ` [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data Thomas Weißschuh
@ 2026-02-06 16:28   ` Nicolas Schier
  2026-03-10 21:36   ` Eric Biggers
  1 sibling, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06 16:28 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:50PM +0100, Thomas Weißschuh wrote:
> The upcoming module hashes functionality will build the modules in
> between the generation of the BTF data and the final link of vmlinux.
> Having a dependency from the modules on vmlinux would make this
> impossible as it would mean having a cyclic dependency.
> Break this cyclic dependency by introducing a new target.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  scripts/Makefile.modfinal | 4 ++--
>  scripts/link-vmlinux.sh   | 6 ++++++
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped
  2026-01-13 12:28 ` [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped Thomas Weißschuh
@ 2026-02-06 16:37   ` Nicolas Schier
  2026-02-20  9:29   ` Fwd: " Thomas Weißschuh
  1 sibling, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06 16:37 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:51PM +0100, Thomas Weißschuh wrote:
> The upcoming module hashes functionality will build the modules in
> between the generation of the BTF data and the final link of vmlinux.
> At this point vmlinux is not yet built and therefore can't be used for
> module BTF generation. vmlinux.unstripped however is usable and
> sufficient for BTF generation.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  scripts/Makefile.modfinal | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index adfef1e002a9..930db0524a0a 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
> @@ -40,11 +40,11 @@ quiet_cmd_ld_ko_o = LD [M]  $@
>  
>  quiet_cmd_btf_ko = BTF [M] $@
>        cmd_btf_ko = 							\
> -	if [ ! -f $(objtree)/vmlinux ]; then				\
> -		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
> +	if [ ! -f $(objtree)/vmlinux.unstripped ]; then			\
> +		printf "Skipping BTF generation for %s due to unavailability of vmlinux.unstripped\n" $@ 1>&2; \
>  	else								\
> -		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
> -		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@;		\
> +		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux.unstripped $@; \
> +		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux.unstripped $@;	\

Reviewed-by: Nicolas Schier <nsc@kernel.org> # kbuild

I'd like to have some BTF ack for that.

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-03 12:55     ` Thomas Weißschuh
@ 2026-02-06 17:12       ` Nicolas Schier
  2026-02-19 14:27       ` Nicolas Schier
  1 sibling, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-06 17:12 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Petr Pavlu, Nathan Chancellor, Arnd Bergmann, Luis Chamberlain,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Feb 03, 2026 at 01:55:05PM +0100, Thomas Weißschuh wrote:
> On 2026-01-30 18:06:20+0100, Petr Pavlu wrote:
> > On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > > Normally the .ko module files depend on a fully built vmlinux to be
> > > available for modpost validation and BTF generation. With
> > > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > > to build a merkle tree. This introduces a dependency cycle which is
> > > impossible to satisfy. Work around this by building the modules during
> > > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > > but before the final module hashes are
> > 
> > I wonder if this dependency cycle could be resolved by utilizing the
> > split into vmlinux.unstripped and vmlinux that occurred last year.
> > 
> > The idea is to create the following ordering: vmlinux.unstripped ->
> > modules -> vmlinux, and to patch in .module_hashes only when building
> > the final vmlinux.
> > 
> > This would require the following:
> > * Split scripts/Makefile.vmlinux into two Makefiles, one that builds the
> >   current vmlinux.unstripped and the second one that builds the final
> >   vmlinux from it.
> > * Modify the top Makefile to recognize vmlinux.unstripped and update the
> >   BTF generation rule 'modules: vmlinux' to
> >   'modules: vmlinux.unstripped'.
> > * Add the 'vmlinux: modules' ordering in the top Makefile for
> >   CONFIG_MODULE_HASHES=y.
> > * Remove the patching of vmlinux.unstripped in scripts/link-vmlinux.sh
> >   and instead move it into scripts/Makefile.vmlinux when running objcopy
> >   to produce the final vmlinux.
> > 
> > I think this approach has two main advantages:
> > * CONFIG_MODULE_HASHES can be made orthogonal to
> >   CONFIG_DEBUG_INFO_BTF_MODULES.
> > * All dependencies are expressed at the Makefile level instead of having
> >   scripts/link-vmlinux.sh invoke 'make -f Makefile modules'.
> > 
> > Below is a rough prototype that applies on top of this series. It is a
> > bit verbose due to the splitting of part of scripts/Makefile.vmlinux
> > into scripts/Makefile.vmlinux_unstripped.
> 
> That looks like a feasible alternative. Before adopting it, I'd like to
> hear the preference of the kbuild folks.

After the first run-through, the proposed alternative sounds good.
Unfortunately, I ran out of time for this week.  I can give a more
founded reply in a few days.

Kind regards,
Nicolas



> > diff --git a/Makefile b/Makefile
> > index 841772a5a260..19a3beb82fa7 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1259,7 +1259,7 @@ vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
> >  vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
> >  	@:
> >  
> > -PHONY += vmlinux
> > +PHONY += vmlinux.unstripped vmlinux
> >  # LDFLAGS_vmlinux in the top Makefile defines linker flags for the top vmlinux,
> >  # not for decompressors. LDFLAGS_vmlinux in arch/*/boot/compressed/Makefile is
> >  # unrelated; the decompressors just happen to have the same base name,
> > @@ -1270,9 +1270,11 @@ PHONY += vmlinux
> >  #   https://savannah.gnu.org/bugs/?61463
> >  # For Make > 4.4, the following simple code will work:
> >  #  vmlinux: private export LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > -vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > -vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> > -vmlinux: vmlinux.o $(KBUILD_LDS) modpost
> > +vmlinux.unstripped: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > +vmlinux.unstripped: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> > +vmlinux.unstripped: vmlinux.o $(KBUILD_LDS) modpost
> > +	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_unstripped
> > +vmlinux: vmlinux.unstripped
> >  	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux
> 
> Maybe we could keep them together in a single Makefile,
> and instead have different targets in it.
> 
> (...)
> 
> > @@ -98,70 +44,15 @@ remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
> >  # To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
> >  # it is necessary to remove the PT_LOAD flag from the segment.
> >  quiet_cmd_strip_relocs = OBJCOPY $@
> > -      cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> > -                         $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
> > +      cmd_script_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
> > +                          $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) \
> > +                                     $(remove-symbols) \
> > +                                     $(patch-module-hashes) $@
> 
> cmd_script_relocs -> cmd_strip_relocs
> 
> (...)

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 10/17] module: Move integrity checks into dedicated function
  2026-01-13 12:28 ` [PATCH v4 10/17] module: Move integrity checks into dedicated function Thomas Weißschuh
@ 2026-02-13 15:09   ` Nicolas Schier
  2026-03-10 22:06   ` Eric Biggers
  1 sibling, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-13 15:09 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:54PM +0100, Thomas Weißschuh wrote:
> With the addition of hash-based integrity checking, the configuration
> matrix is easier to represent in a dedicated function and with explicit
> usage of IS_ENABLED().
> 
> Drop the now unnecessary stub for module_sig_check().
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  kernel/module/internal.h |  7 -------
>  kernel/module/main.c     | 18 ++++++++++++++----
>  2 files changed, 14 insertions(+), 11 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 11/17] module: Move lockdown check into generic module loader
  2026-01-13 12:28 ` [PATCH v4 11/17] module: Move lockdown check into generic module loader Thomas Weißschuh
@ 2026-02-13 15:14   ` Nicolas Schier
  0 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-13 15:14 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:55PM +0100, Thomas Weißschuh wrote:
> The lockdown check buried in module_sig_check() will not compose well
> with the introduction of hash-based module validation.
> Move it into module_integrity_check() which will work better.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  kernel/module/main.c    | 6 +++++-
>  kernel/module/signing.c | 3 +--
>  2 files changed, 6 insertions(+), 3 deletions(-)
> 

Reviewed-by: Nicolas Schier <nsc@kernel.org>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency
  2026-01-13 12:28 ` [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency Thomas Weißschuh
@ 2026-02-13 15:32   ` Nicolas Schier
  0 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-13 15:32 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:58PM +0100, Thomas Weißschuh wrote:
> The new hash-based module integrity checking will also be able to
> satisfy the requirements of lockdown.
> Such an alternative is not representable with "select", so use
> "depends on" instead.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  security/lockdown/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Nicolas Schier <nsc@kernel.org>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-03 12:55     ` Thomas Weißschuh
  2026-02-06 17:12       ` Nicolas Schier
@ 2026-02-19 14:27       ` Nicolas Schier
  1 sibling, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-19 14:27 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Petr Pavlu, Nathan Chancellor, Arnd Bergmann, Luis Chamberlain,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Feb 03, 2026 at 01:55:05PM +0100, Thomas Weißschuh wrote:
> On 2026-01-30 18:06:20+0100, Petr Pavlu wrote:
> > On 1/13/26 1:28 PM, Thomas Weißschuh wrote:
> > > Normally the .ko module files depend on a fully built vmlinux to be
> > > available for modpost validation and BTF generation. With
> > > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > > to build a merkle tree. This introduces a dependency cycle which is
> > > impossible to satisfy. Work around this by building the modules during
> > > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > > but before the final module hashes are
> > 
> > I wonder if this dependency cycle could be resolved by utilizing the
> > split into vmlinux.unstripped and vmlinux that occurred last year.
> > 
> > The idea is to create the following ordering: vmlinux.unstripped ->
> > modules -> vmlinux, and to patch in .module_hashes only when building
> > the final vmlinux.
> > 
> > This would require the following:
> > * Split scripts/Makefile.vmlinux into two Makefiles, one that builds the
> >   current vmlinux.unstripped and the second one that builds the final
> >   vmlinux from it.
> > * Modify the top Makefile to recognize vmlinux.unstripped and update the
> >   BTF generation rule 'modules: vmlinux' to
> >   'modules: vmlinux.unstripped'.
> > * Add the 'vmlinux: modules' ordering in the top Makefile for
> >   CONFIG_MODULE_HASHES=y.
> > * Remove the patching of vmlinux.unstripped in scripts/link-vmlinux.sh
> >   and instead move it into scripts/Makefile.vmlinux when running objcopy
> >   to produce the final vmlinux.
> > 
> > I think this approach has two main advantages:
> > * CONFIG_MODULE_HASHES can be made orthogonal to
> >   CONFIG_DEBUG_INFO_BTF_MODULES.
> > * All dependencies are expressed at the Makefile level instead of having
> >   scripts/link-vmlinux.sh invoke 'make -f Makefile modules'.
> > 
> > Below is a rough prototype that applies on top of this series. It is a
> > bit verbose due to the splitting of part of scripts/Makefile.vmlinux
> > into scripts/Makefile.vmlinux_unstripped.
> 
> That looks like a feasible alternative. Before adopting it, I'd like to
> hear the preference of the kbuild folks.
> 
> > diff --git a/Makefile b/Makefile
> > index 841772a5a260..19a3beb82fa7 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1259,7 +1259,7 @@ vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
> >  vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
> >  	@:
> >  
> > -PHONY += vmlinux
> > +PHONY += vmlinux.unstripped vmlinux
> >  # LDFLAGS_vmlinux in the top Makefile defines linker flags for the top vmlinux,
> >  # not for decompressors. LDFLAGS_vmlinux in arch/*/boot/compressed/Makefile is
> >  # unrelated; the decompressors just happen to have the same base name,
> > @@ -1270,9 +1270,11 @@ PHONY += vmlinux
> >  #   https://savannah.gnu.org/bugs/?61463
> >  # For Make > 4.4, the following simple code will work:
> >  #  vmlinux: private export LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > -vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > -vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> > -vmlinux: vmlinux.o $(KBUILD_LDS) modpost
> > +vmlinux.unstripped: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
> > +vmlinux.unstripped: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
> > +vmlinux.unstripped: vmlinux.o $(KBUILD_LDS) modpost
> > +	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_unstripped
> > +vmlinux: vmlinux.unstripped
> >  	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux
> 
> Maybe we could keep them together in a single Makefile,
> and instead have different targets in it.
> 

yes, I think so, too.  I like the Petr's alternative.

Kind regards,
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Fwd: [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped
  2026-01-13 12:28 ` [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped Thomas Weißschuh
  2026-02-06 16:37   ` Nicolas Schier
@ 2026-02-20  9:29   ` Thomas Weißschuh
  2026-02-20 16:55     ` Ihor Solodrai
  1 sibling, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-20  9:29 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa
  Cc: bpf, linux-kernel

Hi BPF/BTF maintainers,

could you take a look at the patch quoted below?
It is part of a patch series which only tangentially touches BTF [0].
If you prefer to receive the full patch series, please let me know and
I'll do that for the next revision.

[0] https://lore.kernel.org/lkml/20260113-module-hashes-v4-6-0b932db9b56b@weissschuh.net/

Thomas

On 2026-01-13 13:28:51+0100, Thomas Weißschuh wrote:
> The upcoming module hashes functionality will build the modules in
> between the generation of the BTF data and the final link of vmlinux.
> At this point vmlinux is not yet built and therefore can't be used for
> module BTF generation. vmlinux.unstripped however is usable and
> sufficient for BTF generation.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  scripts/Makefile.modfinal | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index adfef1e002a9..930db0524a0a 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
> @@ -40,11 +40,11 @@ quiet_cmd_ld_ko_o = LD [M]  $@
>  
>  quiet_cmd_btf_ko = BTF [M] $@
>        cmd_btf_ko = 							\
> -	if [ ! -f $(objtree)/vmlinux ]; then				\
> -		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
> +	if [ ! -f $(objtree)/vmlinux.unstripped ]; then			\
> +		printf "Skipping BTF generation for %s due to unavailability of vmlinux.unstripped\n" $@ 1>&2; \
>  	else								\
> -		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
> -		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@;		\
> +		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux.unstripped $@; \
> +		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux.unstripped $@;	\
>  	fi;
>  
>  # Same as newer-prereqs, but allows to exclude specified extra dependencies
> 
> -- 
> 2.52.0
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: Fwd: [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped
  2026-02-20  9:29   ` Fwd: " Thomas Weißschuh
@ 2026-02-20 16:55     ` Ihor Solodrai
  2026-02-23  7:40       ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Ihor Solodrai @ 2026-02-20 16:55 UTC (permalink / raw)
  To: Thomas Weißschuh, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa
  Cc: bpf, linux-kernel

On 2/20/26 1:29 AM, Thomas WeiÃschuh wrote:
> Hi BPF/BTF maintainers,
> 
> could you take a look at the patch quoted below?
> It is part of a patch series which only tangentially touches BTF [0].
> If you prefer to receive the full patch series, please let me know and
> I'll do that for the next revision.
> 
> [0] https://lore.kernel.org/lkml/20260113-module-hashes-v4-6-0b932db9b56b@weissschuh.net/

Hi Thomas, thank you for the ping.

The BTF generation process has significantly changed recently:
https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/

I suggest you rebase the integrity checking series on top of that
(it's already been merged into Linus' tree).

Adding a .stamp is a small and simple change, so maybe it's worth
landing separately through BPF tree?


> 
> Thomas
> 
> On 2026-01-13 13:28:51+0100, Thomas Weißschuh wrote:
>> The upcoming module hashes functionality will build the modules in
>> between the generation of the BTF data and the final link of vmlinux.
>> At this point vmlinux is not yet built and therefore can't be used for
>> module BTF generation. vmlinux.unstripped however is usable and
>> sufficient for BTF generation.
>>
>> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
>> ---
>>  scripts/Makefile.modfinal | 8 ++++----
>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
>> index adfef1e002a9..930db0524a0a 100644
>> --- a/scripts/Makefile.modfinal
>> +++ b/scripts/Makefile.modfinal
>> @@ -40,11 +40,11 @@ quiet_cmd_ld_ko_o = LD [M]  $@
>>  
>>  quiet_cmd_btf_ko = BTF [M] $@
>>        cmd_btf_ko = 							\
>> -	if [ ! -f $(objtree)/vmlinux ]; then				\
>> -		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
>> +	if [ ! -f $(objtree)/vmlinux.unstripped ]; then			\
>> +		printf "Skipping BTF generation for %s due to unavailability of vmlinux.unstripped\n" $@ 1>&2; \
>>  	else								\
>> -		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
>> -		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@;		\
>> +		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux.unstripped $@; \
>> +		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux.unstripped $@;	\
>>  	fi;
>>  
>>  # Same as newer-prereqs, but allows to exclude specified extra dependencies
>>
>> -- 
>> 2.52.0
>>


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                     ` (2 preceding siblings ...)
  2026-02-03 12:19   ` Petr Pavlu
@ 2026-02-21 21:38   ` Nicolas Schier
  2026-02-23  7:53     ` Thomas Weißschuh
  2026-03-11  1:12   ` Eric Biggers
  4 siblings, 1 reply; 80+ messages in thread
From: Nicolas Schier @ 2026-02-21 21:38 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

Hi Thomas,

On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.
> 
> The goal is to reach bit-for-bit reproducibility. Excluding certain
> parts of the build output from the reproducibility analysis would be
> error-prone and force each downstream consumer to introduce new tooling.
> 
> Introduce a new mechanism to ensure only well-known modules are loaded
> by embedding a merkle tree root of all modules built as part of the full
> kernel build into vmlinux.
> 
> Non-builtin modules can be validated as before through signatures.
> 
> Normally the .ko module files depend on a fully built vmlinux to be
> available for modpost validation and BTF generation. With
> CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> to build a merkle tree. This introduces a dependency cycle which is
> impossible to satisfy. Work around this by building the modules during
> link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> but before the final module hashes are
> 
> The PKCS7 format which is used for regular module signatures can not
> represent Merkle proofs, so a new kind of module signature is
> introduced. As this signature type is only ever used for builtin
> modules, no compatibility issues can arise.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  .gitignore                                   |   1 +
>  Documentation/kbuild/reproducible-builds.rst |   5 +-
>  Makefile                                     |   8 +-
>  include/asm-generic/vmlinux.lds.h            |  11 +
>  include/linux/module_hashes.h                |  25 ++
>  include/linux/module_signature.h             |   1 +
>  kernel/module/Kconfig                        |  21 +-
>  kernel/module/Makefile                       |   1 +
>  kernel/module/hashes.c                       |  92 ++++++
>  kernel/module/hashes_root.c                  |   6 +
>  kernel/module/internal.h                     |   1 +
>  kernel/module/main.c                         |   4 +-
>  scripts/.gitignore                           |   1 +
>  scripts/Makefile                             |   3 +
>  scripts/Makefile.modfinal                    |  11 +
>  scripts/Makefile.modinst                     |  13 +
>  scripts/Makefile.vmlinux                     |   5 +
>  scripts/link-vmlinux.sh                      |  14 +-
>  scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
>  security/lockdown/Kconfig                    |   2 +-
>  20 files changed, 685 insertions(+), 7 deletions(-)
> 
[...]

> diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> new file mode 100644
> index 000000000000..1abfcd3aa679
> --- /dev/null
> +++ b/kernel/module/hashes_root.c
> @@ -0,0 +1,6 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +#include <linux/module_hashes.h>
> +
> +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index e2d49122c2a1..e22837d3ac76 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
>  			       const char *secstrings);
>  
>  int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
>  
>  #ifdef CONFIG_DEBUG_KMEMLEAK
>  void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index 2a28a0ece809..fa30b6387936 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
>  
>  	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
>  		err = module_sig_check(info, sig, sig_len);
> +	} else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> +		err = module_hash_check(info, sig, sig_len);
>  	} else {
> -		pr_err("module: not signed with expected PKCS#7 message\n");
> +		pr_err("module: not signed with signature mechanism\n");
>  		err = -ENOPKG;

To prevent others from running into the same issue:

My first test got stuck here, as I tested with virtme-ng, which symlinks
modules from build tree to /lib/modules/$(uname -r)/..., resulting in

    [   15.956855] module: not signed with signature mechanism
    modprobe: ERROR: could not insert 'efivarfs': Package not installed

As the modules_install step was missing, modules were not being signed.


[...]
> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> new file mode 100644
> index 000000000000..a6ec0e21213b
> --- /dev/null
> +++ b/scripts/modules-merkle-tree.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Compute hashes for modules files and build a merkle tree.
> + *
> + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> + *
> + */
> +#define _GNU_SOURCE 1
> +#include <arpa/inet.h>
> +#include <err.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include <openssl/evp.h>
> +#include <openssl/err.h>
> +
> +#include "ssl-common.h"
> +
> +static int hash_size;
> +static EVP_MD_CTX *ctx;
> +
> +struct module_signature {
> +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> +	uint8_t		hash;		/* Digest algorithm [0] */
> +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> +	uint8_t		signer_len;	/* Length of signer's name [0] */
> +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> +	uint8_t		__pad[3];
> +	uint32_t	sig_len;	/* Length of signature data */
> +};
> +
> +#define PKEY_ID_MERKLE 3
> +
> +static const char magic_number[] = "~Module signature appended~\n";

This here will be the forth definition of struct module_signature,
increasing the risk of unwanted diversion.  I second Petr's suggestion
to reuse a _common_ definition instead.

(Here, even include/linux/module_signature.h could be included itself.)

> +
> +struct file_entry {
> +	char *name;
> +	unsigned int pos;
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> +	struct leaf_hash **l;
> +	unsigned int *entries;
> +	unsigned int levels;
> +};
> +
> +static inline void *xcalloc(size_t n, size_t size)
> +{
> +	void *p;
> +
> +	p = calloc(n, size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static void *xmalloc(size_t size)
> +{
> +	void *p;
> +
> +	p = malloc(size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static inline void *xreallocarray(void *oldp, size_t n, size_t size)
> +{
> +	void *p;
> +
> +	p = reallocarray(oldp, n, size);
> +	if (!p)
> +		errx(1, "Memory allocation failed");
> +
> +	return p;
> +}
> +
> +static inline char *xasprintf(const char *fmt, ...)
> +{
> +	va_list ap;
> +	char *strp;
> +	int ret;
> +
> +	va_start(ap, fmt);
> +	ret = vasprintf(&strp, fmt, ap);
> +	va_end(ap);
> +	if (ret == -1)
> +		err(1, "Memory allocation failed");
> +
> +	return strp;
> +}

Please consider moving these x* functions into scripts/include/xalloc.h
for reuse.  (I am sure someone else wrote this already, but I can't find
it...)

> 
> 

thanks for all your efforts for reproducibility!

As I have no clue about that:  Is the patent for merkle trees [1] a
problem when integrating that here?

Can you verify if I get the mechanics roughly correct?

  * Modules are merkle tree leaves.  Modules are built and logically
    paired by the order from modules.order; a single left-over module is
    paired with itself.

  * Hashes of paired modules are hashed again (branch node hash);
    hashes of pairs of branch nodes' hashes are hashed again;
    repeat until we reach the single merkle tree root hash

  * The final merkle tree root hash (and the count of tree levels) is
    included in vmlinux


'make && find . -name '*.ko' -exec rm {} \; && make' does not rebuild
the in-tree modules.  Shifting the module-hashes support from
scripts/link-vmlinux.sh to scripts/Makefile.vmlinux might (make it
easier) to fix this again.

Kind regards,
Nicolas



[1]: https://worldwide.espacenet.com/patent/search/family/022107098/publication/US4309569A?q=pn%3DUS4309569


-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: Fwd: [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped
  2026-02-20 16:55     ` Ihor Solodrai
@ 2026-02-23  7:40       ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-23  7:40 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
	bpf, linux-kernel

On 2026-02-20 08:55:45-0800, Ihor Solodrai wrote:
> On 2/20/26 1:29 AM, Thomas WeiÃschuh wrote:
> > could you take a look at the patch quoted below?
> > It is part of a patch series which only tangentially touches BTF [0].
> > If you prefer to receive the full patch series, please let me know and
> > I'll do that for the next revision.
> > 
> > [0] https://lore.kernel.org/lkml/20260113-module-hashes-v4-6-0b932db9b56b@weissschuh.net/
> 
> Hi Thomas, thank you for the ping.
> 
> The BTF generation process has significantly changed recently:
> https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/
> 
> I suggest you rebase the integrity checking series on top of that
> (it's already been merged into Linus' tree).
>
> Adding a .stamp is a small and simple change, so maybe it's worth
> landing separately through BPF tree?

On its own this stamp files doesn't make much sense in my opinion.
With the new BTF generation (and general changes to my series) it may
also not be necessary anymore. So I'd like to keep this patch as part
of the series.

The important point to get feedback on, is less the stamp file but the
usage of vmlinux.unstripped as base for module BTF generation as shown
in the patch below.
Looking at that again, I'll need to validate it with out-of-tree
modules.


Thomas

> > On 2026-01-13 13:28:51+0100, Thomas Weißschuh wrote:
> >> The upcoming module hashes functionality will build the modules in
> >> between the generation of the BTF data and the final link of vmlinux.
> >> At this point vmlinux is not yet built and therefore can't be used for
> >> module BTF generation. vmlinux.unstripped however is usable and
> >> sufficient for BTF generation.
> >>
> >> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> >> ---
> >>  scripts/Makefile.modfinal | 8 ++++----
> >>  1 file changed, 4 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> >> index adfef1e002a9..930db0524a0a 100644
> >> --- a/scripts/Makefile.modfinal
> >> +++ b/scripts/Makefile.modfinal
> >> @@ -40,11 +40,11 @@ quiet_cmd_ld_ko_o = LD [M]  $@
> >>  
> >>  quiet_cmd_btf_ko = BTF [M] $@
> >>        cmd_btf_ko = 							\
> >> -	if [ ! -f $(objtree)/vmlinux ]; then				\
> >> -		printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
> >> +	if [ ! -f $(objtree)/vmlinux.unstripped ]; then			\
> >> +		printf "Skipping BTF generation for %s due to unavailability of vmlinux.unstripped\n" $@ 1>&2; \
> >>  	else								\
> >> -		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
> >> -		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@;		\
> >> +		LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux.unstripped $@; \
> >> +		$(RESOLVE_BTFIDS) -b $(objtree)/vmlinux.unstripped $@;	\
> >>  	fi;
> >>  
> >>  # Same as newer-prereqs, but allows to exclude specified extra dependencies
> >>
> >> -- 
> >> 2.52.0
> >>
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-21 21:38   ` Nicolas Schier
@ 2026-02-23  7:53     ` Thomas Weißschuh
  2026-02-23 18:41       ` Nicolas Schier
  0 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-23  7:53 UTC (permalink / raw)
  To: Nicolas Schier
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-02-21 22:38:29+0100, Nicolas Schier wrote:
> On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> > The current signature-based module integrity checking has some drawbacks
> > in combination with reproducible builds. Either the module signing key
> > is generated at build time, which makes the build unreproducible, or a
> > static signing key is used, which precludes rebuilds by third parties
> > and makes the whole build and packaging process much more complicated.
> > 
> > The goal is to reach bit-for-bit reproducibility. Excluding certain
> > parts of the build output from the reproducibility analysis would be
> > error-prone and force each downstream consumer to introduce new tooling.
> > 
> > Introduce a new mechanism to ensure only well-known modules are loaded
> > by embedding a merkle tree root of all modules built as part of the full
> > kernel build into vmlinux.
> > 
> > Non-builtin modules can be validated as before through signatures.
> > 
> > Normally the .ko module files depend on a fully built vmlinux to be
> > available for modpost validation and BTF generation. With
> > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > to build a merkle tree. This introduces a dependency cycle which is
> > impossible to satisfy. Work around this by building the modules during
> > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > but before the final module hashes are
> > 
> > The PKCS7 format which is used for regular module signatures can not
> > represent Merkle proofs, so a new kind of module signature is
> > introduced. As this signature type is only ever used for builtin
> > modules, no compatibility issues can arise.
> > 
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > ---
> >  .gitignore                                   |   1 +
> >  Documentation/kbuild/reproducible-builds.rst |   5 +-
> >  Makefile                                     |   8 +-
> >  include/asm-generic/vmlinux.lds.h            |  11 +
> >  include/linux/module_hashes.h                |  25 ++
> >  include/linux/module_signature.h             |   1 +
> >  kernel/module/Kconfig                        |  21 +-
> >  kernel/module/Makefile                       |   1 +
> >  kernel/module/hashes.c                       |  92 ++++++
> >  kernel/module/hashes_root.c                  |   6 +
> >  kernel/module/internal.h                     |   1 +
> >  kernel/module/main.c                         |   4 +-
> >  scripts/.gitignore                           |   1 +
> >  scripts/Makefile                             |   3 +
> >  scripts/Makefile.modfinal                    |  11 +
> >  scripts/Makefile.modinst                     |  13 +
> >  scripts/Makefile.vmlinux                     |   5 +
> >  scripts/link-vmlinux.sh                      |  14 +-
> >  scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
> >  security/lockdown/Kconfig                    |   2 +-
> >  20 files changed, 685 insertions(+), 7 deletions(-)
> > 
> [...]
> 
> > diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> > new file mode 100644
> > index 000000000000..1abfcd3aa679
> > --- /dev/null
> > +++ b/kernel/module/hashes_root.c
> > @@ -0,0 +1,6 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#include <linux/module_hashes.h>
> > +
> > +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> > +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> > diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> > index e2d49122c2a1..e22837d3ac76 100644
> > --- a/kernel/module/internal.h
> > +++ b/kernel/module/internal.h
> > @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> >  			       const char *secstrings);
> >  
> >  int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> > +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
> >  
> >  #ifdef CONFIG_DEBUG_KMEMLEAK
> >  void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > index 2a28a0ece809..fa30b6387936 100644
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
> >  
> >  	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> >  		err = module_sig_check(info, sig, sig_len);
> > +	} else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> > +		err = module_hash_check(info, sig, sig_len);
> >  	} else {
> > -		pr_err("module: not signed with expected PKCS#7 message\n");
> > +		pr_err("module: not signed with signature mechanism\n");
> >  		err = -ENOPKG;
> 
> To prevent others from running into the same issue:
> 
> My first test got stuck here, as I tested with virtme-ng, which symlinks
> modules from build tree to /lib/modules/$(uname -r)/..., resulting in
> 
>     [   15.956855] module: not signed with signature mechanism
>     modprobe: ERROR: could not insert 'efivarfs': Package not installed
> 
> As the modules_install step was missing, modules were not being signed.

Currently the signing is deferred to installation time to keep in sync
with regular module signing and to keep the logic simpler by not having
to gracefully handle previously-signed files.
But this could be changed.

> [...]
> > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> > new file mode 100644
> > index 000000000000..a6ec0e21213b
> > --- /dev/null
> > +++ b/scripts/modules-merkle-tree.c
> > @@ -0,0 +1,467 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Compute hashes for modules files and build a merkle tree.
> > + *
> > + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> > + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> > + *
> > + */
> > +#define _GNU_SOURCE 1
> > +#include <arpa/inet.h>
> > +#include <err.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <stdarg.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <stdbool.h>
> > +#include <stdlib.h>
> > +
> > +#include <sys/stat.h>
> > +#include <sys/mman.h>
> > +
> > +#include <openssl/evp.h>
> > +#include <openssl/err.h>
> > +
> > +#include "ssl-common.h"
> > +
> > +static int hash_size;
> > +static EVP_MD_CTX *ctx;
> > +
> > +struct module_signature {
> > +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> > +	uint8_t		hash;		/* Digest algorithm [0] */
> > +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> > +	uint8_t		signer_len;	/* Length of signer's name [0] */
> > +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> > +	uint8_t		__pad[3];
> > +	uint32_t	sig_len;	/* Length of signature data */
> > +};
> > +
> > +#define PKEY_ID_MERKLE 3
> > +
> > +static const char magic_number[] = "~Module signature appended~\n";
> 
> This here will be the forth definition of struct module_signature,
> increasing the risk of unwanted diversion.  I second Petr's suggestion
> to reuse a _common_ definition instead.

Ack.

> (Here, even include/linux/module_signature.h could be included itself.)

I'd like to avoid including internal headers from other components.
We could move it to an UAPI header. Various other subsystems use those
for not-really-UAPI but still ABI definitions.

(...)

> > +static inline char *xasprintf(const char *fmt, ...)
> > +{
> > +	va_list ap;
> > +	char *strp;
> > +	int ret;
> > +
> > +	va_start(ap, fmt);
> > +	ret = vasprintf(&strp, fmt, ap);
> > +	va_end(ap);
> > +	if (ret == -1)
> > +		err(1, "Memory allocation failed");
> > +
> > +	return strp;
> > +}
> 
> Please consider moving these x* functions into scripts/include/xalloc.h
> for reuse.  (I am sure someone else wrote this already, but I can't find
> it...)

Petr suggested it somewhere, it is done for the next revision.

> thanks for all your efforts for reproducibility!
> 
> As I have no clue about that:  Is the patent for merkle trees [1] a
> problem when integrating that here?

That should have expired a long time ago [2].
And fs-verity is also using merkle trees.

> Can you verify if I get the mechanics roughly correct?
> 
>   * Modules are merkle tree leaves.  Modules are built and logically
>     paired by the order from modules.order; a single left-over module is
>     paired with itself.
> 
>   * Hashes of paired modules are hashed again (branch node hash);
>     hashes of pairs of branch nodes' hashes are hashed again;
>     repeat until we reach the single merkle tree root hash
> 
>   * The final merkle tree root hash (and the count of tree levels) is
>     included in vmlinux

The merkle tree code was written by Sebastian so he will have the best
knowledge about it. But this is also my understanding.

> 'make && find . -name '*.ko' -exec rm {} \; && make' does not rebuild
> the in-tree modules.  Shifting the module-hashes support from
> scripts/link-vmlinux.sh to scripts/Makefile.vmlinux might (make it
> easier) to fix this again.

I'll take a look at it.

> [1]: https://worldwide.espacenet.com/patent/search/family/022107098/publication/US4309569A?q=pn%3DUS4309569

[2] https://patents.stackexchange.com/questions/17901/validity-of-patent-on-merkle-trees


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-23  7:53     ` Thomas Weißschuh
@ 2026-02-23 18:41       ` Nicolas Schier
  2026-02-23 21:43         ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Nicolas Schier @ 2026-02-23 18:41 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Mon, Feb 23, 2026 at 08:53:29AM +0100, Thomas Weißschuh wrote:
> On 2026-02-21 22:38:29+0100, Nicolas Schier wrote:
> > On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> > > The current signature-based module integrity checking has some drawbacks
> > > in combination with reproducible builds. Either the module signing key
> > > is generated at build time, which makes the build unreproducible, or a
> > > static signing key is used, which precludes rebuilds by third parties
> > > and makes the whole build and packaging process much more complicated.
> > > 
> > > The goal is to reach bit-for-bit reproducibility. Excluding certain
> > > parts of the build output from the reproducibility analysis would be
> > > error-prone and force each downstream consumer to introduce new tooling.
> > > 
> > > Introduce a new mechanism to ensure only well-known modules are loaded
> > > by embedding a merkle tree root of all modules built as part of the full
> > > kernel build into vmlinux.
> > > 
> > > Non-builtin modules can be validated as before through signatures.
> > > 
> > > Normally the .ko module files depend on a fully built vmlinux to be
> > > available for modpost validation and BTF generation. With
> > > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > > to build a merkle tree. This introduces a dependency cycle which is
> > > impossible to satisfy. Work around this by building the modules during
> > > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > > but before the final module hashes are
> > > 
> > > The PKCS7 format which is used for regular module signatures can not
> > > represent Merkle proofs, so a new kind of module signature is
> > > introduced. As this signature type is only ever used for builtin
> > > modules, no compatibility issues can arise.
> > > 
> > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > > ---
> > >  .gitignore                                   |   1 +
> > >  Documentation/kbuild/reproducible-builds.rst |   5 +-
> > >  Makefile                                     |   8 +-
> > >  include/asm-generic/vmlinux.lds.h            |  11 +
> > >  include/linux/module_hashes.h                |  25 ++
> > >  include/linux/module_signature.h             |   1 +
> > >  kernel/module/Kconfig                        |  21 +-
> > >  kernel/module/Makefile                       |   1 +
> > >  kernel/module/hashes.c                       |  92 ++++++
> > >  kernel/module/hashes_root.c                  |   6 +
> > >  kernel/module/internal.h                     |   1 +
> > >  kernel/module/main.c                         |   4 +-
> > >  scripts/.gitignore                           |   1 +
> > >  scripts/Makefile                             |   3 +
> > >  scripts/Makefile.modfinal                    |  11 +
> > >  scripts/Makefile.modinst                     |  13 +
> > >  scripts/Makefile.vmlinux                     |   5 +
> > >  scripts/link-vmlinux.sh                      |  14 +-
> > >  scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
> > >  security/lockdown/Kconfig                    |   2 +-
> > >  20 files changed, 685 insertions(+), 7 deletions(-)
> > > 
> > [...]
> > 
> > > diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> > > new file mode 100644
> > > index 000000000000..1abfcd3aa679
> > > --- /dev/null
> > > +++ b/kernel/module/hashes_root.c
> > > @@ -0,0 +1,6 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +
> > > +#include <linux/module_hashes.h>
> > > +
> > > +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> > > +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> > > diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> > > index e2d49122c2a1..e22837d3ac76 100644
> > > --- a/kernel/module/internal.h
> > > +++ b/kernel/module/internal.h
> > > @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> > >  			       const char *secstrings);
> > >  
> > >  int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> > > +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
> > >  
> > >  #ifdef CONFIG_DEBUG_KMEMLEAK
> > >  void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> > > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > > index 2a28a0ece809..fa30b6387936 100644
> > > --- a/kernel/module/main.c
> > > +++ b/kernel/module/main.c
> > > @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
> > >  
> > >  	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> > >  		err = module_sig_check(info, sig, sig_len);
> > > +	} else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> > > +		err = module_hash_check(info, sig, sig_len);
> > >  	} else {
> > > -		pr_err("module: not signed with expected PKCS#7 message\n");
> > > +		pr_err("module: not signed with signature mechanism\n");
> > >  		err = -ENOPKG;
> > 
> > To prevent others from running into the same issue:
> > 
> > My first test got stuck here, as I tested with virtme-ng, which symlinks
> > modules from build tree to /lib/modules/$(uname -r)/..., resulting in
> > 
> >     [   15.956855] module: not signed with signature mechanism
> >     modprobe: ERROR: could not insert 'efivarfs': Package not installed
> > 
> > As the modules_install step was missing, modules were not being signed.
> 
> Currently the signing is deferred to installation time to keep in sync
> with regular module signing and to keep the logic simpler by not having
> to gracefully handle previously-signed files.
> But this could be changed.

I did not want to suggest changing the behaviour, that would make things
more complicated to prevent needless rebuilds.  I just wanted to mention
it here to prevent others from burning time.

> > [...]
> > > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> > > new file mode 100644
> > > index 000000000000..a6ec0e21213b
> > > --- /dev/null
> > > +++ b/scripts/modules-merkle-tree.c
> > > @@ -0,0 +1,467 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * Compute hashes for modules files and build a merkle tree.
> > > + *
> > > + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> > > + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> > > + *
> > > + */
> > > +#define _GNU_SOURCE 1
> > > +#include <arpa/inet.h>
> > > +#include <err.h>
> > > +#include <unistd.h>
> > > +#include <fcntl.h>
> > > +#include <stdarg.h>
> > > +#include <stdio.h>
> > > +#include <string.h>
> > > +#include <stdbool.h>
> > > +#include <stdlib.h>
> > > +
> > > +#include <sys/stat.h>
> > > +#include <sys/mman.h>
> > > +
> > > +#include <openssl/evp.h>
> > > +#include <openssl/err.h>
> > > +
> > > +#include "ssl-common.h"
> > > +
> > > +static int hash_size;
> > > +static EVP_MD_CTX *ctx;
> > > +
> > > +struct module_signature {
> > > +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> > > +	uint8_t		hash;		/* Digest algorithm [0] */
> > > +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> > > +	uint8_t		signer_len;	/* Length of signer's name [0] */
> > > +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> > > +	uint8_t		__pad[3];
> > > +	uint32_t	sig_len;	/* Length of signature data */
> > > +};
> > > +
> > > +#define PKEY_ID_MERKLE 3
> > > +
> > > +static const char magic_number[] = "~Module signature appended~\n";
> > 
> > This here will be the forth definition of struct module_signature,
> > increasing the risk of unwanted diversion.  I second Petr's suggestion
> > to reuse a _common_ definition instead.
> 
> Ack.
> 
> > (Here, even include/linux/module_signature.h could be included itself.)
> 
> I'd like to avoid including internal headers from other components.
> We could move it to an UAPI header. Various other subsystems use those
> for not-really-UAPI but still ABI definitions.

Yeah, ack.

> (...)
> 
> > > +static inline char *xasprintf(const char *fmt, ...)
> > > +{
> > > +	va_list ap;
> > > +	char *strp;
> > > +	int ret;
> > > +
> > > +	va_start(ap, fmt);
> > > +	ret = vasprintf(&strp, fmt, ap);
> > > +	va_end(ap);
> > > +	if (ret == -1)
> > > +		err(1, "Memory allocation failed");
> > > +
> > > +	return strp;
> > > +}
> > 
> > Please consider moving these x* functions into scripts/include/xalloc.h
> > for reuse.  (I am sure someone else wrote this already, but I can't find
> > it...)
> 
> Petr suggested it somewhere, it is done for the next revision.
> 
> > thanks for all your efforts for reproducibility!
> > 
> > As I have no clue about that:  Is the patent for merkle trees [1] a
> > problem when integrating that here?
> 
> That should have expired a long time ago [2].
> And fs-verity is also using merkle trees.

Great, thanks.


> > Can you verify if I get the mechanics roughly correct?
> > 
> >   * Modules are merkle tree leaves.  Modules are built and logically
> >     paired by the order from modules.order; a single left-over module is
> >     paired with itself.
> > 
> >   * Hashes of paired modules are hashed again (branch node hash);
> >     hashes of pairs of branch nodes' hashes are hashed again;
> >     repeat until we reach the single merkle tree root hash
> > 
> >   * The final merkle tree root hash (and the count of tree levels) is
> >     included in vmlinux
> 
> The merkle tree code was written by Sebastian so he will have the best
> knowledge about it. But this is also my understanding.

I'd like to see some (rough) description in Documentation or in a commit
message at least, otherwise future me will have to ask that again.


> > 'make && find . -name '*.ko' -exec rm {} \; && make' does not rebuild
> > the in-tree modules.  Shifting the module-hashes support from
> > scripts/link-vmlinux.sh to scripts/Makefile.vmlinux might (make it
> > easier) to fix this again.
> 
> I'll take a look at it.

Thanks!

Kind regards,
Nicolas



> > [1]: https://worldwide.espacenet.com/patent/search/family/022107098/publication/US4309569A?q=pn%3DUS4309569
> 
> [2] https://patents.stackexchange.com/questions/17901/validity-of-patent-on-merkle-trees
> 
> 
> Thomas

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-23 18:41       ` Nicolas Schier
@ 2026-02-23 21:43         ` Thomas Weißschuh
  2026-02-24 16:14           ` Nicolas Schier
  0 siblings, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-02-23 21:43 UTC (permalink / raw)
  To: Nicolas Schier, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Petr Pavlu, Sami Tolvanen, Daniel Gomez,
	Paul Moore, James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Daniel Gomez, Aaron Tomlin,
	Christophe Leroy (CS GROUP), Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-02-23 19:41:52+0100, Nicolas Schier wrote:
> On Mon, Feb 23, 2026 at 08:53:29AM +0100, Thomas Weißschuh wrote:
> > On 2026-02-21 22:38:29+0100, Nicolas Schier wrote:
> > > On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> > > > The current signature-based module integrity checking has some drawbacks
> > > > in combination with reproducible builds. Either the module signing key
> > > > is generated at build time, which makes the build unreproducible, or a
> > > > static signing key is used, which precludes rebuilds by third parties
> > > > and makes the whole build and packaging process much more complicated.
> > > > 
> > > > The goal is to reach bit-for-bit reproducibility. Excluding certain
> > > > parts of the build output from the reproducibility analysis would be
> > > > error-prone and force each downstream consumer to introduce new tooling.
> > > > 
> > > > Introduce a new mechanism to ensure only well-known modules are loaded
> > > > by embedding a merkle tree root of all modules built as part of the full
> > > > kernel build into vmlinux.
> > > > 
> > > > Non-builtin modules can be validated as before through signatures.
> > > > 
> > > > Normally the .ko module files depend on a fully built vmlinux to be
> > > > available for modpost validation and BTF generation. With
> > > > CONFIG_MODULE_HASHES, vmlinux now depends on the modules
> > > > to build a merkle tree. This introduces a dependency cycle which is
> > > > impossible to satisfy. Work around this by building the modules during
> > > > link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
> > > > but before the final module hashes are
> > > > 
> > > > The PKCS7 format which is used for regular module signatures can not
> > > > represent Merkle proofs, so a new kind of module signature is
> > > > introduced. As this signature type is only ever used for builtin
> > > > modules, no compatibility issues can arise.
> > > > 
> > > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > > > ---
> > > >  .gitignore                                   |   1 +
> > > >  Documentation/kbuild/reproducible-builds.rst |   5 +-
> > > >  Makefile                                     |   8 +-
> > > >  include/asm-generic/vmlinux.lds.h            |  11 +
> > > >  include/linux/module_hashes.h                |  25 ++
> > > >  include/linux/module_signature.h             |   1 +
> > > >  kernel/module/Kconfig                        |  21 +-
> > > >  kernel/module/Makefile                       |   1 +
> > > >  kernel/module/hashes.c                       |  92 ++++++
> > > >  kernel/module/hashes_root.c                  |   6 +
> > > >  kernel/module/internal.h                     |   1 +
> > > >  kernel/module/main.c                         |   4 +-
> > > >  scripts/.gitignore                           |   1 +
> > > >  scripts/Makefile                             |   3 +
> > > >  scripts/Makefile.modfinal                    |  11 +
> > > >  scripts/Makefile.modinst                     |  13 +
> > > >  scripts/Makefile.vmlinux                     |   5 +
> > > >  scripts/link-vmlinux.sh                      |  14 +-
> > > >  scripts/modules-merkle-tree.c                | 467 +++++++++++++++++++++++++++
> > > >  security/lockdown/Kconfig                    |   2 +-
> > > >  20 files changed, 685 insertions(+), 7 deletions(-)
> > > > 
> > > [...]
> > > 
> > > > diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
> > > > new file mode 100644
> > > > index 000000000000..1abfcd3aa679
> > > > --- /dev/null
> > > > +++ b/kernel/module/hashes_root.c
> > > > @@ -0,0 +1,6 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > +
> > > > +#include <linux/module_hashes.h>
> > > > +
> > > > +/* Blank dummy data. Will be overridden by link-vmlinux.sh */
> > > > +const struct module_hashes_root module_hashes_root __module_hashes_section = {};
> > > > diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> > > > index e2d49122c2a1..e22837d3ac76 100644
> > > > --- a/kernel/module/internal.h
> > > > +++ b/kernel/module/internal.h
> > > > @@ -338,6 +338,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> > > >  			       const char *secstrings);
> > > >  
> > > >  int module_sig_check(struct load_info *info, const u8 *sig, size_t sig_len);
> > > > +int module_hash_check(struct load_info *info, const u8 *sig, size_t sig_len);
> > > >  
> > > >  #ifdef CONFIG_DEBUG_KMEMLEAK
> > > >  void kmemleak_load_module(const struct module *mod, const struct load_info *info);
> > > > diff --git a/kernel/module/main.c b/kernel/module/main.c
> > > > index 2a28a0ece809..fa30b6387936 100644
> > > > --- a/kernel/module/main.c
> > > > +++ b/kernel/module/main.c
> > > > @@ -3362,8 +3362,10 @@ static int module_integrity_check(struct load_info *info, int flags)
> > > >  
> > > >  	if (IS_ENABLED(CONFIG_MODULE_SIG) && sig_type == PKEY_ID_PKCS7) {
> > > >  		err = module_sig_check(info, sig, sig_len);
> > > > +	} else if (IS_ENABLED(CONFIG_MODULE_HASHES) && sig_type == PKEY_ID_MERKLE) {
> > > > +		err = module_hash_check(info, sig, sig_len);
> > > >  	} else {
> > > > -		pr_err("module: not signed with expected PKCS#7 message\n");
> > > > +		pr_err("module: not signed with signature mechanism\n");
> > > >  		err = -ENOPKG;
> > > 
> > > To prevent others from running into the same issue:
> > > 
> > > My first test got stuck here, as I tested with virtme-ng, which symlinks
> > > modules from build tree to /lib/modules/$(uname -r)/..., resulting in
> > > 
> > >     [   15.956855] module: not signed with signature mechanism
> > >     modprobe: ERROR: could not insert 'efivarfs': Package not installed
> > > 
> > > As the modules_install step was missing, modules were not being signed.
> > 
> > Currently the signing is deferred to installation time to keep in sync
> > with regular module signing and to keep the logic simpler by not having
> > to gracefully handle previously-signed files.
> > But this could be changed.
> 
> I did not want to suggest changing the behaviour, that would make things
> more complicated to prevent needless rebuilds.  I just wanted to mention
> it here to prevent others from burning time.

Understood.

> > > [...]
> > > > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> > > > new file mode 100644
> > > > index 000000000000..a6ec0e21213b
> > > > --- /dev/null
> > > > +++ b/scripts/modules-merkle-tree.c
> > > > @@ -0,0 +1,467 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > +/*
> > > > + * Compute hashes for modules files and build a merkle tree.
> > > > + *
> > > > + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> > > > + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> > > > + *
> > > > + */
> > > > +#define _GNU_SOURCE 1
> > > > +#include <arpa/inet.h>
> > > > +#include <err.h>
> > > > +#include <unistd.h>
> > > > +#include <fcntl.h>
> > > > +#include <stdarg.h>
> > > > +#include <stdio.h>
> > > > +#include <string.h>
> > > > +#include <stdbool.h>
> > > > +#include <stdlib.h>
> > > > +
> > > > +#include <sys/stat.h>
> > > > +#include <sys/mman.h>
> > > > +
> > > > +#include <openssl/evp.h>
> > > > +#include <openssl/err.h>
> > > > +
> > > > +#include "ssl-common.h"
> > > > +
> > > > +static int hash_size;
> > > > +static EVP_MD_CTX *ctx;
> > > > +
> > > > +struct module_signature {
> > > > +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> > > > +	uint8_t		hash;		/* Digest algorithm [0] */
> > > > +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> > > > +	uint8_t		signer_len;	/* Length of signer's name [0] */
> > > > +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> > > > +	uint8_t		__pad[3];
> > > > +	uint32_t	sig_len;	/* Length of signature data */
> > > > +};
> > > > +
> > > > +#define PKEY_ID_MERKLE 3
> > > > +
> > > > +static const char magic_number[] = "~Module signature appended~\n";
> > > 
> > > This here will be the forth definition of struct module_signature,
> > > increasing the risk of unwanted diversion.  I second Petr's suggestion
> > > to reuse a _common_ definition instead.
> > 
> > Ack.
> > 
> > > (Here, even include/linux/module_signature.h could be included itself.)
> > 
> > I'd like to avoid including internal headers from other components.
> > We could move it to an UAPI header. Various other subsystems use those
> > for not-really-UAPI but still ABI definitions.
> 
> Yeah, ack.

What exactly is the 'ack' for?
* Avoiding to include internal headers?
* Moving the definition to UAPI headers?

(...)

> > > Can you verify if I get the mechanics roughly correct?
> > > 
> > >   * Modules are merkle tree leaves.  Modules are built and logically
> > >     paired by the order from modules.order; a single left-over module is
> > >     paired with itself.
> > > 
> > >   * Hashes of paired modules are hashed again (branch node hash);
> > >     hashes of pairs of branch nodes' hashes are hashed again;
> > >     repeat until we reach the single merkle tree root hash
> > > 
> > >   * The final merkle tree root hash (and the count of tree levels) is
> > >     included in vmlinux
> > 
> > The merkle tree code was written by Sebastian so he will have the best
> > knowledge about it. But this is also my understanding.
> 
> I'd like to see some (rough) description in Documentation or in a commit
> message at least, otherwise future me will have to ask that again.

Ack in general. I'd prefer to document it in a source code comment,
though. That feels like the best fit to me.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-23 21:43         ` Thomas Weißschuh
@ 2026-02-24 16:14           ` Nicolas Schier
  0 siblings, 0 replies; 80+ messages in thread
From: Nicolas Schier @ 2026-02-24 16:14 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Daniel Gomez,
	Aaron Tomlin, Christophe Leroy (CS GROUP), Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Mon, Feb 23, 2026 at 10:43:30PM +0100, Thomas Weißschuh wrote:
> On 2026-02-23 19:41:52+0100, Nicolas Schier wrote:
> > On Mon, Feb 23, 2026 at 08:53:29AM +0100, Thomas Weißschuh wrote:
> > > On 2026-02-21 22:38:29+0100, Nicolas Schier wrote:
> > > > On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
[...]
> > > > [...]
> > > > > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> > > > > new file mode 100644
> > > > > index 000000000000..a6ec0e21213b
> > > > > --- /dev/null
> > > > > +++ b/scripts/modules-merkle-tree.c
> > > > > @@ -0,0 +1,467 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > > +/*
> > > > > + * Compute hashes for modules files and build a merkle tree.
> > > > > + *
> > > > > + * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
> > > > > + * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
> > > > > + *
> > > > > + */
> > > > > +#define _GNU_SOURCE 1
> > > > > +#include <arpa/inet.h>
> > > > > +#include <err.h>
> > > > > +#include <unistd.h>
> > > > > +#include <fcntl.h>
> > > > > +#include <stdarg.h>
> > > > > +#include <stdio.h>
> > > > > +#include <string.h>
> > > > > +#include <stdbool.h>
> > > > > +#include <stdlib.h>
> > > > > +
> > > > > +#include <sys/stat.h>
> > > > > +#include <sys/mman.h>
> > > > > +
> > > > > +#include <openssl/evp.h>
> > > > > +#include <openssl/err.h>
> > > > > +
> > > > > +#include "ssl-common.h"
> > > > > +
> > > > > +static int hash_size;
> > > > > +static EVP_MD_CTX *ctx;
> > > > > +
> > > > > +struct module_signature {
> > > > > +	uint8_t		algo;		/* Public-key crypto algorithm [0] */
> > > > > +	uint8_t		hash;		/* Digest algorithm [0] */
> > > > > +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
> > > > > +	uint8_t		signer_len;	/* Length of signer's name [0] */
> > > > > +	uint8_t		key_id_len;	/* Length of key identifier [0] */
> > > > > +	uint8_t		__pad[3];
> > > > > +	uint32_t	sig_len;	/* Length of signature data */
> > > > > +};
> > > > > +
> > > > > +#define PKEY_ID_MERKLE 3
> > > > > +
> > > > > +static const char magic_number[] = "~Module signature appended~\n";
> > > > 
> > > > This here will be the forth definition of struct module_signature,
> > > > increasing the risk of unwanted diversion.  I second Petr's suggestion
> > > > to reuse a _common_ definition instead.
> > > 
> > > Ack.
> > > 
> > > > (Here, even include/linux/module_signature.h could be included itself.)
> > > 
> > > I'd like to avoid including internal headers from other components.
> > > We could move it to an UAPI header. Various other subsystems use those
> > > for not-really-UAPI but still ABI definitions.
> > 
> > Yeah, ack.
> 
> What exactly is the 'ack' for?
> * Avoiding to include internal headers?
> * Moving the definition to UAPI headers?

ah, sorry.  I think reduction of duplicated definitions is good; moving
these definitions to UAPI headers sounds like a valid compromise to me.


> (...)
> 
> > > > Can you verify if I get the mechanics roughly correct?
> > > > 
> > > >   * Modules are merkle tree leaves.  Modules are built and logically
> > > >     paired by the order from modules.order; a single left-over module is
> > > >     paired with itself.
> > > > 
> > > >   * Hashes of paired modules are hashed again (branch node hash);
> > > >     hashes of pairs of branch nodes' hashes are hashed again;
> > > >     repeat until we reach the single merkle tree root hash
> > > > 
> > > >   * The final merkle tree root hash (and the count of tree levels) is
> > > >     included in vmlinux
> > > 
> > > The merkle tree code was written by Sebastian so he will have the best
> > > knowledge about it. But this is also my understanding.
> > 
> > I'd like to see some (rough) description in Documentation or in a commit
> > message at least, otherwise future me will have to ask that again.
> 
> Ack in general. I'd prefer to document it in a source code comment,
> though. That feels like the best fit to me.

Great, thanks.

-- 
Nicolas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
  2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
  2026-01-30 20:43   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
@ 2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 21:11 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:46PM +0100, Thomas Weißschuh wrote:
> When CONFIG_MODULE_SIG is disabled set_module_sig_enforced() is defined
> as an empty stub, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the check avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  arch/powerpc/kernel/ima_arch.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)

Reviewed-by: Eric Biggers <ebiggers@kernel.org>

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
  2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
  2026-01-30 20:49   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
@ 2026-03-10 21:11   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 21:11 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:47PM +0100, Thomas Weißschuh wrote:
> When configuration settings are disabled the guarded functions are
> defined as empty stubs, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the checks avoids some later churn.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  security/integrity/ima/ima_efi.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)

Reviewed-by: Eric Biggers <ebiggers@kernel.org>

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 04/17] module: Make mod_verify_sig() static
  2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
  2026-01-30 20:53   ` Aaron Tomlin
  2026-02-06  8:25   ` Nicolas Schier
@ 2026-03-10 21:12   ` Eric Biggers
  2 siblings, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 21:12 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:48PM +0100, Thomas Weißschuh wrote:
> It is not used outside of signing.c.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  kernel/module/internal.h | 1 -
>  kernel/module/signing.c  | 2 +-
>  2 files changed, 1 insertion(+), 2 deletions(-)

Reviewed-by: Eric Biggers <ebiggers@kernel.org>

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data
  2026-01-13 12:28 ` [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data Thomas Weißschuh
  2026-02-06 16:28   ` Nicolas Schier
@ 2026-03-10 21:36   ` Eric Biggers
  2026-03-11 12:58     ` Thomas Weißschuh
  1 sibling, 1 reply; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 21:36 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:50PM +0100, Thomas Weißschuh wrote:
> The upcoming module hashes functionality will build the modules in
> between the generation of the BTF data and the final link of vmlinux.
> Having a dependency from the modules on vmlinux would make this
> impossible as it would mean having a cyclic dependency.
> Break this cyclic dependency by introducing a new target.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  scripts/Makefile.modfinal | 4 ++--
>  scripts/link-vmlinux.sh   | 6 ++++++
>  2 files changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> index 149e12ff5700..adfef1e002a9 100644
> --- a/scripts/Makefile.modfinal
> +++ b/scripts/Makefile.modfinal
> @@ -56,8 +56,8 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
>  	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
>  
>  # Re-generate module BTFs if either module's .ko or vmlinux changed
> -%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
> -	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
> +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/.tmp_vmlinux_btf.stamp) FORCE
> +	+$(call if_changed_except,ld_ko_o,$(objtree)/.tmp_vmlinux_btf.stamp)
>  ifdef CONFIG_DEBUG_INFO_BTF_MODULES
>  	+$(if $(newer-prereqs),$(call cmd,btf_ko))
>  endif
> diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> index 4ab44c73da4d..8c98f8645a5c 100755
> --- a/scripts/link-vmlinux.sh
> +++ b/scripts/link-vmlinux.sh
> @@ -111,6 +111,7 @@ vmlinux_link()
>  gen_btf()
>  {
>  	local btf_data=${1}.btf.o
> +	local btf_stamp=.tmp_vmlinux_btf.stamp
>  
>  	info BTF "${btf_data}"
>  	LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
> @@ -131,6 +132,11 @@ gen_btf()
>  	fi
>  	printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
>  
> +	info STAMP $btf_stamp
> +	if ! cmp --silent $btf_data $btf_stamp; then
> +		cp $btf_data $btf_stamp
> +	fi

A "stamp file" is traditionally an empty file that is written when some
build step has completed.  The above code is instead copying the entire
.tmp_vmlinux1.btf.o file (megabytes in size) to .tmp_vmlinux_btf.stamp.

So, it's not clear to me why the stamp file is needed at all, versus
depending directly on .tmp_vmlinux1.btf.o.

I guess 'make' doesn't know about the dependencies of
.tmp_vmlinux1.btf.o.  But the same is true of the stamp file, right?  So
either way, how would 'make' know to finish rebuilding the file before
starting to execute the "Re-generate module BTFs" rule?

Also, passing the long option '--silent' to 'cmp' creates a dependency
on the GNU implementation of 'cmp', which isn't documented as a kernel
build dependency.  Probably better to use the short option '-s'.

Also, the stamp file isn't being deleted by 'make clean'.  It looks like
it would need to be added to cleanup() in link-vmlinux.sh.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG
  2026-01-13 12:28 ` [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG Thomas Weißschuh
@ 2026-03-10 22:01   ` Eric Biggers
  2026-03-11 12:59     ` Thomas Weißschuh
  0 siblings, 1 reply; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 22:01 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:53PM +0100, Thomas Weißschuh wrote:
> The loading policy functionality will also be used by the hash-based
> module validation. Split it out from CONFIG_MODULE_SIG so it is usable
> by both.
> 
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
>  include/linux/module.h  |  8 ++++----
>  kernel/module/Kconfig   |  5 ++++-
>  kernel/module/main.c    | 26 +++++++++++++++++++++++++-
>  kernel/module/signing.c | 21 ---------------------
>  4 files changed, 33 insertions(+), 27 deletions(-)
> 
> diff --git a/include/linux/module.h b/include/linux/module.h
> index f288ca5cd95b..f9601cba47cd 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -444,7 +444,7 @@ struct module {
>  	const u32 *gpl_crcs;
>  	bool using_gplonly_symbols;
>  
> -#ifdef CONFIG_MODULE_SIG
> +#ifdef CONFIG_MODULE_SIG_POLICY
>  	/* Signature was verified. */
>  	bool sig_ok;
>  #endif
[...]
> +config MODULE_SIG_POLICY
> +	def_bool MODULE_SIG

Maybe MODULE_AUTH_POLICY?  Hash-based module authentication does not use
signatures.

This issue appears elsewhere in the code too.  There are lots of places
that still refer to module signatures or "sigs", when really module
authentication is meant.

I'm not sure how far you want to go with the renaming, but it's
something to think about.  It's confusing to use the term "signature" to
mean something that is not a signature.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 10/17] module: Move integrity checks into dedicated function
  2026-01-13 12:28 ` [PATCH v4 10/17] module: Move integrity checks into dedicated function Thomas Weißschuh
  2026-02-13 15:09   ` Nicolas Schier
@ 2026-03-10 22:06   ` Eric Biggers
  1 sibling, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-10 22:06 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:54PM +0100, Thomas Weißschuh wrote:
> +static int module_integrity_check(struct load_info *info, int flags)
> +{
> +	int err = 0;
> +
> +	if (IS_ENABLED(CONFIG_MODULE_SIG))
> +		err = module_sig_check(info, flags);
> +
> +	return err;
> +}

Maybe module_authenticity_check()?  The purpose is authenticity, not
merely integrity.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
                     ` (3 preceding siblings ...)
  2026-02-21 21:38   ` Nicolas Schier
@ 2026-03-11  1:12   ` Eric Biggers
  2026-03-11  8:50     ` Sebastian Andrzej Siewior
  2026-03-11 13:19     ` Thomas Weißschuh
  4 siblings, 2 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-11  1:12 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> The current signature-based module integrity checking has some drawbacks
> in combination with reproducible builds. Either the module signing key
> is generated at build time, which makes the build unreproducible, or a
> static signing key is used, which precludes rebuilds by third parties
> and makes the whole build and packaging process much more complicated.

I think this actually undersells the feature.  It's also much simpler
than the signature-based module authentication.  The latter relies on
PKCS#7, X.509, ASN.1, OID registry, crypto_sig API, etc in addition to
the implementations of the actual signature algorithm (RSA / ECDSA /
ML-DSA) and at least one hash algorithm.

I've even seen a case where the vmlinux size decreases by almost 200KB
just by disabling module signing.  That's not even counting the
signatures themselves, which ML-DSA has increased to 2-5 KB each.

The hashes are much simpler, even accounting for the Merkle tree proofs
that make them scalable.  They're less likely to have vulnerabilities
like the PKCS#7 bugs the kernel has had historically.  They also
eliminate the dependency on a lot of userspace libcrypto functionality
that has been causing portability problems, such as the CMS functions.

So I think this is how module authentication should have been done
originally, and I'm glad to see this is finally being fixed.

> +struct module_hashes_proof {
> +	__be32 pos;
> +	u8 hash_sigs[][MODULE_HASHES_HASH_SIZE];
> +} __packed;

Is the choice of big endian for consistency with struct
module_signature?  Little endian is the usual choice in new code.

> diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
> index a45ce3b24403..3b510651830d 100644
> --- a/include/linux/module_signature.h
> +++ b/include/linux/module_signature.h
> @@ -18,6 +18,7 @@ enum pkey_id_type {
>  	PKEY_ID_PGP,		/* OpenPGP generated key ID */
>  	PKEY_ID_X509,		/* X.509 arbitrary subjectKeyIdentifier */
>  	PKEY_ID_PKCS7,		/* Signature in PKCS#7 message */
> +	PKEY_ID_MERKLE,		/* Merkle proof for modules */

I recommend making the hash algorithm explicit:

        PKEY_ID_MERKLE_SHA256,	/* SHA-256 merkle proof for modules */

While I wouldn't encourage the addition of another hash algorithm
(specifying one good algorithm for now is absolutely the right choice),
if someone ever does need to add another one, we'd want them to be
guided to simply introduce a new value of this enum rather than hack it
in some other way.

> +static void hash_entry(const void *left, const void *right, void *out)

Byte arrays should use u8 instead of void

> diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
[...]

> +struct file_entry {
> +	char *name;
> +	unsigned int pos;
> +	unsigned char hash[EVP_MAX_MD_SIZE];

Considering that the hash algorithm is fixed, EVP_MAX_MD_SIZE can be
replaced with a tighter local definition:

    #define MAX_HASH_SIZE 32

> +static struct file_entry *fh_list;
> +static size_t num_files;
> +
> +struct leaf_hash {
> +	unsigned char hash[EVP_MAX_MD_SIZE];
> +};
> +
> +struct mtree {
> +	struct leaf_hash **l;
> +	unsigned int *entries;
> +	unsigned int levels;
> +};

'struct leaf_hash' is confusing because it's actually used for the
hashes of internal nodes, not leaf nodes.

Maybe rename it to 'struct hash' and use it for both the hashes and leaf
nodes and internal nodes.

Also, clearer naming would improve readability, e.g.:

    struct merkle_tree {
            struct hash **level_hashes;
            unsigned int level_size;
            unsigned int num_levels;
    };

> +static void hash_data(void *p, unsigned int pos, size_t size, void *ret_hash)

static void hash_data(const uint8_t *data, unsigned int pos,
                      size_t size, struct hash *ret_hash)

> +	unsigned char magic = 0x01;

uint8_t

Also, when defining these magic numbers, maybe explicitly document that
they are domain separation prefixes:

        uint8_t magic = 0x01; /* domain separation prefix */

> +	unsigned int pos_be;

uint32_t

> +static void hash_entry(void *left, void *right, void *ret_hash)

Could use stronger typing:

static void hash_entry(const struct hash *left, const struct hash *right,
                       struct hash *ret_hash)

> +static struct mtree *build_merkle(struct file_entry *fh, size_t num)

Could use clearer names, and constify the file_entry array:

static struct merkle_tree *build_merkle(const struct file_entry *files,
                                        size_t num_files)

> +	/* First level of pairs */
> +	for (unsigned int i = 0; i < num; i += 2) {
> +		if (i == num - 1) {
> +			/* Odd number of files, no pair. Hash with itself */
> +			hash_entry(fh[i].hash, fh[i].hash, mt->l[0][i / 2].hash);
> +		} else {
> +			hash_entry(fh[i].hash, fh[i + 1].hash, mt->l[0][i / 2].hash);
> +		}
> +	}
> +	for (unsigned int i = 1; i < mt->levels; i++) {
> +		int odd = 0;
> +
> +		if (le & 1) {
> +			le++;
> +			odd++;
> +		}
> +
> +		mt->entries[i] = le / 2;
> +		mt->l[i] = xcalloc(sizeof(**mt->l), le);
> +
> +		for (unsigned int n = 0; n < le; n += 2) {
> +			if (n == le - 2 && odd) {
> +				/* Odd number of pairs, no pair. Hash with itself */
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n].hash,
> +					   mt->l[i][n / 2].hash);
> +			} else {
> +				hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n + 1].hash,
> +					   mt->l[i][n / 2].hash);
> +			}
> +		}
> +		le =  mt->entries[i];
> +	}

There should be an assertion at the end that we ended up with exactly 1
hash in the root level.

It might also be possible to refactor this code such that the leaf nodes
and internal nodes are handled in the same loop, rather than handling
the leaf nodes as a special case.

> +static void write_merkle_root(struct mtree *mt, const char *fp)

fp => filename since it's a string, not e.g. a 'FILE *'.

> +{
> +	char buf[1024];
> +	unsigned int levels;
> +	unsigned char *h;
> +	FILE *f;
> +
> +	if (mt) {
> +		levels = mt->levels;
> +		h = mt->l[mt->levels - 1][0].hash;
> +	} else {
> +		levels = 0;
> +		h = xcalloc(1, hash_size);
> +	}
> +
> +	f = fopen(fp, "w");
> +	if (!f)
> +		err(1, "Failed to create %s", buf);

Above should log the name of the file.  'buf' should be removed.

> +static char *xstrdup_replace_suffix(const char *str, const char *new_suffix)
> +{
> +	const char *current_suffix;
> +	size_t base_len;
> +
> +	current_suffix = strchr(str, '.');
> +	if (!current_suffix)
> +		errx(1, "No existing suffix in '%s'", str);

This doesn't handle base names that contain a period.  strrchr() would
work if the old suffix always contains exactly one period.  Otherwise
another solution would be needed to identify the old suffix.

> +static __attribute__((noreturn))
> +void format(void)

usage()

> +{
> +	fprintf(stderr,
> +		"Usage: scripts/modules-merkle-tree <root definition>\n");
> +	exit(2);

This should show both parameters, <root hash> <new suffix>

But they probably should be flipped to put the output second.

Though, is <new suffix> needed at all?  It looks like it doesn't
actually affect the output.

> +	hash_evp = EVP_get_digestbyname("sha256");

EVP_sha256()

> +	hash_size = EVP_MD_get_size(hash_evp);

The old name 'EVP_MD_size()' would have wider compatibility.

> +	ERR(hash_size <= 0, "EVP_get_digestbyname");

Log message should say EVP_MD_size

> +	for (unsigned int i = 0; i < num_files; i++) {

size_t, for consistency with the type of num_files

> +		fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
> +		if (fd < 0)
> +			err(1, "Can't create %s", signame);
> +
> +		build_proof(mt, i, fd);
> +		append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));

Maybe build_and_append_proof()?

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-02-03 12:19   ` Petr Pavlu
  2026-02-03 12:59     ` Thomas Weißschuh
@ 2026-03-11  1:18     ` Eric Biggers
  1 sibling, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-11  1:18 UTC (permalink / raw)
  To: Petr Pavlu
  Cc: Thomas Weißschuh, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Sami Tolvanen, Daniel Gomez, Paul Moore,
	James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Nicolas Schier, Daniel Gomez, Aaron Tomlin,
	Christophe Leroy (CS GROUP), Nicolas Schier, Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Tue, Feb 03, 2026 at 01:19:20PM +0100, Petr Pavlu wrote:
> > +static unsigned int get_pow2(unsigned int val)
> > +{
> > +	return 31 - __builtin_clz(val);
> > +}
> > +
> > +static unsigned int roundup_pow2(unsigned int val)
> > +{
> > +	return 1 << (get_pow2(val - 1) + 1);
> > +}
> > +
> > +static unsigned int log2_roundup(unsigned int val)
> > +{
> > +	return get_pow2(roundup_pow2(val));
> > +}
> 
> In the edge case when the kernel is built with only one module, the code
> calls log2_roundup(1) -> roundup_pow2(1) -> get_pow2(0) ->
> __builtin_clz(0). The return value of __builtin_clz() is undefined if
> the input is zero.

A suggestion:

        static unsigned int log2_roundup(unsigned int val)
        {
                if (val <= 1)
                        return 0;
                return 32 - __builtin_clz(val - 1);
        }

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-03-11  1:12   ` Eric Biggers
@ 2026-03-11  8:50     ` Sebastian Andrzej Siewior
  2026-03-11 13:19     ` Thomas Weißschuh
  1 sibling, 0 replies; 80+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-03-11  8:50 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Thomas Weißschuh, Nathan Chancellor, Arnd Bergmann,
	Luis Chamberlain, Petr Pavlu, Sami Tolvanen, Daniel Gomez,
	Paul Moore, James Morris, Serge E. Hallyn, Jonathan Corbet,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Naveen N Rao, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Nicolas Schier, Daniel Gomez, Aaron Tomlin,
	Christophe Leroy (CS GROUP), Nicolas Schier, Nicolas Bouchinet,
	Xiu Jianfeng, Fabian Grünbichler, Arnout Engelen,
	Mattia Rizzolo, kpcyrd, Christian Heusel, Câju Mihai-Drosi,
	linux-kbuild, linux-kernel, linux-arch, linux-modules,
	linux-security-module, linux-doc, linuxppc-dev, linux-integrity

On 2026-03-10 18:12:18 [-0700], Eric Biggers wrote:
> > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> [...]
> 
> > +struct file_entry {
> > +	char *name;
> > +	unsigned int pos;
> > +	unsigned char hash[EVP_MAX_MD_SIZE];
> 
> Considering that the hash algorithm is fixed, EVP_MAX_MD_SIZE can be
> replaced with a tighter local definition:
> 
>     #define MAX_HASH_SIZE 32
> 
> > +static struct file_entry *fh_list;
> > +static size_t num_files;
> > +
> > +struct leaf_hash {
> > +	unsigned char hash[EVP_MAX_MD_SIZE];
> > +};
> > +
> > +struct mtree {
> > +	struct leaf_hash **l;
> > +	unsigned int *entries;
> > +	unsigned int levels;
> > +};
> 
> 'struct leaf_hash' is confusing because it's actually used for the
> hashes of internal nodes, not leaf nodes.

You could still consider the internal nodes as leafs.

> Maybe rename it to 'struct hash' and use it for both the hashes and leaf
> nodes and internal nodes.
> 
> Also, clearer naming would improve readability, e.g.:
> 
>     struct merkle_tree {
>             struct hash **level_hashes;
>             unsigned int level_size;
>             unsigned int num_levels;
>     };

but this could improve it, indeed.

> > +	hash_evp = EVP_get_digestbyname("sha256");
> 
> EVP_sha256()

I would suggest to use EVP_MD_fetch() instead.

> > +	hash_size = EVP_MD_get_size(hash_evp);
> 
> The old name 'EVP_MD_size()' would have wider compatibility.

EVP_MD_fetch() and EVP_MD_get_size() are openssl 3.0.0+ and nothing
below 3.0.0 is considered supported (while 3.0.0 is EOL 07 Sep 2026).

Sebastian

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data
  2026-03-10 21:36   ` Eric Biggers
@ 2026-03-11 12:58     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-03-11 12:58 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-03-10 14:36:06-0700, Eric Biggers wrote:
> On Tue, Jan 13, 2026 at 01:28:50PM +0100, Thomas Weißschuh wrote:
> > The upcoming module hashes functionality will build the modules in
> > between the generation of the BTF data and the final link of vmlinux.
> > Having a dependency from the modules on vmlinux would make this
> > impossible as it would mean having a cyclic dependency.
> > Break this cyclic dependency by introducing a new target.
> > 
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > ---
> >  scripts/Makefile.modfinal | 4 ++--
> >  scripts/link-vmlinux.sh   | 6 ++++++
> >  2 files changed, 8 insertions(+), 2 deletions(-)
> > 
> > diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
> > index 149e12ff5700..adfef1e002a9 100644
> > --- a/scripts/Makefile.modfinal
> > +++ b/scripts/Makefile.modfinal
> > @@ -56,8 +56,8 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
> >  	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
> >  
> >  # Re-generate module BTFs if either module's .ko or vmlinux changed
> > -%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
> > -	+$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
> > +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/.tmp_vmlinux_btf.stamp) FORCE
> > +	+$(call if_changed_except,ld_ko_o,$(objtree)/.tmp_vmlinux_btf.stamp)
> >  ifdef CONFIG_DEBUG_INFO_BTF_MODULES
> >  	+$(if $(newer-prereqs),$(call cmd,btf_ko))
> >  endif
> > diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> > index 4ab44c73da4d..8c98f8645a5c 100755
> > --- a/scripts/link-vmlinux.sh
> > +++ b/scripts/link-vmlinux.sh
> > @@ -111,6 +111,7 @@ vmlinux_link()
> >  gen_btf()
> >  {
> >  	local btf_data=${1}.btf.o
> > +	local btf_stamp=.tmp_vmlinux_btf.stamp
> >  
> >  	info BTF "${btf_data}"
> >  	LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
> > @@ -131,6 +132,11 @@ gen_btf()
> >  	fi
> >  	printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
> >  
> > +	info STAMP $btf_stamp
> > +	if ! cmp --silent $btf_data $btf_stamp; then
> > +		cp $btf_data $btf_stamp
> > +	fi

This patch will be gone from the next revision of the series.
Making use of the recently introduced vmlinux.unstripped,
as suggested by Petr, removes any modifications to link-vmlinux.sh
and the issue that this patch tried to address.

> A "stamp file" is traditionally an empty file that is written when some
> build step has completed.  The above code is instead copying the entire
> .tmp_vmlinux1.btf.o file (megabytes in size) to .tmp_vmlinux_btf.stamp.

The goal here was not to only have a reference timestamp, but
specifically the reference file contents.

Note: The duplicated vmlinux.unstripped in its current form is by far
larger than .tmp_vmlinux1.btf.o.

> So, it's not clear to me why the stamp file is needed at all, versus
> depending directly on .tmp_vmlinux1.btf.o.
>
> I guess 'make' doesn't know about the dependencies of
> .tmp_vmlinux1.btf.o.  But the same is true of the stamp file, right?  So
> either way, how would 'make' know to finish rebuilding the file before
> starting to execute the "Re-generate module BTFs" rule?

The problem was not the ordering, this is handled within link-vmlinux.sh.
IIRC originally without this patch even no-op rebuilds would end up
rebuilding the modules. Using .tmp_vmlinux1.btf.o may have worked too.
But in v4, the patch "kbuild: generate module BTF based on
vmlinux.unstripped" was added, which also solves this problem.

> Also, passing the long option '--silent' to 'cmp' creates a dependency
> on the GNU implementation of 'cmp', which isn't documented as a kernel
> build dependency.  Probably better to use the short option '-s'.

Ack.

> Also, the stamp file isn't being deleted by 'make clean'.  It looks like
> it would need to be added to cleanup() in link-vmlinux.sh.

Ack.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG
  2026-03-10 22:01   ` Eric Biggers
@ 2026-03-11 12:59     ` Thomas Weißschuh
  0 siblings, 0 replies; 80+ messages in thread
From: Thomas Weißschuh @ 2026-03-11 12:59 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-03-10 15:01:46-0700, Eric Biggers wrote:
> On Tue, Jan 13, 2026 at 01:28:53PM +0100, Thomas Weißschuh wrote:
> > The loading policy functionality will also be used by the hash-based
> > module validation. Split it out from CONFIG_MODULE_SIG so it is usable
> > by both.
> > 
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> > ---
> >  include/linux/module.h  |  8 ++++----
> >  kernel/module/Kconfig   |  5 ++++-
> >  kernel/module/main.c    | 26 +++++++++++++++++++++++++-
> >  kernel/module/signing.c | 21 ---------------------
> >  4 files changed, 33 insertions(+), 27 deletions(-)
> > 
> > diff --git a/include/linux/module.h b/include/linux/module.h
> > index f288ca5cd95b..f9601cba47cd 100644
> > --- a/include/linux/module.h
> > +++ b/include/linux/module.h
> > @@ -444,7 +444,7 @@ struct module {
> >  	const u32 *gpl_crcs;
> >  	bool using_gplonly_symbols;
> >  
> > -#ifdef CONFIG_MODULE_SIG
> > +#ifdef CONFIG_MODULE_SIG_POLICY
> >  	/* Signature was verified. */
> >  	bool sig_ok;
> >  #endif
> [...]
> > +config MODULE_SIG_POLICY
> > +	def_bool MODULE_SIG
> 
> Maybe MODULE_AUTH_POLICY?  Hash-based module authentication does not use
> signatures.
> 
> This issue appears elsewhere in the code too.  There are lots of places
> that still refer to module signatures or "sigs", when really module
> authentication is meant.
> 
> I'm not sure how far you want to go with the renaming, but it's
> something to think about.  It's confusing to use the term "signature" to
> mean something that is not a signature.

Ack. "authentication" is much better, I'll use that.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-03-11  1:12   ` Eric Biggers
  2026-03-11  8:50     ` Sebastian Andrzej Siewior
@ 2026-03-11 13:19     ` Thomas Weißschuh
  2026-03-11 21:14       ` Eric Biggers
  1 sibling, 1 reply; 80+ messages in thread
From: Thomas Weißschuh @ 2026-03-11 13:19 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On 2026-03-10 18:12:18-0700, Eric Biggers wrote:
> On Tue, Jan 13, 2026 at 01:28:59PM +0100, Thomas Weißschuh wrote:
> > The current signature-based module integrity checking has some drawbacks
> > in combination with reproducible builds. Either the module signing key
> > is generated at build time, which makes the build unreproducible, or a
> > static signing key is used, which precludes rebuilds by third parties
> > and makes the whole build and packaging process much more complicated.
> 
> I think this actually undersells the feature.

(...)

> So I think this is how module authentication should have been done
> originally, and I'm glad to see this is finally being fixed.

Thanks, that is nice to hear.

> > +struct module_hashes_proof {
> > +	__be32 pos;
> > +	u8 hash_sigs[][MODULE_HASHES_HASH_SIZE];
> > +} __packed;
> 
> Is the choice of big endian for consistency with struct
> module_signature?  Little endian is the usual choice in new code.

Yes, it's for consistency. But I am fine with either way. Given that
this is essentially an internal ABI, we could always change it later.

> > diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
> > index a45ce3b24403..3b510651830d 100644
> > --- a/include/linux/module_signature.h
> > +++ b/include/linux/module_signature.h
> > @@ -18,6 +18,7 @@ enum pkey_id_type {
> >  	PKEY_ID_PGP,		/* OpenPGP generated key ID */
> >  	PKEY_ID_X509,		/* X.509 arbitrary subjectKeyIdentifier */
> >  	PKEY_ID_PKCS7,		/* Signature in PKCS#7 message */
> > +	PKEY_ID_MERKLE,		/* Merkle proof for modules */
> 
> I recommend making the hash algorithm explicit:
> 
>         PKEY_ID_MERKLE_SHA256,	/* SHA-256 merkle proof for modules */
> 
> While I wouldn't encourage the addition of another hash algorithm
> (specifying one good algorithm for now is absolutely the right choice),
> if someone ever does need to add another one, we'd want them to be
> guided to simply introduce a new value of this enum rather than hack it
> in some other way.

The idea here was that this will only ever be used for module built as
part of the kernel build. So the actual implementation could change freely
without affecting anything.

But I don't have hard feelings about it.

> > +static void hash_entry(const void *left, const void *right, void *out)
> 
> Byte arrays should use u8 instead of void

Ack.

> > diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
> [...]
> 
> > +struct file_entry {
> > +	char *name;
> > +	unsigned int pos;
> > +	unsigned char hash[EVP_MAX_MD_SIZE];
> 
> Considering that the hash algorithm is fixed, EVP_MAX_MD_SIZE can be
> replaced with a tighter local definition:

Ack.

>     #define MAX_HASH_SIZE 32

IMO it shouldn't even mention 'MAX', as there is only one hash
algorithm.

(...)

> > +{
> > +	fprintf(stderr,
> > +		"Usage: scripts/modules-merkle-tree <root definition>\n");
> > +	exit(2);
> 
> This should show both parameters, <root hash> <new suffix>

Ack.

> But they probably should be flipped to put the output second.

Ack.

> Though, is <new suffix> needed at all?  It looks like it doesn't
> actually affect the output.

It will be required for compatibility with INSTALL_MOD_STRIP,
two patches later. I'll move this code into the later patch.

> > +	hash_evp = EVP_get_digestbyname("sha256");
> 
> EVP_sha256()

(...)

Ack to all other remarks.


Thomas

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 15/17] module: Introduce hash-based integrity checking
  2026-03-11 13:19     ` Thomas Weißschuh
@ 2026-03-11 21:14       ` Eric Biggers
  0 siblings, 0 replies; 80+ messages in thread
From: Eric Biggers @ 2026-03-11 21:14 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Schier, Nicolas Bouchinet, Xiu Jianfeng,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity

On Wed, Mar 11, 2026 at 02:19:02PM +0100, Thomas Weißschuh wrote:
> > > diff --git a/include/linux/module_signature.h b/include/linux/module_signature.h
> > > index a45ce3b24403..3b510651830d 100644
> > > --- a/include/linux/module_signature.h
> > > +++ b/include/linux/module_signature.h
> > > @@ -18,6 +18,7 @@ enum pkey_id_type {
> > >  	PKEY_ID_PGP,		/* OpenPGP generated key ID */
> > >  	PKEY_ID_X509,		/* X.509 arbitrary subjectKeyIdentifier */
> > >  	PKEY_ID_PKCS7,		/* Signature in PKCS#7 message */
> > > +	PKEY_ID_MERKLE,		/* Merkle proof for modules */
> > 
> > I recommend making the hash algorithm explicit:
> > 
> >         PKEY_ID_MERKLE_SHA256,	/* SHA-256 merkle proof for modules */
> > 
> > While I wouldn't encourage the addition of another hash algorithm
> > (specifying one good algorithm for now is absolutely the right choice),
> > if someone ever does need to add another one, we'd want them to be
> > guided to simply introduce a new value of this enum rather than hack it
> > in some other way.
> 
> The idea here was that this will only ever be used for module built as
> part of the kernel build. So the actual implementation could change freely
> without affecting anything.
> 
> But I don't have hard feelings about it.

Ah, okay.  That's even better then: if someone adds another algorithm it
would simply be a kconfig option.

It seems 'struct module_signature' itself is intended to be a stable
ABI, though.  So I think there's an opportunity for confusion here.  It
might be worth leaving a note somewhere that the format of the
PKEY_ID_MERKLE portion of the struct does not need to be kept stable and
can freely change in each kernel build.

- Eric

^ permalink raw reply	[flat|nested] 80+ messages in thread

end of thread, other threads:[~2026-03-11 21:14 UTC | newest]

Thread overview: 80+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-13 12:28 [PATCH v4 00/17] module: Introduce hash-based integrity checking Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 01/17] module: Only declare set_module_sig_enforced when CONFIG_MODULE_SIG=y Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG Thomas Weißschuh
2026-01-30 20:43   ` Aaron Tomlin
2026-02-06  8:25   ` Nicolas Schier
2026-03-10 21:11   ` Eric Biggers
2026-01-13 12:28 ` [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG Thomas Weißschuh
2026-01-30 20:49   ` Aaron Tomlin
2026-02-06  8:25   ` Nicolas Schier
2026-03-10 21:11   ` Eric Biggers
2026-01-13 12:28 ` [PATCH v4 04/17] module: Make mod_verify_sig() static Thomas Weißschuh
2026-01-30 20:53   ` Aaron Tomlin
2026-02-06  8:25   ` Nicolas Schier
2026-03-10 21:12   ` Eric Biggers
2026-01-13 12:28 ` [PATCH v4 05/17] module: Switch load_info::len to size_t Thomas Weißschuh
2026-02-06  8:18   ` David Howells
2026-02-06  8:34     ` Thomas Weißschuh
2026-02-06  8:30   ` Nicolas Schier
2026-02-06  8:38     ` Thomas Weißschuh
2026-02-06  8:55       ` Nicolas Schier
2026-02-06  9:09   ` Christophe Leroy (CS GROUP)
2026-02-06  9:18     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 06/17] kbuild: add stamp file for vmlinux BTF data Thomas Weißschuh
2026-02-06 16:28   ` Nicolas Schier
2026-03-10 21:36   ` Eric Biggers
2026-03-11 12:58     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 07/17] kbuild: generate module BTF based on vmlinux.unstripped Thomas Weißschuh
2026-02-06 16:37   ` Nicolas Schier
2026-02-20  9:29   ` Fwd: " Thomas Weißschuh
2026-02-20 16:55     ` Ihor Solodrai
2026-02-23  7:40       ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 08/17] module: Deduplicate signature extraction Thomas Weißschuh
2026-01-27 15:20   ` Petr Pavlu
2026-02-03 12:41     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 09/17] module: Make module loading policy usable without MODULE_SIG Thomas Weißschuh
2026-03-10 22:01   ` Eric Biggers
2026-03-11 12:59     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 10/17] module: Move integrity checks into dedicated function Thomas Weißschuh
2026-02-13 15:09   ` Nicolas Schier
2026-03-10 22:06   ` Eric Biggers
2026-01-13 12:28 ` [PATCH v4 11/17] module: Move lockdown check into generic module loader Thomas Weißschuh
2026-02-13 15:14   ` Nicolas Schier
2026-01-13 12:28 ` [PATCH v4 12/17] module: Move signature splitting up Thomas Weißschuh
2026-01-29 14:41   ` Petr Pavlu
2026-02-03 12:42     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 13/17] module: Report signature type to users Thomas Weißschuh
2026-01-29 14:44   ` Petr Pavlu
2026-02-03 12:44     ` Thomas Weißschuh
2026-01-13 12:28 ` [PATCH v4 14/17] lockdown: Make the relationship to MODULE_SIG a dependency Thomas Weißschuh
2026-02-13 15:32   ` Nicolas Schier
2026-01-13 12:28 ` [PATCH v4 15/17] module: Introduce hash-based integrity checking Thomas Weißschuh
2026-01-13 14:56   ` Sebastian Andrzej Siewior
2026-01-30 17:06   ` Petr Pavlu
2026-02-03 12:55     ` Thomas Weißschuh
2026-02-06 17:12       ` Nicolas Schier
2026-02-19 14:27       ` Nicolas Schier
2026-02-03 12:19   ` Petr Pavlu
2026-02-03 12:59     ` Thomas Weißschuh
2026-03-11  1:18     ` Eric Biggers
2026-02-21 21:38   ` Nicolas Schier
2026-02-23  7:53     ` Thomas Weißschuh
2026-02-23 18:41       ` Nicolas Schier
2026-02-23 21:43         ` Thomas Weißschuh
2026-02-24 16:14           ` Nicolas Schier
2026-03-11  1:12   ` Eric Biggers
2026-03-11  8:50     ` Sebastian Andrzej Siewior
2026-03-11 13:19     ` Thomas Weißschuh
2026-03-11 21:14       ` Eric Biggers
2026-01-13 12:29 ` [PATCH v4 16/17] kbuild: move handling of module stripping to Makefile.lib Thomas Weißschuh
2026-01-13 12:29 ` [PATCH v4 17/17] kbuild: make CONFIG_MODULE_HASHES compatible with module stripping Thomas Weißschuh
2026-01-31  7:36 ` [PATCH v4 00/17] module: Introduce hash-based integrity checking Mihai-Drosi Câju
2026-02-01 16:22   ` Thomas Weißschuh
2026-02-01 17:09   ` David Howells
2026-02-01 20:12     ` Eric Biggers
2026-02-02  9:21       ` David Howells
2026-02-02 18:30         ` Eric Biggers
2026-02-02 18:38           ` David Howells
2026-02-02 18:47             ` Eric Biggers
2026-02-03  8:18     ` James Bottomley
2026-02-03  8:22       ` David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox