public inbox for linux-modules@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 3/8] pkcs7, x509: Add ML-DSA support
From: David Howells @ 2026-01-05 15:21 UTC (permalink / raw)
  To: Lukas Wunner, Ignat Korchagin
  Cc: David Howells, Jarkko Sakkinen, Herbert Xu, Eric Biggers,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Jason A . Donenfeld, Ard Biesheuvel, Stephan Mueller,
	linux-crypto, keyrings, linux-modules, linux-kernel
In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com>

Add support for ML-DSA keys and signatures to the PKCS#7 and X.509
implementations.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: keyrings@vger.kernel.org
cc: linux-crypto@vger.kernel.org
---
 crypto/asymmetric_keys/pkcs7_parser.c     | 15 ++++++++++++++
 crypto/asymmetric_keys/public_key.c       |  7 +++++++
 crypto/asymmetric_keys/x509_cert_parser.c | 24 +++++++++++++++++++++++
 include/linux/oid_registry.h              |  5 +++++
 4 files changed, 51 insertions(+)

diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 3cdbab3b9f50..90c36fe1b5ed 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -297,6 +297,21 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
 		ctx->sinfo->sig->pkey_algo = "ecrdsa";
 		ctx->sinfo->sig->encoding = "raw";
 		break;
+	case OID_id_ml_dsa_44:
+		ctx->sinfo->sig->pkey_algo = "mldsa44";
+		ctx->sinfo->sig->encoding = "raw";
+		ctx->sinfo->sig->algo_does_hash = true;
+		break;
+	case OID_id_ml_dsa_65:
+		ctx->sinfo->sig->pkey_algo = "mldsa65";
+		ctx->sinfo->sig->encoding = "raw";
+		ctx->sinfo->sig->algo_does_hash = true;
+		break;
+	case OID_id_ml_dsa_87:
+		ctx->sinfo->sig->pkey_algo = "mldsa87";
+		ctx->sinfo->sig->encoding = "raw";
+		ctx->sinfo->sig->algo_does_hash = true;
+		break;
 	default:
 		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
 		return -ENOPKG;
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index e5b177c8e842..ed6b4b5ae4ef 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -142,6 +142,13 @@ software_key_determine_akcipher(const struct public_key *pkey,
 		if (strcmp(hash_algo, "streebog256") != 0 &&
 		    strcmp(hash_algo, "streebog512") != 0)
 			return -EINVAL;
+	} else if (strcmp(pkey->pkey_algo, "mldsa44") == 0 ||
+		   strcmp(pkey->pkey_algo, "mldsa65") == 0 ||
+		   strcmp(pkey->pkey_algo, "mldsa87") == 0) {
+		if (strcmp(encoding, "raw") != 0)
+			return -EINVAL;
+		if (!hash_algo)
+			return -EINVAL;
 	} else {
 		/* Unknown public key algorithm */
 		return -ENOPKG;
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index b37cae914987..5ab5b4e5f1b4 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -257,6 +257,15 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 	case OID_gost2012Signature512:
 		ctx->cert->sig->hash_algo = "streebog512";
 		goto ecrdsa;
+	case OID_id_ml_dsa_44:
+		ctx->cert->sig->pkey_algo = "mldsa44";
+		goto ml_dsa;
+	case OID_id_ml_dsa_65:
+		ctx->cert->sig->pkey_algo = "mldsa65";
+		goto ml_dsa;
+	case OID_id_ml_dsa_87:
+		ctx->cert->sig->pkey_algo = "mldsa87";
+		goto ml_dsa;
 	}
 
 rsa_pkcs1:
@@ -274,6 +283,12 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
 	ctx->cert->sig->encoding = "x962";
 	ctx->sig_algo = ctx->last_oid;
 	return 0;
+ml_dsa:
+	ctx->cert->sig->algo_does_hash = true;
+	ctx->cert->sig->hash_algo = ctx->cert->sig->pkey_algo;
+	ctx->cert->sig->encoding = "raw";
+	ctx->sig_algo = ctx->last_oid;
+	return 0;
 }
 
 /*
@@ -524,6 +539,15 @@ int x509_extract_key_data(void *context, size_t hdrlen,
 			return -ENOPKG;
 		}
 		break;
+	case OID_id_ml_dsa_44:
+		ctx->cert->pub->pkey_algo = "mldsa44";
+		break;
+	case OID_id_ml_dsa_65:
+		ctx->cert->pub->pkey_algo = "mldsa65";
+		break;
+	case OID_id_ml_dsa_87:
+		ctx->cert->pub->pkey_algo = "mldsa87";
+		break;
 	default:
 		return -ENOPKG;
 	}
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 6de479ebbe5d..30821a6a4f72 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -145,6 +145,11 @@ enum OID {
 	OID_id_rsassa_pkcs1_v1_5_with_sha3_384, /* 2.16.840.1.101.3.4.3.15 */
 	OID_id_rsassa_pkcs1_v1_5_with_sha3_512, /* 2.16.840.1.101.3.4.3.16 */
 
+	/* NIST FIPS-204 ML-DSA (Dilithium) */
+	OID_id_ml_dsa_44,			/* 2.16.840.1.101.3.4.3.17 */
+	OID_id_ml_dsa_65,			/* 2.16.840.1.101.3.4.3.18 */
+	OID_id_ml_dsa_87,			/* 2.16.840.1.101.3.4.3.19 */
+
 	OID__NR
 };
 


^ permalink raw reply related

* [PATCH v11 2/8] pkcs7: Allow the signing algo to calculate the digest itself
From: David Howells @ 2026-01-05 15:21 UTC (permalink / raw)
  To: Lukas Wunner, Ignat Korchagin
  Cc: David Howells, Jarkko Sakkinen, Herbert Xu, Eric Biggers,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Jason A . Donenfeld, Ard Biesheuvel, Stephan Mueller,
	linux-crypto, keyrings, linux-modules, linux-kernel
In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com>

The ML-DSA public key algorithm really wants to calculate the message
digest itself, rather than having the digest precalculated and fed to it
separately as RSA does[*].  The kernel's PKCS#7 parser, however, is
designed around the latter approach.

  [*] ML-DSA does allow for an "external mu", but CMS doesn't yet have that
  standardised.

Fix this by noting in the public_key_signature struct when the signing
algorithm is going to want this and then, rather than doing the digest of
the authenticatedAttributes ourselves and overwriting the sig->digest with
that, replace sig->digest with a copy of the contents of the
authenticatedAttributes section and adjust the digest length to match.

This will then be fed to the public key algorithm as normal which can do
what it wants with the data.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: keyrings@vger.kernel.org
cc: linux-crypto@vger.kernel.org
---
 crypto/asymmetric_keys/pkcs7_parser.c |  4 +--
 crypto/asymmetric_keys/pkcs7_verify.c | 48 ++++++++++++++++++---------
 include/crypto/public_key.h           |  1 +
 3 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 423d13c47545..3cdbab3b9f50 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -599,8 +599,8 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
 	}
 
 	/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
-	sinfo->authattrs = value - (hdrlen - 1);
-	sinfo->authattrs_len = vlen + (hdrlen - 1);
+	sinfo->authattrs = value - hdrlen;
+	sinfo->authattrs_len = vlen + hdrlen;
 	return 0;
 }
 
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 6d6475e3a9bf..0f9f515b784d 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -70,8 +70,6 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
 	 * digest we just calculated.
 	 */
 	if (sinfo->authattrs) {
-		u8 tag;
-
 		if (!sinfo->msgdigest) {
 			pr_warn("Sig %u: No messageDigest\n", sinfo->index);
 			ret = -EKEYREJECTED;
@@ -97,20 +95,40 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
 		 * as the contents of the digest instead.  Note that we need to
 		 * convert the attributes from a CONT.0 into a SET before we
 		 * hash it.
+		 *
+		 * However, for certain algorithms, such as ML-DSA, the digest
+		 * is integrated into the signing algorithm.  In such a case,
+		 * we copy the authattrs, modifying the tag type, and set that
+		 * as the digest.
 		 */
-		memset(sig->digest, 0, sig->digest_size);
-
-		ret = crypto_shash_init(desc);
-		if (ret < 0)
-			goto error;
-		tag = ASN1_CONS_BIT | ASN1_SET;
-		ret = crypto_shash_update(desc, &tag, 1);
-		if (ret < 0)
-			goto error;
-		ret = crypto_shash_finup(desc, sinfo->authattrs,
-					 sinfo->authattrs_len, sig->digest);
-		if (ret < 0)
-			goto error;
+		if (sig->algo_does_hash) {
+			kfree(sig->digest);
+
+			ret = -ENOMEM;
+			sig->digest = kmalloc(umax(sinfo->authattrs_len, sig->digest_size),
+					      GFP_KERNEL);
+			if (!sig->digest)
+				goto error_no_desc;
+
+			sig->digest_size = sinfo->authattrs_len;
+			memcpy(sig->digest, sinfo->authattrs, sinfo->authattrs_len);
+			((u8 *)sig->digest)[0] = ASN1_CONS_BIT | ASN1_SET;
+			ret = 0;
+		} else {
+			u8 tag = ASN1_CONS_BIT | ASN1_SET;
+
+			ret = crypto_shash_init(desc);
+			if (ret < 0)
+				goto error;
+			ret = crypto_shash_update(desc, &tag, 1);
+			if (ret < 0)
+				goto error;
+			ret = crypto_shash_finup(desc, sinfo->authattrs + 1,
+						 sinfo->authattrs_len - 1,
+						 sig->digest);
+			if (ret < 0)
+				goto error;
+		}
 		pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
 	}
 
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 81098e00c08f..e4ec8003a3a4 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -46,6 +46,7 @@ struct public_key_signature {
 	u8 *digest;
 	u32 s_size;		/* Number of bytes in signature */
 	u32 digest_size;	/* Number of bytes in digest */
+	bool algo_does_hash;	/* Public key algo does its own hashing */
 	const char *pkey_algo;
 	const char *hash_algo;
 	const char *encoding;


^ permalink raw reply related

* [PATCH v11 1/8] crypto: Add ML-DSA crypto_sig support
From: David Howells @ 2026-01-05 15:21 UTC (permalink / raw)
  To: Lukas Wunner, Ignat Korchagin
  Cc: David Howells, Jarkko Sakkinen, Herbert Xu, Eric Biggers,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Jason A . Donenfeld, Ard Biesheuvel, Stephan Mueller,
	linux-crypto, keyrings, linux-modules, linux-kernel
In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com>

Add verify-only public key crypto support for ML-DSA so that the
X.509/PKCS#7 signature verification code, as used by module signing,
amongst other things, can make use of it through the common crypto_sig API.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: keyrings@vger.kernel.org
cc: linux-crypto@vger.kernel.org
---
 crypto/Kconfig  |  10 +++
 crypto/Makefile |   2 +
 crypto/mldsa.c  | 201 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 213 insertions(+)
 create mode 100644 crypto/mldsa.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 12a87f7cf150..8dd5c6660c5a 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -344,6 +344,16 @@ config CRYPTO_ECRDSA
 	  One of the Russian cryptographic standard algorithms (called GOST
 	  algorithms). Only signature verification is implemented.
 
+config CRYPTO_MLDSA
+	tristate "ML-DSA (Module-Lattice-Based Digital Signature Algorithm)"
+	select CRYPTO_SIG
+	select CRYPTO_LIB_MLDSA
+	select CRYPTO_LIB_SHA3
+	help
+	  ML-DSA (Module-Lattice-Based Digital Signature Algorithm) (FIPS-204).
+
+	  Only signature verification is implemented.
+
 endmenu
 
 menu "Block ciphers"
diff --git a/crypto/Makefile b/crypto/Makefile
index 23d3db7be425..267d5403045b 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -60,6 +60,8 @@ ecdsa_generic-y += ecdsa-p1363.o
 ecdsa_generic-y += ecdsasignature.asn1.o
 obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o
 
+obj-$(CONFIG_CRYPTO_MLDSA) += mldsa.o
+
 crypto_acompress-y := acompress.o
 crypto_acompress-y += scompress.o
 obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o
diff --git a/crypto/mldsa.c b/crypto/mldsa.c
new file mode 100644
index 000000000000..2146c774b5ca
--- /dev/null
+++ b/crypto/mldsa.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * crypto_sig wrapper around ML-DSA library.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <crypto/internal/sig.h>
+#include <crypto/mldsa.h>
+
+struct crypto_mldsa_ctx {
+	u8 pk[MAX(MAX(MLDSA44_PUBLIC_KEY_SIZE,
+		      MLDSA65_PUBLIC_KEY_SIZE),
+		  MLDSA87_PUBLIC_KEY_SIZE)];
+	unsigned int pk_len;
+	enum mldsa_alg strength;
+	u8 key_set;
+};
+
+static int crypto_mldsa_sign(struct crypto_sig *tfm,
+			     const void *msg, unsigned int msg_len,
+			     void *sig, unsigned int sig_len)
+{
+	return -EOPNOTSUPP;
+}
+
+static int crypto_mldsa_verify(struct crypto_sig *tfm,
+			       const void *sig, unsigned int sig_len,
+			       const void *msg, unsigned int msg_len)
+{
+	const struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	if (unlikely(!ctx->key_set))
+		return -EINVAL;
+
+	return mldsa_verify(ctx->strength, sig, sig_len, msg, msg_len,
+			    ctx->pk, ctx->pk_len);
+}
+
+static unsigned int crypto_mldsa_key_size(struct crypto_sig *tfm)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	switch (ctx->strength) {
+	case MLDSA44:
+		return MLDSA44_PUBLIC_KEY_SIZE;
+	case MLDSA65:
+		return MLDSA65_PUBLIC_KEY_SIZE;
+	case MLDSA87:
+		return MLDSA87_PUBLIC_KEY_SIZE;
+	default:
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+}
+
+static int crypto_mldsa_set_pub_key(struct crypto_sig *tfm,
+				    const void *key, unsigned int keylen)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+	unsigned int expected_len = crypto_mldsa_key_size(tfm);
+
+	if (keylen != expected_len)
+		return -EINVAL;
+
+	ctx->pk_len = keylen;
+	memcpy(ctx->pk, key, keylen);
+	ctx->key_set = true;
+	return 0;
+}
+
+static int crypto_mldsa_set_priv_key(struct crypto_sig *tfm,
+				     const void *key, unsigned int keylen)
+{
+	return -EOPNOTSUPP;
+}
+
+static unsigned int crypto_mldsa_max_size(struct crypto_sig *tfm)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	switch (ctx->strength) {
+	case MLDSA44:
+		return MLDSA44_SIGNATURE_SIZE;
+	case MLDSA65:
+		return MLDSA65_SIGNATURE_SIZE;
+	case MLDSA87:
+		return MLDSA87_SIGNATURE_SIZE;
+	default:
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+}
+
+static int crypto_mldsa44_alg_init(struct crypto_sig *tfm)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	ctx->strength = MLDSA44;
+	ctx->key_set = false;
+	return 0;
+}
+
+static int crypto_mldsa65_alg_init(struct crypto_sig *tfm)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	ctx->strength = MLDSA65;
+	ctx->key_set = false;
+	return 0;
+}
+
+static int crypto_mldsa87_alg_init(struct crypto_sig *tfm)
+{
+	struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm);
+
+	ctx->strength = MLDSA87;
+	ctx->key_set = false;
+	return 0;
+}
+
+static void crypto_mldsa_alg_exit(struct crypto_sig *tfm)
+{
+}
+
+static struct sig_alg crypto_mldsa_algs[] = {
+	{
+		.sign			= crypto_mldsa_sign,
+		.verify			= crypto_mldsa_verify,
+		.set_pub_key		= crypto_mldsa_set_pub_key,
+		.set_priv_key		= crypto_mldsa_set_priv_key,
+		.key_size		= crypto_mldsa_key_size,
+		.max_size		= crypto_mldsa_max_size,
+		.init			= crypto_mldsa44_alg_init,
+		.exit			= crypto_mldsa_alg_exit,
+		.base.cra_name		= "mldsa44",
+		.base.cra_driver_name	= "mldsa44-lib",
+		.base.cra_ctxsize	= sizeof(struct crypto_mldsa_ctx),
+		.base.cra_module	= THIS_MODULE,
+		.base.cra_priority	= 5000,
+	}, {
+		.sign			= crypto_mldsa_sign,
+		.verify			= crypto_mldsa_verify,
+		.set_pub_key		= crypto_mldsa_set_pub_key,
+		.set_priv_key		= crypto_mldsa_set_priv_key,
+		.key_size		= crypto_mldsa_key_size,
+		.max_size		= crypto_mldsa_max_size,
+		.init			= crypto_mldsa65_alg_init,
+		.exit			= crypto_mldsa_alg_exit,
+		.base.cra_name		= "mldsa65",
+		.base.cra_driver_name	= "mldsa65-lib",
+		.base.cra_ctxsize	= sizeof(struct crypto_mldsa_ctx),
+		.base.cra_module	= THIS_MODULE,
+		.base.cra_priority	= 5000,
+	}, {
+		.sign			= crypto_mldsa_sign,
+		.verify			= crypto_mldsa_verify,
+		.set_pub_key		= crypto_mldsa_set_pub_key,
+		.set_priv_key		= crypto_mldsa_set_priv_key,
+		.key_size		= crypto_mldsa_key_size,
+		.max_size		= crypto_mldsa_max_size,
+		.init			= crypto_mldsa87_alg_init,
+		.exit			= crypto_mldsa_alg_exit,
+		.base.cra_name		= "mldsa87",
+		.base.cra_driver_name	= "mldsa87-lib",
+		.base.cra_ctxsize	= sizeof(struct crypto_mldsa_ctx),
+		.base.cra_module	= THIS_MODULE,
+		.base.cra_priority	= 5000,
+	},
+};
+
+static int __init mldsa_init(void)
+{
+	int ret, i;
+
+	for (i = 0; i < ARRAY_SIZE(crypto_mldsa_algs); i++) {
+		ret = crypto_register_sig(&crypto_mldsa_algs[i]);
+		if (ret < 0)
+			goto error;
+	}
+	return 0;
+
+error:
+	pr_err("Failed to register (%d)\n", ret);
+	for (i--; i >= 0; i--)
+		crypto_unregister_sig(&crypto_mldsa_algs[i]);
+	return ret;
+}
+module_init(mldsa_init);
+
+static void mldsa_exit(void)
+{
+	for (int i = 0; i < ARRAY_SIZE(crypto_mldsa_algs); i++)
+		crypto_unregister_sig(&crypto_mldsa_algs[i]);
+}
+module_exit(mldsa_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Crypto API support for ML-DSA signature verification");
+MODULE_ALIAS_CRYPTO("mldsa44");
+MODULE_ALIAS_CRYPTO("mldsa65");
+MODULE_ALIAS_CRYPTO("mldsa87");


^ permalink raw reply related

* [PATCH v11 0/8] x509, pkcs7, crypto: Add ML-DSA and RSASSA-PSS signing
From: David Howells @ 2026-01-05 15:21 UTC (permalink / raw)
  To: Lukas Wunner, Ignat Korchagin
  Cc: David Howells, Jarkko Sakkinen, Herbert Xu, Eric Biggers,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Jason A . Donenfeld, Ard Biesheuvel, Stephan Mueller,
	linux-crypto, keyrings, linux-modules, linux-kernel

Hi Lukas, Ignat,

[Note this is based on Eric Bigger's libcrypto-next branch].

These patches add ML-DSA module signing and RSASSA-PSS module signing.  The
first half of the set adds ML-DSA signing:

 (1) Add a crypto_sig interface for ML-DSA, verification only.

 (2) Modify PKCS#7 support to allow kernel module signatures to carry
     authenticatedAttributes as OpenSSL refuses to let them be opted out of
     for ML-DSA (CMS_NOATTR).  This adds an extra digest calculation to the
     process.

     Modify PKCS#7 to pass the authenticatedAttributes directly to the
     ML-DSA algorithm rather than passing over a digest as is done with RSA
     as ML-DSA wants to do its own hashing and will add other stuff into
     the hash.  We could use hashML-DSA or an external mu instead, but they
     aren't standardised for CMS yet.

 (3) Add support to the PKCS#7 and X.509 parsers for ML-DSA.

 (4) Modify sign-file to handle OpenSSL not permitting CMS_NOATTR with
     ML-DSA and add ML-DSA to the choice of algorithm with which to sign
     modules.  Note that this might need some more 'select' lines in the
     Kconfig to select the lib stuff as well.

This is based on Eric's libcrypto-next branch which has the core
implementation of ML-DSA.

The second half of the set adds RSASSA-PSS signing:

 (5) Add an info string parameter to the internal signature verification
     routines where that does not already exist.  This is necessary to pass
     extra parameters and is already supported in the KEYCTL_PKEY_VERIFY
     keyctl.

     Both X.509 and PKCS#7 provide for these parameters to be supplied, but
     it is tricky to pass the parameters in a blob with the signature or
     key data as there are checks on these sizes that are then violated;
     further, the way the parameters are laid out in the ASN.1 doesn't lend
     itself easily to simply extracting out a larger blob.

 (6) Add RSASSA-PSS support to the RSA driver in crypto/.  This parses the
     info string to get the verification parameters.

 (7) Add support to the PKCS#7 and X.509 parsers for RSASSA-PSS.

 (8) Modify sign-file to pass the extra parameters necessary to be able
     generate RSASSA-PSS.  For the moment, only select MGF1 with the same
     hash algorithm as for the data for the mask function.  Add RSASSA-PSS
     to the choice of algorithm with which to sign modules.

Note that I do still need to add some FIPS tests for both ML-DSA and
RSASSA-PSS in the form of X.509 certs, data and detached PKCS#7 signatures.
I'm not sure if I can use FIPS-standard tests for that.

The patches can also be found here:

	https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-pqc

David

Changes
=======
ver #11)
 - Rebased on Eric's libcrypto-next branch.
 - Added RSASSA-PSS support patches.

ver #10)
 - Replaced the Leancrypto ML-DSA implementation with Eric's.
 - Fixed Eric's implementation to have MODULE_* info.
 - Added a patch to drive Eric's ML-DSA implementation from crypto_sig.
 - Removed SHAKE256 from the list of available module hash algorithms.
 - Changed a some more ML_DSA to MLDSA in config symbols.

ver #9)
 - ML-DSA changes:
   - Separate output into four modules (1 common, 3 strength-specific).
     - Solves Kconfig issue with needing to select at least one strength.
   - Separate the strength-specific crypto-lib APIs.
     - This is now generated by preprocessor-templating.
   - Remove the multiplexor code.
   - Multiplex the crypto-lib APIs by C type.
 - Fix the PKCS#7/X.509 code to have the correct algo names.

ver #8)
 - Moved the ML-DSA code to lib/crypto/mldsa/.
 - Renamed some bits from ml-dsa to mldsa.
 - Created a simplified API and placed that in include/crypto/mldsa.h.
 - Made the testing code use the simplified API.
 - Fixed a warning about implicitly casting between uint16_t and __le16.

ver #7)
 - Rebased on Eric's tree as that now contains all the necessary SHA-3
   infrastructure and drop the SHA-3 patches from here.
 - Added a minimal patch to provide shake256 support for crypto_sig.
 - Got rid of the memory allocation wrappers.
 - Removed the ML-DSA keypair generation code and the signing code, leaving
   only the signature verification code.
 - Removed the secret key handling code.
 - Removed the secret keys from the kunit tests and the signing testing.
 - Removed some unused bits from the ML-DSA code.
 - Downgraded the kdoc comments to ordinary comments, but keep the markup
   for easier comparison to Leancrypto.

ver #6)
 - Added a patch to make the jitterentropy RNG use lib/sha3.
 - Added back the crypto/sha3_generic changes.
 - Added ML-DSA implementation (still needs more cleanup).
 - Added kunit test for ML-DSA.
 - Modified PKCS#7 to accommodate ML-DSA.
 - Modified PKCS#7 and X.509 to allow ML-DSA to be specified and used.
 - Modified sign-file to not use CMS_NOATTR with ML-DSA.
 - Allowed SHA3 and SHAKE* algorithms for module signing default.
 - Allowed ML-DSA-{44,65,87} to be selected as the module signing default.

ver #5)
 - Fix gen-hash-testvecs.py to correctly handle algo names that contain a
   dash.
 - Fix gen-hash-testvecs.py to not generate HMAC for SHA3-* or SHAKE* as
   these don't currently have HMAC variants implemented.
 - Fix algo names to be correct.
 - Fix kunit module description as it now tests all SHA3 variants.

ver #4)
 - Fix a couple of arm64 build problems.
 - Doc fixes:
   - Fix the description of the algorithm to be closer to the NIST spec's
     terminology.
   - Don't talk of finialising the context for XOFs.
   - Don't say "Return: None".
   - Declare the "Context" to be "Any context" and make no mention of the
     fact that it might use the FPU.
   - Change "initialise" to "initialize".
   - Don't warn that the context is relatively large for stack use.
 - Use size_t for size parameters/variables.
 - Make the module_exit unconditional.
 - Dropped the crypto/ dir-affecting patches for the moment.

ver #3)
 - Renamed conflicting arm64 functions.
 - Made a separate wrapper API for each algorithm in the family.
 - Removed sha3_init(), sha3_reinit() and sha3_final().
 - Removed sha3_ctx::digest_size.
 - Renamed sha3_ctx::partial to sha3_ctx::absorb_offset.
 - Refer to the output of SHAKE* as "output" not "digest".
 - Moved the Iota transform into the one-round function.
 - Made sha3_update() warn if called after sha3_squeeze().
 - Simplified the module-load test to not do update after squeeze.
 - Added Return: and Context: kdoc statements and expanded the kdoc
   headers.
 - Added an API description document.
 - Overhauled the kunit tests.
   - Only have one kunit test.
   - Only call the general hash tester on one algo.
   - Add separate simple cursory checks for the other algos.
   - Add resqueezing tests.
   - Add some NIST example tests.
 - Changed crypto/sha3_generic to use this
 - Added SHAKE128/256 to crypto/sha3_generic and crypto/testmgr
 - Folded struct sha3_state into struct sha3_ctx.

ver #2)
  - Simplify the endianness handling.
  - Rename sha3_final() to sha3_squeeze() and don't clear the context at the
    end as it's permitted to continue calling sha3_final() to extract
    continuations of the digest (needed by ML-DSA).
  - Don't reapply the end marker to the hash state in continuation
    sha3_squeeze() unless sha3_update() gets called again (needed by
    ML-DSA).
  - Give sha3_squeeze() the amount of digest to produce as a parameter
    rather than using ctx->digest_size and don't return the amount digested.
  - Reimplement sha3_final() as a wrapper around sha3_squeeze() that
    extracts ctx->digest_size amount of digest and then zeroes out the
    context.  The latter is necessary to avoid upsetting
    hash-test-template.h.
  - Provide a sha3_reinit() function to clear the state, but to leave the
    parameters that indicate the hash properties unaffected, allowing for
    reuse.
  - Provide a sha3_set_digestsize() function to change the size of the
    digest to be extracted by sha3_final().  sha3_squeeze() takes a
    parameter for this instead.
  - Don't pass the digest size as a parameter to shake128/256_init() but
    rather default to 128/256 bits as per the function name.
  - Provide a sha3_clear() function to zero out the context.

David Howells (8):
  crypto: Add ML-DSA crypto_sig support
  pkcs7: Allow the signing algo to calculate the digest itself
  pkcs7, x509: Add ML-DSA support
  modsign: Enable ML-DSA module signing
  crypto: Add supplementary info param to asymmetric key signature
    verification
  crypto: Add RSASSA-PSS support
  pkcs7, x509: Add RSASSA-PSS support
  modsign: Enable RSASSA-PSS module signing

 Documentation/admin-guide/module-signing.rst |  15 +-
 certs/Kconfig                                |  27 ++
 certs/Makefile                               |   4 +
 crypto/Kconfig                               |  10 +
 crypto/Makefile                              |   3 +
 crypto/asymmetric_keys/Makefile              |  12 +-
 crypto/asymmetric_keys/asymmetric_type.c     |   1 +
 crypto/asymmetric_keys/mgf1_params.asn1      |  12 +
 crypto/asymmetric_keys/pkcs7.asn1            |   2 +-
 crypto/asymmetric_keys/pkcs7_parser.c        | 114 +++---
 crypto/asymmetric_keys/pkcs7_verify.c        |  52 ++-
 crypto/asymmetric_keys/public_key.c          |  19 +-
 crypto/asymmetric_keys/rsassa_params.asn1    |  25 ++
 crypto/asymmetric_keys/rsassa_parser.c       | 233 +++++++++++
 crypto/asymmetric_keys/rsassa_parser.h       |  25 ++
 crypto/asymmetric_keys/signature.c           |   1 +
 crypto/asymmetric_keys/x509.asn1             |   2 +-
 crypto/asymmetric_keys/x509_cert_parser.c    | 120 ++++--
 crypto/asymmetric_keys/x509_parser.h         |  33 +-
 crypto/asymmetric_keys/x509_public_key.c     |  28 +-
 crypto/ecdsa-p1363.c                         |   5 +-
 crypto/ecdsa-x962.c                          |   5 +-
 crypto/ecdsa.c                               |   3 +-
 crypto/ecrdsa.c                              |   3 +-
 crypto/mldsa.c                               | 202 ++++++++++
 crypto/rsa.c                                 |   8 +
 crypto/rsassa-pkcs1.c                        |   3 +-
 crypto/rsassa-pss.c                          | 397 +++++++++++++++++++
 crypto/sig.c                                 |   3 +-
 include/crypto/internal/rsa.h                |   2 +
 include/crypto/public_key.h                  |   2 +
 include/crypto/sig.h                         |   9 +-
 include/linux/oid_registry.h                 |   7 +
 scripts/sign-file.c                          |  63 ++-
 34 files changed, 1307 insertions(+), 143 deletions(-)
 create mode 100644 crypto/asymmetric_keys/mgf1_params.asn1
 create mode 100644 crypto/asymmetric_keys/rsassa_params.asn1
 create mode 100644 crypto/asymmetric_keys/rsassa_parser.c
 create mode 100644 crypto/asymmetric_keys/rsassa_parser.h
 create mode 100644 crypto/mldsa.c
 create mode 100644 crypto/rsassa-pss.c


^ permalink raw reply

* Re: [PATCH] KEYS: replace -EEXIST with -EBUSY
From: David Howells @ 2026-01-05  9:57 UTC (permalink / raw)
  To: Daniel Gomez
  Cc: dhowells, Lukas Wunner, Ignat Korchagin, Herbert Xu,
	David S. Miller, Jarkko Sakkinen, Paul Moore, James Morris,
	Serge E. Hallyn, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
	Aaron Tomlin, Lucas De Marchi, keyrings, linux-crypto,
	linux-modules, linux-kernel, linux-security-module, Daniel Gomez
In-Reply-To: <20251220-dev-module-init-eexists-keyring-v1-1-a2f23248c300@samsung.com>

Daniel Gomez <da.gomez@kernel.org> wrote:

> From: Daniel Gomez <da.gomez@samsung.com>
> 
> The -EEXIST error code is reserved by the module loading infrastructure
> to indicate that a module is already loaded.

EEXIST means a file exists when you're trying to create it.  Granted we abuse
that somewhat rather than add ever more error codes, but you cannot reserve it
for indicating that a module exists.

> When a module's init
> function returns -EEXIST, userspace tools like kmod interpret this as
> "module already loaded" and treat the operation as successful, returning
> 0 to the user even though the module initialization actually failed.
> 
> This follows the precedent set by commit 54416fd76770 ("netfilter:
> conntrack: helper: Replace -EEXIST by -EBUSY") which fixed the same
> issue in nf_conntrack_helper_register().
> 
> Affected modules:
>   * pkcs8_key_parser x509_key_parser asymmetric_keys dns_resolver
>   * nvme_keyring pkcs7_test_key rxrpc turris_signing_key
> 
> Signed-off-by: Daniel Gomez <da.gomez@samsung.com>

Please don't.  Userspace can always check /proc/modules (assuming procfs is
enabled, I suppose).

David


^ permalink raw reply

* Re: [PATCH] KEYS: replace -EEXIST with -EBUSY
From: Lukas Wunner @ 2026-01-05  9:33 UTC (permalink / raw)
  To: Daniel Gomez
  Cc: David Howells, Ignat Korchagin, Herbert Xu, David S. Miller,
	Jarkko Sakkinen, Paul Moore, James Morris, Serge E. Hallyn,
	Luis Chamberlain, Petr Pavlu, Sami Tolvanen, Aaron Tomlin,
	Lucas De Marchi, keyrings, linux-crypto, linux-modules,
	linux-kernel, linux-security-module, Daniel Gomez
In-Reply-To: <20251220-dev-module-init-eexists-keyring-v1-1-a2f23248c300@samsung.com>

On Sat, Dec 20, 2025 at 04:50:31AM +0100, Daniel Gomez wrote:
> The -EEXIST error code is reserved by the module loading infrastructure
> to indicate that a module is already loaded. When a module's init
> function returns -EEXIST, userspace tools like kmod interpret this as
> "module already loaded" and treat the operation as successful, returning
> 0 to the user even though the module initialization actually failed.
> 
> This follows the precedent set by commit 54416fd76770 ("netfilter:
> conntrack: helper: Replace -EEXIST by -EBUSY") which fixed the same
> issue in nf_conntrack_helper_register().
> 
> Affected modules:
>   * pkcs8_key_parser x509_key_parser asymmetric_keys dns_resolver
>   * nvme_keyring pkcs7_test_key rxrpc turris_signing_key

For the record, GregKH summarily rejected this approach:

https://lore.kernel.org/r/2025122212-fiction-setback-ede5@gregkh/

Thanks,

Lukas

^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Jani Nikula @ 2026-01-05  9:29 UTC (permalink / raw)
  To: Yury Norov, Andy Shevchenko
  Cc: Joel Fernandes, Steven Rostedt, Andrew Morton, Masami Hiramatsu,
	Mathieu Desnoyers, Christophe Leroy, Randy Dunlap, Ingo Molnar,
	Joonas Lahtinen, David Laight, Petr Pavlu, Andi Shyti,
	Rodrigo Vivi, Tvrtko Ursulin, Daniel Gomez, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, linux-kernel, intel-gfx,
	dri-devel, linux-modules, linux-trace-kernel
In-Reply-To: <aVkmQ4EGIQgAddZQ@yury>

On Sat, 03 Jan 2026, Yury Norov <yury.norov@gmail.com> wrote:
> On Sat, Jan 03, 2026 at 02:57:58PM +0200, Andy Shevchenko wrote:
>> On Fri, Jan 02, 2026 at 07:50:59PM -0500, Joel Fernandes wrote:
>> > On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:
>> 
>> ...
>> 
>> > I use trace_printk() all the time for kernel, particularly RCU development.
>> > One of the key usecases I have is dumping traces on panic (with panic on warn
>> > and stop tracing on warn enabled). This is extremely useful since I can add
>> > custom tracing and dump traces when rare conditions occur. I fixed several
>> > bugs with this technique.
>> > 
>> > I also recommend keeping it convenient to use.
>> 
>> Okay, you know C, please share your opinion what header is the best to hold the
>> trace_printk.h to be included.
>
> What if we include it on Makefile level, similarly to how W=1 works?
>
>         make D=1 // trace_printk() is available
>         make D=0 // trace_printk() is not available
>         make     // trace_printk() is not available
>
> Where D stands for debugging.
>
> D=1 may be a default setting if you prefer, but the most important is
> that every compilation unit will have an access to debugging without
> polluting core headers.

You do realize this means recompiling everything when adding D=1 for
debugging?

BR,
Jani.

-- 
Jani Nikula, Intel

^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Andy Shevchenko @ 2026-01-04  0:20 UTC (permalink / raw)
  To: Joel Fernandes
  Cc: Steven Rostedt, Andrew Morton, Yury Norov, Masami Hiramatsu,
	Mathieu Desnoyers, Christophe Leroy, Randy Dunlap, Ingo Molnar,
	Jani Nikula, Joonas Lahtinen, David Laight, Petr Pavlu,
	Andi Shyti, Vivi Rodrigo, Tvrtko Ursulin, Daniel Gomez,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	linux-kernel@vger.kernel.org, intel-gfx@lists.freedesktop.org,
	dri-devel@lists.freedesktop.org, linux-modules@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org
In-Reply-To: <937926D0-00DC-499B-9FD8-D921C903882D@nvidia.com>

On Sat, Jan 03, 2026 at 03:36:44PM +0000, Joel Fernandes wrote:
> > On Jan 3, 2026, at 7:58 AM, Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:
> > On Fri, Jan 02, 2026 at 07:50:59PM -0500, Joel Fernandes wrote:
> >> On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:

...

> >> I use trace_printk() all the time for kernel, particularly RCU development.
> >> One of the key usecases I have is dumping traces on panic (with panic on warn
> >> and stop tracing on warn enabled). This is extremely useful since I can add
> >> custom tracing and dump traces when rare conditions occur. I fixed several
> >> bugs with this technique.
> >> 
> >> I also recommend keeping it convenient to use.
> > 
> > Okay, you know C, please share your opinion what header is the best to hold the
> > trace_printk.h to be included.
> 
> I do not think it is necessary to move it.

I'm not talking about move, I'm talking about the C 101 thingy. Any custom API
should be included before use, otherwise compiler won't see it. Which header do
you want to include to have this API being provided? Note, it's really bad
situation right now with the header to be included implicitly via non-obvious
or obscure path. The discussion moved as far as I see it towards the finding a
good place for the trace_printk.h.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Joel Fernandes @ 2026-01-03 15:36 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Steven Rostedt, Andrew Morton, Yury Norov, Masami Hiramatsu,
	Mathieu Desnoyers, Christophe Leroy, Randy Dunlap, Ingo Molnar,
	Jani Nikula, Joonas Lahtinen, David Laight, Petr Pavlu,
	Andi Shyti, Vivi Rodrigo, Tvrtko Ursulin, Daniel Gomez,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	linux-kernel@vger.kernel.org, intel-gfx@lists.freedesktop.org,
	dri-devel@lists.freedesktop.org, linux-modules@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org
In-Reply-To: <aVkSVk2L6VH9MYGz@smile.fi.intel.com>



> On Jan 3, 2026, at 7:58 AM, Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:
> 
> On Fri, Jan 02, 2026 at 07:50:59PM -0500, Joel Fernandes wrote:
>> On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:
> 
> ...
> 
>> I use trace_printk() all the time for kernel, particularly RCU development.
>> One of the key usecases I have is dumping traces on panic (with panic on warn
>> and stop tracing on warn enabled). This is extremely useful since I can add
>> custom tracing and dump traces when rare conditions occur. I fixed several
>> bugs with this technique.
>> 
>> I also recommend keeping it convenient to use.
> 
> Okay, you know C, please share your opinion what header is the best to hold the
> trace_printk.h to be included.

I do not think it is necessary to move it.

 - Joel



> 
> --
> With Best Regards,
> Andy Shevchenko
> 
> 

^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Yury Norov @ 2026-01-03 14:22 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Joel Fernandes, Steven Rostedt, Andrew Morton, Masami Hiramatsu,
	Mathieu Desnoyers, Christophe Leroy, Randy Dunlap, Ingo Molnar,
	Jani Nikula, Joonas Lahtinen, David Laight, Petr Pavlu,
	Andi Shyti, Rodrigo Vivi, Tvrtko Ursulin, Daniel Gomez,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	linux-kernel, intel-gfx, dri-devel, linux-modules,
	linux-trace-kernel
In-Reply-To: <aVkSVk2L6VH9MYGz@smile.fi.intel.com>

On Sat, Jan 03, 2026 at 02:57:58PM +0200, Andy Shevchenko wrote:
> On Fri, Jan 02, 2026 at 07:50:59PM -0500, Joel Fernandes wrote:
> > On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:
> 
> ...
> 
> > I use trace_printk() all the time for kernel, particularly RCU development.
> > One of the key usecases I have is dumping traces on panic (with panic on warn
> > and stop tracing on warn enabled). This is extremely useful since I can add
> > custom tracing and dump traces when rare conditions occur. I fixed several
> > bugs with this technique.
> > 
> > I also recommend keeping it convenient to use.
> 
> Okay, you know C, please share your opinion what header is the best to hold the
> trace_printk.h to be included.

What if we include it on Makefile level, similarly to how W=1 works?

        make D=1 // trace_printk() is available
        make D=0 // trace_printk() is not available
        make     // trace_printk() is not available

Where D stands for debugging.

D=1 may be a default setting if you prefer, but the most important is
that every compilation unit will have an access to debugging without
polluting core headers.

^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Andy Shevchenko @ 2026-01-03 12:57 UTC (permalink / raw)
  To: Joel Fernandes
  Cc: Steven Rostedt, Andrew Morton, Yury Norov (NVIDIA),
	Masami Hiramatsu, Mathieu Desnoyers, Christophe Leroy,
	Randy Dunlap, Ingo Molnar, Jani Nikula, Joonas Lahtinen,
	David Laight, Petr Pavlu, Andi Shyti, Rodrigo Vivi,
	Tvrtko Ursulin, Daniel Gomez, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, linux-kernel, intel-gfx,
	dri-devel, linux-modules, linux-trace-kernel
In-Reply-To: <20260103005059.GA11015@joelbox2>

On Fri, Jan 02, 2026 at 07:50:59PM -0500, Joel Fernandes wrote:
> On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:

...

> I use trace_printk() all the time for kernel, particularly RCU development.
> One of the key usecases I have is dumping traces on panic (with panic on warn
> and stop tracing on warn enabled). This is extremely useful since I can add
> custom tracing and dump traces when rare conditions occur. I fixed several
> bugs with this technique.
> 
> I also recommend keeping it convenient to use.

Okay, you know C, please share your opinion what header is the best to hold the
trace_printk.h to be included.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Joel Fernandes @ 2026-01-03  0:50 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Andrew Morton, Yury Norov (NVIDIA), Masami Hiramatsu,
	Mathieu Desnoyers, Andy Shevchenko, Christophe Leroy,
	Randy Dunlap, Ingo Molnar, Jani Nikula, Joonas Lahtinen,
	David Laight, Petr Pavlu, Andi Shyti, Rodrigo Vivi,
	Tvrtko Ursulin, Daniel Gomez, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, linux-kernel, intel-gfx,
	dri-devel, linux-modules, linux-trace-kernel
In-Reply-To: <20251229111748.3ba66311@gandalf.local.home>

On Mon, Dec 29, 2025 at 11:17:48AM -0500, Steven Rostedt wrote:
> On Sun, 28 Dec 2025 13:31:50 -0800
> Andrew Morton <akpm@linux-foundation.org> wrote:
> 
> > > trace_printk() should be as available to the kernel as printk() is.  
> > 
> > um, why?  trace_printk is used 1% as often as is printk.  Seems
> > reasonable to include a header file to access such a rarely-used(!) and
> > specialized thing?
> 
> It will waste a lot of kernel developers time. Go to conferences and talk
> with developers. trace_printk() is now one of the most common ways to debug
> your code. Having to add "#include <linux/trace_printk.h>" in every file
> that you use trace_printk() (and after your build fails because you forgot
> to include that file **WILL** slow down kernel debugging for hundreds of
> developers! It *is* used more than printk() for debugging today. Because
> it's fast and can be used in any context (NMI, interrupt handlers, etc).
> 
> But sure, if you want to save the few minutes that is added to "make
> allyesconfig" by sacrificing minutes of kernel developer's time. Go ahead
> and make this change.
> 
> I don't know how much you debug and develop today, but lots of people I
> talk to at conferences thank me for trace_printk() because it makes
> debugging their code so much easier.
> 
> The "shotgun" approach is very common. That is, you add:
> 
> 	trace_printk("%s:%d\n", __func__, __LINE__);
> 
> all over your code to find out where things are going wrong. With the
> persistent ring buffer, you can even extract that information after a
> crash and reboot.

I use trace_printk() all the time for kernel, particularly RCU development.
One of the key usecases I have is dumping traces on panic (with panic on warn
and stop tracing on warn enabled). This is extremely useful since I can add
custom tracing and dump traces when rare conditions occur. I fixed several
bugs with this technique.

I also recommend keeping it convenient to use.

thanks,

 - Joel


^ permalink raw reply

* [PATCH RFC 6/6] rust: WIP: Replace ModuleMetadata with THIS_MODULE
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

ModuleMetadata seems redudant after we have prober THIS_MODULE.
---
 drivers/gpu/nova-core/nova_core.rs    |  2 +-
 rust/kernel/auxiliary.rs              | 10 +++++++---
 rust/kernel/driver.rs                 | 10 ++++------
 rust/kernel/firmware.rs               |  2 +-
 rust/kernel/i2c.rs                    |  4 ++--
 rust/kernel/lib.rs                    |  8 ++------
 rust/kernel/pci.rs                    |  6 +++---
 rust/kernel/platform.rs               |  4 ++--
 rust/kernel/usb.rs                    |  6 +++---
 rust/macros/module.rs                 | 13 +++++++------
 samples/rust/rust_driver_auxiliary.rs |  6 +++---
 11 files changed, 35 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index b98a1c03f13d..fbfbcc9446c0 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -19,7 +19,7 @@
 mod util;
 mod vbios;
 
-pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
+pub(crate) const MODULE_NAME: &kernel::str::CStr = THIS_MODULE::name();
 
 kernel::module_pci_driver! {
     type: driver::NovaCore,
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 323074322505..bd064b677c05 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -27,10 +27,10 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::auxiliary_driver;
 
-    unsafe fn register<M: ThisModule>(adrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
+    unsafe fn register<M: ThisModule>(adrv: &Opaque<Self::RegType>) -> Result {
         // SAFETY: It's safe to set the fields of `struct auxiliary_driver` on initialization.
         unsafe {
-            (*adrv.get()).name = name.as_char_ptr();
+            (*adrv.get()).name = M::NAME.as_char_ptr();
             (*adrv.get()).probe = Some(Self::probe_callback);
             (*adrv.get()).remove = Some(Self::remove_callback);
             (*adrv.get()).id_table = T::ID_TABLE.as_ptr();
@@ -38,7 +38,11 @@ unsafe fn register<M: ThisModule>(adrv: &Opaque<Self::RegType>, name: &'static C
 
         // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::__auxiliary_driver_register(adrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
+            bindings::__auxiliary_driver_register(
+                adrv.get(),
+                M::OWNER.as_ptr(),
+                M::NAME.as_char_ptr(),
+            )
         })
     }
 
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 7c4ad24bb48a..5bb029075a57 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -118,7 +118,7 @@ pub unsafe trait RegistrationOps {
     ///
     /// On success, `reg` must remain pinned and valid until the matching call to
     /// [`RegistrationOps::unregister`].
-    unsafe fn register<M: ThisModule>(reg: &Opaque<Self::RegType>, name: &'static CStr) -> Result;
+    unsafe fn register<M: ThisModule>(reg: &Opaque<Self::RegType>) -> Result;
 
     /// Unregisters a driver previously registered with [`RegistrationOps::register`].
     ///
@@ -151,7 +151,7 @@ unsafe impl<T: RegistrationOps> Send for Registration<T> {}
 
 impl<T: RegistrationOps> Registration<T> {
     /// Creates a new instance of the registration object.
-    pub fn new<M: ThisModule>(name: &'static CStr) -> impl PinInit<Self, Error> {
+    pub fn new<M: ThisModule>() -> impl PinInit<Self, Error> {
         try_pin_init!(Self {
             reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| {
                 // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
@@ -162,7 +162,7 @@ pub fn new<M: ThisModule>(name: &'static CStr) -> impl PinInit<Self, Error> {
                 let drv = unsafe { &*(ptr as *const Opaque<T::RegType>) };
 
                 // SAFETY: `drv` is guaranteed to be pinned until `T::unregister`.
-                unsafe { T::register::<M>(drv, name) }
+                unsafe { T::register::<M>(drv) }
             }),
         })
     }
@@ -195,9 +195,7 @@ struct DriverModule {
         impl $crate::InPlaceModule for DriverModule {
             fn init<M: ::kernel::ThisModule>() -> impl ::pin_init::PinInit<Self, $crate::error::Error> {
                 $crate::try_pin_init!(Self {
-                    _driver <- $crate::driver::Registration::new::<M>(
-                        <Self as $crate::ModuleMetadata>::NAME,
-                    ),
+                    _driver <- $crate::driver::Registration::new::<M>(),
                 })
             }
         }
diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs
index 71168d8004e2..254b5c6b64af 100644
--- a/rust/kernel/firmware.rs
+++ b/rust/kernel/firmware.rs
@@ -206,7 +206,7 @@ macro_rules! module_firmware {
             const __MODULE_FIRMWARE_PREFIX: &'static $crate::str::CStr = if cfg!(MODULE) {
                 c""
             } else {
-                <LocalModule as $crate::ModuleMetadata>::NAME
+                THIS_MODULE::name()
             };
 
             #[link_section = ".modinfo"]
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index bc154506b16f..31db4d45bab1 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -97,7 +97,7 @@ macro_rules! i2c_device_table {
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::i2c_driver;
 
-    unsafe fn register<M: ThisModule>(idrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
+    unsafe fn register<M: ThisModule>(idrv: &Opaque<Self::RegType>) -> Result {
         build_assert!(
             T::ACPI_ID_TABLE.is_some() || T::OF_ID_TABLE.is_some() || T::I2C_ID_TABLE.is_some(),
             "At least one of ACPI/OF/Legacy tables must be present when registering an i2c driver"
@@ -120,7 +120,7 @@ unsafe fn register<M: ThisModule>(idrv: &Opaque<Self::RegType>, name: &'static C
 
         // SAFETY: It's safe to set the fields of `struct i2c_client` on initialization.
         unsafe {
-            (*idrv.get()).driver.name = name.as_char_ptr();
+            (*idrv.get()).driver.name = M::NAME.as_char_ptr();
             (*idrv.get()).probe = Some(Self::probe_callback);
             (*idrv.get()).remove = Some(Self::remove_callback);
             (*idrv.get()).shutdown = Some(Self::shutdown_callback);
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 6d4563662a02..3bae80a949d2 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -199,12 +199,6 @@ fn init<M: ThisModule>() -> impl pin_init::PinInit<Self, error::Error> {
     }
 }
 
-/// Metadata attached to a [`Module`] or [`InPlaceModule`].
-pub trait ModuleMetadata {
-    /// The name of the module as specified in the `module!` macro.
-    const NAME: &'static crate::str::CStr;
-}
-
 pub mod this_module {
     //! TODO
     //!
@@ -224,6 +218,8 @@ pub mod this_module {
     pub trait ThisModule {
         /// TODO Doc
         const OWNER: ModuleWrapper;
+        /// TODO Doc
+        const NAME: &'static kernel::str::CStr;
     }
 
     /// See [`this_module`]
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 792560ca8020..b043d7a388d0 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -54,10 +54,10 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::pci_driver;
 
-    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
+    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>) -> Result {
         // SAFETY: It's safe to set the fields of `struct pci_driver` on initialization.
         unsafe {
-            (*pdrv.get()).name = name.as_char_ptr();
+            (*pdrv.get()).name = M::NAME.as_char_ptr();
             (*pdrv.get()).probe = Some(Self::probe_callback);
             (*pdrv.get()).remove = Some(Self::remove_callback);
             (*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
@@ -65,7 +65,7 @@ unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static C
 
         // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::__pci_register_driver(pdrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
+            bindings::__pci_register_driver(pdrv.get(), M::OWNER.as_ptr(), M::NAME.as_char_ptr())
         })
     }
 
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 67d46231600e..27f196a140e5 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -30,7 +30,7 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::platform_driver;
 
-    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
+    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>) -> Result {
         let of_table = match T::OF_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
@@ -43,7 +43,7 @@ unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static C
 
         // SAFETY: It's safe to set the fields of `struct platform_driver` on initialization.
         unsafe {
-            (*pdrv.get()).driver.name = name.as_char_ptr();
+            (*pdrv.get()).driver.name = M::NAME.as_char_ptr();
             (*pdrv.get()).probe = Some(Self::probe_callback);
             (*pdrv.get()).remove = Some(Self::remove_callback);
             (*pdrv.get()).driver.of_match_table = of_table;
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index c6ee98d12875..43259307986f 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -31,10 +31,10 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::usb_driver;
 
-    unsafe fn register<M: ThisModule>(udrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
+    unsafe fn register<M: ThisModule>(udrv: &Opaque<Self::RegType>) -> Result {
         // SAFETY: It's safe to set the fields of `struct usb_driver` on initialization.
         unsafe {
-            (*udrv.get()).name = name.as_char_ptr();
+            (*udrv.get()).name = M::NAME.as_char_ptr();
             (*udrv.get()).probe = Some(Self::probe_callback);
             (*udrv.get()).disconnect = Some(Self::disconnect_callback);
             (*udrv.get()).id_table = T::ID_TABLE.as_ptr();
@@ -42,7 +42,7 @@ unsafe fn register<M: ThisModule>(udrv: &Opaque<Self::RegType>, name: &'static C
 
         // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::usb_register_driver(udrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
+            bindings::usb_register_driver(udrv.get(), M::OWNER.as_ptr(), M::NAME.as_char_ptr())
         })
     }
 
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 6b8753d122cc..6a1ce6435e8f 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -375,6 +375,13 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
             #[allow(non_camel_case_types)]
             pub struct THIS_MODULE;
 
+            impl THIS_MODULE {{
+                /// Returns the name of this module.
+                pub const fn name() -> &'static ::kernel::str::CStr {{
+                    c\"{name}\"
+                }}
+            }}
+
             impl ::kernel::prelude::ThisModule for THIS_MODULE {{
                 #[cfg(not(MODULE))] 
                 const OWNER: ::kernel::this_module::ModuleWrapper = unsafe {{
@@ -392,13 +399,7 @@ impl ::kernel::prelude::ThisModule for THIS_MODULE {{
                     
                     ::kernel::this_module::ModuleWrapper::from_ptr(__this_module.get())
                 }};
-            }}
-
-            /// The `LocalModule` type is the type of the module created by `module!`,
-            /// `module_pci_driver!`, `module_platform_driver!`, etc.
-            type LocalModule = {type_};
 
-            impl ::kernel::ModuleMetadata for {type_} {{
                 const NAME: &'static ::kernel::str::CStr = c\"{name}\";
             }}
 
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index e996dca19454..2f77b0873e81 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -18,7 +18,7 @@
 use core::any::TypeId;
 use pin_init::PinInit;
 
-const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
+const MODULE_NAME: &CStr = THIS_MODULE::name();
 const AUXILIARY_NAME: &CStr = c_str!("auxiliary");
 
 struct AuxiliaryDriver;
@@ -113,8 +113,8 @@ struct SampleModule {
 impl InPlaceModule for SampleModule {
     fn init<M: ThisModule>() -> impl PinInit<Self, Error> {
         try_pin_init!(Self {
-            _pci_driver <- driver::Registration::new::<M>(MODULE_NAME),
-            _aux_driver <- driver::Registration::new::<M>(MODULE_NAME),
+            _pci_driver <- driver::Registration::new::<M>(),
+            _aux_driver <- driver::Registration::new::<M>(),
         })
     }
 }

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 5/6] rust: debugfs: WIP: Use owner for file_operations
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

---
 rust/kernel/debugfs.rs          | 79 ++++++++++++++++++++++-------------------
 rust/kernel/debugfs/file_ops.rs | 50 +++++++++++++++++++-------
 samples/rust/rust_debugfs.rs    |  6 ++--
 3 files changed, 83 insertions(+), 52 deletions(-)

diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index facad81e8290..03e22b1cfa2b 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -6,7 +6,8 @@
 //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
 
 // When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful.
-#![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))]
+// #![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))]
+#![allow(unused_variables)]
 
 use crate::fmt;
 use crate::prelude::*;
@@ -46,27 +47,31 @@
 // able to refer to us. In this case, we need to silently fail. All future child directories/files
 // will silently fail as well.
 #[derive(Clone)]
-pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>);
+pub struct Dir<M: ThisModule>(
+    #[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>,
+    PhantomData<M>,
+);
 
-impl Dir {
+impl<M: ThisModule> Dir<M> {
     /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
-    fn create(name: &CStr, parent: Option<&Dir>) -> Self {
+    fn create(name: &CStr, parent: Option<&Self>) -> Self {
         #[cfg(CONFIG_DEBUG_FS)]
         {
             let parent_entry = match parent {
                 // If the parent couldn't be allocated, just early-return
-                Some(Dir(None)) => return Self(None),
-                Some(Dir(Some(entry))) => Some(entry.clone()),
+                Some(Self(None, _)) => return Self(None, PhantomData),
+                Some(Self(Some(entry), _)) => Some(entry.clone()),
                 None => None,
             };
             Self(
                 // If Arc creation fails, the `Entry` will be dropped, so the directory will be
                 // cleaned up.
                 Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
+                PhantomData,
             )
         }
         #[cfg(not(CONFIG_DEBUG_FS))]
-        Self()
+        Self(PhantomData)
     }
 
     /// Creates a DebugFS file which will own the data produced by the initializer provided in
@@ -107,7 +112,7 @@ fn create_file<'a, T, E: 'a>(
     /// let debugfs = Dir::new(c_str!("parent"));
     /// ```
     pub fn new(name: &CStr) -> Self {
-        Dir::create(name, None)
+        Self::create(name, None)
     }
 
     /// Creates a subdirectory within this directory.
@@ -121,7 +126,7 @@ pub fn new(name: &CStr) -> Self {
     /// let child = parent.subdir(c_str!("child"));
     /// ```
     pub fn subdir(&self, name: &CStr) -> Self {
-        Dir::create(name, Some(self))
+        Self::create(name, Some(self))
     }
 
     /// Creates a read-only file in this directory.
@@ -149,7 +154,7 @@ pub fn read_only_file<'a, T, E: 'a>(
     where
         T: Writer + Send + Sync + 'static,
     {
-        let file_ops = &<T as ReadFile<_>>::FILE_OPS;
+        let file_ops = &<T as ReadFile<M, _>>::FILE_OPS;
         self.create_file(name, data, file_ops)
     }
 
@@ -176,7 +181,7 @@ pub fn read_binary_file<'a, T, E: 'a>(
     where
         T: BinaryWriter + Send + Sync + 'static,
     {
-        self.create_file(name, data, &T::FILE_OPS)
+        self.create_file(name, data, &<T as BinaryReadFile<M, _>>::FILE_OPS)
     }
 
     /// Creates a read-only file in this directory, with contents from a callback.
@@ -215,7 +220,7 @@ pub fn read_callback_file<'a, T, E: 'a, F>(
         T: Send + Sync + 'static,
         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
     {
-        let file_ops = <FormatAdapter<T, F>>::FILE_OPS.adapt();
+        let file_ops = <FormatAdapter<T, F> as ReadFile<M, FormatAdapter<T, F>>>::FILE_OPS.adapt();
         self.create_file(name, data, file_ops)
     }
 
@@ -231,7 +236,7 @@ pub fn read_write_file<'a, T, E: 'a>(
     where
         T: Writer + Reader + Send + Sync + 'static,
     {
-        let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS;
+        let file_ops = &<T as ReadWriteFile<M, _>>::FILE_OPS;
         self.create_file(name, data, file_ops)
     }
 
@@ -247,7 +252,7 @@ pub fn read_write_binary_file<'a, T, E: 'a>(
     where
         T: BinaryWriter + BinaryReader + Send + Sync + 'static,
     {
-        let file_ops = &<T as BinaryReadWriteFile<_>>::FILE_OPS;
+        let file_ops = &<T as BinaryReadWriteFile<M, _>>::FILE_OPS;
         self.create_file(name, data, file_ops)
     }
 
@@ -270,7 +275,7 @@ pub fn read_write_callback_file<'a, T, E: 'a, F, W>(
         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
     {
         let file_ops =
-            <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<_>>::FILE_OPS
+            <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<M, _>>::FILE_OPS
                 .adapt()
                 .adapt();
         self.create_file(name, data, file_ops)
@@ -290,7 +295,7 @@ pub fn write_only_file<'a, T, E: 'a>(
     where
         T: Reader + Send + Sync + 'static,
     {
-        self.create_file(name, data, &T::FILE_OPS)
+        self.create_file(name, data, &<T as WriteFile<M, _>>::FILE_OPS)
     }
 
     /// Creates a write-only binary file in this directory.
@@ -307,7 +312,7 @@ pub fn write_binary_file<'a, T, E: 'a>(
     where
         T: BinaryReader + Send + Sync + 'static,
     {
-        self.create_file(name, data, &T::FILE_OPS)
+        self.create_file(name, data, &<T as BinaryWriteFile<M, _>>::FILE_OPS)
     }
 
     /// Creates a write-only file in this directory, with write logic from a callback.
@@ -324,7 +329,7 @@ pub fn write_callback_file<'a, T, E: 'a, W>(
         T: Send + Sync + 'static,
         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
     {
-        let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
+        let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<M, _>>::FILE_OPS
             .adapt()
             .adapt();
         self.create_file(name, data, file_ops)
@@ -527,7 +532,7 @@ fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static Fil
     /// file is removed when the [`Scope`] that this directory belongs
     /// to is dropped.
     pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
-        self.create_file(name, data, &T::FILE_OPS)
+        // self.create_file(name, data, &<T as ReadFile<M, T>>::FILE_OPS)
     }
 
     /// Creates a read-only binary file in this directory.
@@ -541,7 +546,7 @@ pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>(
         name: &CStr,
         data: &'data T,
     ) {
-        self.create_file(name, data, &T::FILE_OPS)
+        // self.create_file(name, data, &<T as ReadFile<M, T>>::FILE_OPS)
     }
 
     /// Creates a read-only file in this directory, with contents from a callback.
@@ -560,8 +565,8 @@ pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static
         T: Send + Sync + 'static,
         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
     {
-        let vtable = <FormatAdapter<T, F> as ReadFile<_>>::FILE_OPS.adapt();
-        self.create_file(name, data, vtable)
+        // let vtable = <FormatAdapter<T, F> as ReadFile<M, _>>::FILE_OPS.adapt();
+        // self.create_file(name, data, vtable)
     }
 
     /// Creates a read-write file in this directory.
@@ -577,8 +582,8 @@ pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>(
         name: &CStr,
         data: &'data T,
     ) {
-        let vtable = &<T as ReadWriteFile<_>>::FILE_OPS;
-        self.create_file(name, data, vtable)
+        // let vtable = &<T as ReadWriteFile<_>>::FILE_OPS;
+        // self.create_file(name, data, vtable)
     }
 
     /// Creates a read-write binary file in this directory.
@@ -593,8 +598,8 @@ pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'st
         name: &CStr,
         data: &'data T,
     ) {
-        let vtable = &<T as BinaryReadWriteFile<_>>::FILE_OPS;
-        self.create_file(name, data, vtable)
+        // let vtable = &<T as BinaryReadWriteFile<_>>::FILE_OPS;
+        // self.create_file(name, data, vtable)
     }
 
     /// Creates a read-write file in this directory, with logic from callbacks.
@@ -618,10 +623,10 @@ pub fn read_write_callback_file<T, F, W>(
         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
     {
-        let vtable = <WritableAdapter<FormatAdapter<T, F>, W> as ReadWriteFile<_>>::FILE_OPS
-            .adapt()
-            .adapt();
-        self.create_file(name, data, vtable)
+        // let vtable = <WritableAdapter<FormatAdapter<T, F>, W> as ReadWriteFile<_>>::FILE_OPS
+        //     .adapt()
+        //     .adapt();
+        // self.create_file(name, data, vtable)
     }
 
     /// Creates a write-only file in this directory.
@@ -632,8 +637,8 @@ pub fn read_write_callback_file<T, F, W>(
     /// file is removed when the [`Scope`] that this directory belongs
     /// to is dropped.
     pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
-        let vtable = &<T as WriteFile<_>>::FILE_OPS;
-        self.create_file(name, data, vtable)
+        // let vtable = &<T as WriteFile<_>>::FILE_OPS;
+        // self.create_file(name, data, vtable)
     }
 
     /// Creates a write-only binary file in this directory.
@@ -647,7 +652,7 @@ pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>(
         name: &CStr,
         data: &'data T,
     ) {
-        self.create_file(name, data, &T::FILE_OPS)
+        // self.create_file(name, data, &<T as ReadFile<M, T>>::FILE_OPS)
     }
 
     /// Creates a write-only file in this directory, with write logic from a callback.
@@ -665,10 +670,10 @@ pub fn write_only_callback_file<T, W>(&self, name: &CStr, data: &'data T, _w: &'
         T: Send + Sync + 'static,
         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
     {
-        let vtable = &<WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
-            .adapt()
-            .adapt();
-        self.create_file(name, data, vtable)
+        // let vtable = &<WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
+        //     .adapt()
+        //     .adapt();
+        // self.create_file(name, data, vtable)
     }
 
     fn empty() -> Self {
diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs
index 8a0442d6dd7a..0e5059c044af 100644
--- a/rust/kernel/debugfs/file_ops.rs
+++ b/rust/kernel/debugfs/file_ops.rs
@@ -115,13 +115,14 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 }
 
 // Work around lack of generic const items.
-pub(crate) trait ReadFile<T> {
+pub(crate) trait ReadFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: Writer + Sync> ReadFile<T> for T {
+impl<M: ThisModule, T: Writer + Sync> ReadFile<M, T> for T {
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             read: Some(bindings::seq_read),
             llseek: Some(bindings::seq_lseek),
             release: Some(bindings::single_release),
@@ -167,13 +168,18 @@ fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize {
 }
 
 // A trait to get the file operations for a type.
-pub(crate) trait ReadWriteFile<T> {
+pub(crate) trait ReadWriteFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
+impl<M, T> ReadWriteFile<M, T> for T
+where
+    M: ThisModule,
+    T: Writer + Reader + Sync,
+{
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             open: Some(writer_open::<T>),
             read: Some(bindings::seq_read),
             write: Some(write::<T>),
@@ -225,13 +231,18 @@ impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
     read(data, buf, count)
 }
 
-pub(crate) trait WriteFile<T> {
+pub(crate) trait WriteFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: Reader + Sync> WriteFile<T> for T {
+impl<M, T> WriteFile<M, T> for T
+where
+    M: ThisModule,
+    T: Reader + Sync,
+{
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             open: Some(write_only_open),
             write: Some(write_only_write::<T>),
             llseek: Some(bindings::noop_llseek),
@@ -278,13 +289,18 @@ extern "C" fn blob_read<T: BinaryWriter>(
 }
 
 /// Representation of [`FileOps`] for read only binary files.
-pub(crate) trait BinaryReadFile<T> {
+pub(crate) trait BinaryReadFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
+impl<M, T> BinaryReadFile<M, T> for T
+where
+    M: ThisModule,
+    T: BinaryWriter + Sync,
+{
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             read: Some(blob_read::<T>),
             llseek: Some(bindings::default_llseek),
             open: Some(bindings::simple_open),
@@ -333,13 +349,18 @@ extern "C" fn blob_write<T: BinaryReader>(
 }
 
 /// Representation of [`FileOps`] for write only binary files.
-pub(crate) trait BinaryWriteFile<T> {
+pub(crate) trait BinaryWriteFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
+impl<M, T> BinaryWriteFile<M, T> for T
+where
+    M: ThisModule,
+    T: BinaryReader + Sync,
+{
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             write: Some(blob_write::<T>),
             llseek: Some(bindings::default_llseek),
             open: Some(bindings::simple_open),
@@ -358,13 +379,18 @@ impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
 }
 
 /// Representation of [`FileOps`] for read/write binary files.
-pub(crate) trait BinaryReadWriteFile<T> {
+pub(crate) trait BinaryReadWriteFile<M, T> {
     const FILE_OPS: FileOps<T>;
 }
 
-impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
+impl<M, T> BinaryReadWriteFile<M, T> for T
+where
+    M: ThisModule,
+    T: BinaryWriter + BinaryReader + Sync,
+{
     const FILE_OPS: FileOps<T> = {
         let operations = bindings::file_operations {
+            owner: M::OWNER.as_ptr(),
             read: Some(blob_read::<T>),
             write: Some(blob_write::<T>),
             llseek: Some(bindings::default_llseek),
diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs
index 025e8f9d12de..85c3f93159fd 100644
--- a/samples/rust/rust_debugfs.rs
+++ b/samples/rust/rust_debugfs.rs
@@ -54,7 +54,7 @@ struct RustDebugFs {
     pdev: ARef<platform::Device>,
     // As we only hold these for drop effect (to remove the directory/files) we have a leading
     // underscore to indicate to the compiler that we don't expect to use this field directly.
-    _debugfs: Dir,
+    _debugfs: Dir<THIS_MODULE>,
     #[pin]
     _compatible: File<CString>,
     #[pin]
@@ -124,11 +124,11 @@ fn probe(
 }
 
 impl RustDebugFs {
-    fn build_counter(dir: &Dir) -> impl PinInit<File<Atomic<usize>>> + '_ {
+    fn build_counter<M: ThisModule>(dir: &Dir<M>) -> impl PinInit<File<Atomic<usize>>> + '_ {
         dir.read_write_file(c_str!("counter"), Atomic::<usize>::new(0))
     }
 
-    fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ {
+    fn build_inner<M: ThisModule>(dir: &Dir<M>) -> impl PinInit<File<Mutex<Inner>>> + '_ {
         dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 }))
     }
 

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 4/6] rust: WIP: use ThisModule trait to fix some missing owners
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

Some places do not define owner and we have null pointer dereference
bugs which we can fix with this. I have tested miscdevice that it
actually works.
---
 drivers/android/binder/rust_binder_main.rs |  2 +-
 drivers/block/rnull/rnull.rs               |  1 +
 drivers/gpu/drm/nova/driver.rs             |  1 +
 drivers/gpu/drm/tyr/driver.rs              |  1 +
 lib/find_bit_benchmark_rust.rs             |  2 +-
 rust/kernel/block/mq/gen_disk.rs           | 31 ++++--------------------------
 rust/kernel/block/mq/operations.rs         | 12 ++++++++++++
 rust/kernel/drm/device.rs                  |  2 +-
 rust/kernel/drm/driver.rs                  |  3 +++
 rust/kernel/miscdevice.rs                  |  4 ++++
 samples/rust/rust_misc_device.rs           |  1 +
 11 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index 169fe552e32a..7877503c639e 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -311,7 +311,7 @@ unsafe impl<T> Sync for AssertSync<T> {}
     let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
 
     let ops = kernel::bindings::file_operations {
-        owner: THIS_MODULE.as_ptr(),
+        owner: <THIS_MODULE as ThisModule>::OWNER.as_ptr(),
         poll: Some(rust_binder_poll),
         unlocked_ioctl: Some(rust_binder_ioctl),
         compat_ioctl: Some(bindings::compat_ptr_ioctl),
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index c9dff74489c1..3360a0f50fc6 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -75,6 +75,7 @@ struct QueueData {
 #[vtable]
 impl Operations for NullBlkDevice {
     type QueueData = KBox<QueueData>;
+    type ThisModule = THIS_MODULE;
 
     #[inline(always)]
     fn queue_rq(queue_data: &QueueData, rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs
index 2246d8e104e0..a49c9848ce2e 100644
--- a/drivers/gpu/drm/nova/driver.rs
+++ b/drivers/gpu/drm/nova/driver.rs
@@ -57,6 +57,7 @@ fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<S
 
 #[vtable]
 impl drm::Driver for NovaDriver {
+    type ThisModule = crate::THIS_MODULE;
     type Data = NovaData;
     type File = File;
     type Object = gem::Object<NovaObject>;
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 264c2362237a..a84825dbd008 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -180,6 +180,7 @@ fn drop(self: Pin<&mut Self>) {
 
 #[vtable]
 impl drm::Driver for TyrDriver {
+    type ThisModule = crate::THIS_MODULE;
     type Data = TyrData;
     type File = File;
     type Object = drm::gem::Object<TyrObject>;
diff --git a/lib/find_bit_benchmark_rust.rs b/lib/find_bit_benchmark_rust.rs
index 6bdc51de2f30..420a1855b08a 100644
--- a/lib/find_bit_benchmark_rust.rs
+++ b/lib/find_bit_benchmark_rust.rs
@@ -88,7 +88,7 @@ fn find_bit_test() {
 }
 
 impl kernel::Module for Benchmark {
-    fn init(_module: &'static ThisModule) -> Result<Self> {
+    fn init<M: ThisModule>() -> Result<Self> {
         find_bit_test();
         // Return error so test module can be inserted again without rmmod.
         Err(code::EINVAL)
diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs
index 1ce815c8cdab..f5839829d0b7 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -7,7 +7,7 @@
 
 use crate::{
     bindings,
-    block::mq::{Operations, TagSet},
+    block::mq::{operations::OperationsVTable, Operations, TagSet},
     error::{self, from_err_ptr, Result},
     fmt::{self, Write},
     prelude::*,
@@ -126,32 +126,9 @@ pub fn build<T: Operations>(
             )
         })?;
 
-        const TABLE: bindings::block_device_operations = bindings::block_device_operations {
-            submit_bio: None,
-            open: None,
-            release: None,
-            ioctl: None,
-            compat_ioctl: None,
-            check_events: None,
-            unlock_native_capacity: None,
-            getgeo: None,
-            set_read_only: None,
-            swap_slot_free_notify: None,
-            report_zones: None,
-            devnode: None,
-            alternative_gpt_sector: None,
-            get_unique_id: None,
-            // TODO: Set to THIS_MODULE. Waiting for const_refs_to_static feature to
-            // be merged (unstable in rustc 1.78 which is staged for linux 6.10)
-            // <https://github.com/rust-lang/rust/issues/119618>
-            owner: core::ptr::null_mut(),
-            pr_ops: core::ptr::null_mut(),
-            free_disk: None,
-            poll_bio: None,
-        };
-
-        // SAFETY: `gendisk` is a valid pointer as we initialized it above
-        unsafe { (*gendisk).fops = &TABLE };
+        unsafe {
+            (*gendisk).fops = OperationsVTable::<T>::build_block_device_operations();
+        }
 
         let mut writer = NullTerminatedFormatter::new(
             // SAFETY: `gendisk` points to a valid and initialized instance. We
diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs
index 8ad46129a52c..3e0e8baa7d07 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -31,6 +31,8 @@ pub trait Operations: Sized {
     /// Data associated with the `struct request_queue` that is allocated for
     /// the `GenDisk` associated with this `Operations` implementation.
     type QueueData: ForeignOwnable;
+    /// TODO Doc
+    type ThisModule: ThisModule;
 
     /// Called by the kernel to queue a request with the driver. If `is_last` is
     /// `false`, the driver is allowed to defer committing the request.
@@ -283,4 +285,14 @@ impl<T: Operations> OperationsVTable<T> {
     pub(crate) const fn build() -> &'static bindings::blk_mq_ops {
         &Self::VTABLE
     }
+
+    const BLOCK_OPS: bindings::block_device_operations = bindings::block_device_operations {
+        owner: T::ThisModule::OWNER.as_ptr(),
+        ..pin_init::zeroed()
+    };
+
+    pub(crate) const fn build_block_device_operations() -> &'static bindings::block_device_operations
+    {
+        &Self::BLOCK_OPS
+    }
 }
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 3ce8f62a0056..a740c87933d0 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -92,7 +92,7 @@ impl<T: drm::Driver> Device<T> {
         fops: &Self::GEM_FOPS,
     };
 
-    const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
+    const GEM_FOPS: bindings::file_operations = drm::gem::create_fops::<T::ThisModule>();
 
     /// Create a new `drm::Device` for a `drm::Driver`.
     pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index f30ee4c6245c..ae8f7d3b9156 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -99,6 +99,9 @@ pub trait AllocImpl: super::private::Sealed + drm::gem::IntoGEMObject {
 /// drm_driver` to be registered in the DRM subsystem.
 #[vtable]
 pub trait Driver {
+    /// TODO Doc
+    type ThisModule: ThisModule;
+
     /// Context data associated with the DRM driver
     type Data: Sync + Send;
 
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index d698cddcb4a5..08346c52d574 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -116,6 +116,9 @@ pub trait MiscDevice: Sized {
     /// What kind of pointer should `Self` be wrapped in.
     type Ptr: ForeignOwnable + Send + Sync;
 
+    /// TODO Docs
+    type ThisModule: ThisModule;
+
     /// Called when the misc device is opened.
     ///
     /// The returned pointer will be stored as the private data for the file.
@@ -389,6 +392,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
     }
 
     const VTABLE: bindings::file_operations = bindings::file_operations {
+        owner: T::ThisModule::OWNER.as_ptr(),
         open: Some(Self::open),
         release: Some(Self::release),
         mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None },
diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs
index 3f1acb6818a5..78d239b26dcc 100644
--- a/samples/rust/rust_misc_device.rs
+++ b/samples/rust/rust_misc_device.rs
@@ -157,6 +157,7 @@ struct RustMiscDevice {
 #[vtable]
 impl MiscDevice for RustMiscDevice {
     type Ptr = Pin<KBox<Self>>;
+    type ThisModule = THIS_MODULE;
 
     fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
         let dev = ARef::from(misc.device());

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 3/6] rust: WIP: use ThisModule trait for initializing
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

Now that ThisModule is trait we can use it for initializing. This way we
can also use it in const context. This does matter in later on.
---
 drivers/android/binder/rust_binder_main.rs |  2 +-
 drivers/block/rnull/configfs.rs            |  2 +-
 drivers/block/rnull/rnull.rs               |  2 +-
 rust/kernel/auxiliary.rs                   |  9 ++-------
 rust/kernel/configfs.rs                    | 19 +++++++++----------
 rust/kernel/driver.rs                      | 17 +++++------------
 rust/kernel/drm/gem/mod.rs                 |  4 ++--
 rust/kernel/i2c.rs                         |  8 ++------
 rust/kernel/lib.rs                         |  8 ++++----
 rust/kernel/net/phy.rs                     | 16 ++++++++--------
 rust/kernel/pci.rs                         |  9 ++-------
 rust/kernel/platform.rs                    |  9 ++-------
 rust/kernel/usb.rs                         |  9 ++-------
 samples/rust/rust_configfs.rs              |  2 +-
 samples/rust/rust_driver_auxiliary.rs      |  6 +++---
 samples/rust/rust_driver_faux.rs           |  2 +-
 samples/rust/rust_minimal.rs               |  2 +-
 samples/rust/rust_misc_device.rs           |  2 +-
 samples/rust/rust_print_main.rs            |  2 +-
 19 files changed, 49 insertions(+), 81 deletions(-)

diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index c79a9e742240..169fe552e32a 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -282,7 +282,7 @@ fn ptr_align(value: usize) -> Option<usize> {
 struct BinderModule {}
 
 impl kernel::Module for BinderModule {
-    fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
+    fn init<M: ThisModule>() -> Result<Self> {
         // SAFETY: The module initializer never runs twice, so we only call this once.
         unsafe { crate::context::CONTEXTS.init() };
 
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 6713a6d92391..a581c97219c7 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 
-use super::{NullBlkDevice, THIS_MODULE};
+use super::NullBlkDevice;
 use kernel::{
     block::mq::gen_disk::{GenDisk, GenDiskBuilder},
     c_str,
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index a9d5e575a2c4..c9dff74489c1 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -36,7 +36,7 @@ struct NullBlkModule {
 }
 
 impl kernel::InPlaceModule for NullBlkModule {
-    fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+    fn init<M: ThisModule>() -> impl PinInit<Self, Error> {
         pr_info!("Rust null_blk loaded\n");
 
         try_pin_init!(Self {
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 56f3c180e8f6..323074322505 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -12,7 +12,6 @@
     error::{from_result, to_result, Result},
     prelude::*,
     types::Opaque,
-    ThisModule,
 };
 use core::{
     marker::PhantomData,
@@ -28,11 +27,7 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::auxiliary_driver;
 
-    unsafe fn register(
-        adrv: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result {
+    unsafe fn register<M: ThisModule>(adrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
         // SAFETY: It's safe to set the fields of `struct auxiliary_driver` on initialization.
         unsafe {
             (*adrv.get()).name = name.as_char_ptr();
@@ -43,7 +38,7 @@ unsafe fn register(
 
         // SAFETY: `adrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
+            bindings::__auxiliary_driver_register(adrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
         })
     }
 
diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs
index 466fb7f40762..f6f0a32e06d1 100644
--- a/rust/kernel/configfs.rs
+++ b/rust/kernel/configfs.rs
@@ -744,17 +744,17 @@ macro_rules! impl_item_type {
     ($tpe:ty) => {
         impl<Data> ItemType<$tpe, Data> {
             #[doc(hidden)]
-            pub const fn new_with_child_ctor<const N: usize, Child>(
-                owner: &'static ThisModule,
+            pub const fn new_with_child_ctor<const N: usize, Child, M>(
                 attributes: &'static AttributeList<N, Data>,
             ) -> Self
             where
                 Data: GroupOperations<Child = Child>,
                 Child: 'static,
+                M: ThisModule,
             {
                 Self {
                     item_type: Opaque::new(bindings::config_item_type {
-                        ct_owner: owner.as_ptr(),
+                        ct_owner: M::OWNER.as_ptr(),
                         ct_group_ops: GroupOperationsVTable::<Data, Child>::vtable_ptr().cast_mut(),
                         ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
                         ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
@@ -765,13 +765,12 @@ pub const fn new_with_child_ctor<const N: usize, Child>(
             }
 
             #[doc(hidden)]
-            pub const fn new<const N: usize>(
-                owner: &'static ThisModule,
+            pub const fn new<const N: usize, M: ThisModule>(
                 attributes: &'static AttributeList<N, Data>,
             ) -> Self {
                 Self {
                     item_type: Opaque::new(bindings::config_item_type {
-                        ct_owner: owner.as_ptr(),
+                        ct_owner: M::OWNER.as_ptr(),
                         ct_group_ops: core::ptr::null_mut(),
                         ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
                         ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
@@ -1019,8 +1018,8 @@ macro_rules! configfs_attrs {
                     const [<$no_child:upper>]: bool = true;
 
                     static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data>  =
-                        $crate::configfs::ItemType::<$container, $data>::new::<N>(
-                            &THIS_MODULE, &[<$ data:upper _ATTRS >]
+                        $crate::configfs::ItemType::<$container, $data>::new::<N, crate::THIS_MODULE>(
+                            &[<$ data:upper _ATTRS >]
                         );
                 )?
 
@@ -1028,8 +1027,8 @@ macro_rules! configfs_attrs {
                     static [< $data:upper _TPE >]:
                         $crate::configfs::ItemType<$container, $data>  =
                             $crate::configfs::ItemType::<$container, $data>::
-                            new_with_child_ctor::<N, $child>(
-                                &THIS_MODULE, &[<$ data:upper _ATTRS >]
+                            new_with_child_ctor::<N, $child, crate::THIS_MODULE>(
+                                &[<$ data:upper _ATTRS >]
                             );
                 )?
 
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
index 9beae2e3d57e..7c4ad24bb48a 100644
--- a/rust/kernel/driver.rs
+++ b/rust/kernel/driver.rs
@@ -118,11 +118,7 @@ pub unsafe trait RegistrationOps {
     ///
     /// On success, `reg` must remain pinned and valid until the matching call to
     /// [`RegistrationOps::unregister`].
-    unsafe fn register(
-        reg: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result;
+    unsafe fn register<M: ThisModule>(reg: &Opaque<Self::RegType>, name: &'static CStr) -> Result;
 
     /// Unregisters a driver previously registered with [`RegistrationOps::register`].
     ///
@@ -155,7 +151,7 @@ unsafe impl<T: RegistrationOps> Send for Registration<T> {}
 
 impl<T: RegistrationOps> Registration<T> {
     /// Creates a new instance of the registration object.
-    pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Self, Error> {
+    pub fn new<M: ThisModule>(name: &'static CStr) -> impl PinInit<Self, Error> {
         try_pin_init!(Self {
             reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| {
                 // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
@@ -166,7 +162,7 @@ pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Sel
                 let drv = unsafe { &*(ptr as *const Opaque<T::RegType>) };
 
                 // SAFETY: `drv` is guaranteed to be pinned until `T::unregister`.
-                unsafe { T::register(drv, name, module) }
+                unsafe { T::register::<M>(drv, name) }
             }),
         })
     }
@@ -197,13 +193,10 @@ struct DriverModule {
         }
 
         impl $crate::InPlaceModule for DriverModule {
-            fn init(
-                module: &'static $crate::ThisModule
-            ) -> impl ::pin_init::PinInit<Self, $crate::error::Error> {
+            fn init<M: ::kernel::ThisModule>() -> impl ::pin_init::PinInit<Self, $crate::error::Error> {
                 $crate::try_pin_init!(Self {
-                    _driver <- $crate::driver::Registration::new(
+                    _driver <- $crate::driver::Registration::new::<M>(
                         <Self as $crate::ModuleMetadata>::NAME,
-                        module,
                     ),
                 })
             }
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs
index bdaac839dacc..1d9f78752946 100644
--- a/rust/kernel/drm/gem/mod.rs
+++ b/rust/kernel/drm/gem/mod.rs
@@ -292,10 +292,10 @@ impl<T: DriverObject> AllocImpl for Object<T> {
     };
 }
 
-pub(super) const fn create_fops() -> bindings::file_operations {
+pub(super) const fn create_fops<M: ThisModule>() -> bindings::file_operations {
     let mut fops: bindings::file_operations = pin_init::zeroed();
 
-    fops.owner = core::ptr::null_mut();
+    fops.owner = M::OWNER.as_ptr();
     fops.open = Some(bindings::drm_open);
     fops.release = Some(bindings::drm_release);
     fops.unlocked_ioctl = Some(bindings::drm_ioctl);
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 491e6cc25cf4..bc154506b16f 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -97,11 +97,7 @@ macro_rules! i2c_device_table {
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::i2c_driver;
 
-    unsafe fn register(
-        idrv: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result {
+    unsafe fn register<M: ThisModule>(idrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
         build_assert!(
             T::ACPI_ID_TABLE.is_some() || T::OF_ID_TABLE.is_some() || T::I2C_ID_TABLE.is_some(),
             "At least one of ACPI/OF/Legacy tables must be present when registering an i2c driver"
@@ -134,7 +130,7 @@ unsafe fn register(
         }
 
         // SAFETY: `idrv` is guaranteed to be a valid `RegType`.
-        to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
+        to_result(unsafe { bindings::i2c_register_driver(M::OWNER.as_ptr(), idrv.get()) })
     }
 
     unsafe fn unregister(idrv: &Opaque<Self::RegType>) {
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 224410745734..6d4563662a02 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -173,7 +173,7 @@ pub trait Module: Sized + Sync + Send {
     /// should do.
     ///
     /// Equivalent to the `module_init` macro in the C API.
-    fn init(module: &'static ThisModule) -> error::Result<Self>;
+    fn init<M: ThisModule>() -> error::Result<Self>;
 }
 
 /// A module that is pinned and initialised in-place.
@@ -181,13 +181,13 @@ pub trait InPlaceModule: Sync + Send {
     /// Creates an initialiser for the module.
     ///
     /// It is called when the module is loaded.
-    fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, error::Error>;
+    fn init<M: ThisModule>() -> impl pin_init::PinInit<Self, error::Error>;
 }
 
 impl<T: Module> InPlaceModule for T {
-    fn init(module: &'static ThisModule) -> impl pin_init::PinInit<Self, error::Error> {
+    fn init<M: ThisModule>() -> impl pin_init::PinInit<Self, error::Error> {
         let initer = move |slot: *mut Self| {
-            let m = <Self as Module>::init(module)?;
+            let m = <Self as Module>::init::<M>()?;
 
             // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`.
             unsafe { slot.write(m) };
diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index bf6272d87a7b..5fc90d949d60 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -648,10 +648,7 @@ unsafe impl Send for Registration {}
 
 impl Registration {
     /// Registers a PHY driver.
-    pub fn register(
-        module: &'static crate::ThisModule,
-        drivers: Pin<&'static mut [DriverVTable]>,
-    ) -> Result<Self> {
+    pub fn register<M: ThisModule>(drivers: Pin<&'static mut [DriverVTable]>) -> Result<Self> {
         if drivers.is_empty() {
             return Err(code::EINVAL);
         }
@@ -659,7 +656,11 @@ pub fn register(
         // the `drivers` slice are initialized properly. `drivers` will not be moved.
         // So it's just an FFI call.
         to_result(unsafe {
-            bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
+            bindings::phy_drivers_register(
+                drivers[0].0.get(),
+                drivers.len().try_into()?,
+                M::OWNER.as_ptr(),
+            )
         })?;
         // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
         Ok(Registration { drivers })
@@ -891,12 +892,11 @@ struct Module {
                 [$($crate::net::phy::create_phy_driver::<$driver>()),+];
 
             impl $crate::Module for Module {
-                fn init(module: &'static $crate::ThisModule) -> Result<Self> {
+                fn init<M: $crate::ThisModule>() -> Result<Self> {
                     // SAFETY: The anonymous constant guarantees that nobody else can access
                     // the `DRIVERS` static. The array is used only in the C side.
                     let drivers = unsafe { &mut DRIVERS };
-                    let mut reg = $crate::net::phy::Registration::register(
-                        module,
+                    let mut reg = $crate::net::phy::Registration::register::<M>(
                         ::core::pin::Pin::static_mut(drivers),
                     )?;
                     Ok(Module { _reg: reg })
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 82e128431f08..792560ca8020 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -20,7 +20,6 @@
     prelude::*,
     str::CStr,
     types::Opaque,
-    ThisModule, //
 };
 use core::{
     marker::PhantomData,
@@ -55,11 +54,7 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::pci_driver;
 
-    unsafe fn register(
-        pdrv: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result {
+    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
         // SAFETY: It's safe to set the fields of `struct pci_driver` on initialization.
         unsafe {
             (*pdrv.get()).name = name.as_char_ptr();
@@ -70,7 +65,7 @@ unsafe fn register(
 
         // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr())
+            bindings::__pci_register_driver(pdrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
         })
     }
 
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index ed889f079cab..67d46231600e 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -14,7 +14,6 @@
     of,
     prelude::*,
     types::Opaque,
-    ThisModule,
 };
 
 use core::{
@@ -31,11 +30,7 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::platform_driver;
 
-    unsafe fn register(
-        pdrv: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result {
+    unsafe fn register<M: ThisModule>(pdrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
         let of_table = match T::OF_ID_TABLE {
             Some(table) => table.as_ptr(),
             None => core::ptr::null(),
@@ -56,7 +51,7 @@ unsafe fn register(
         }
 
         // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
-        to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
+        to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), M::OWNER.as_ptr()) })
     }
 
     unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index d10b65e9fb6a..c6ee98d12875 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -13,7 +13,6 @@
     prelude::*,
     str::CStr,
     types::{AlwaysRefCounted, Opaque},
-    ThisModule,
 };
 use core::{
     marker::PhantomData,
@@ -32,11 +31,7 @@
 unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
     type RegType = bindings::usb_driver;
 
-    unsafe fn register(
-        udrv: &Opaque<Self::RegType>,
-        name: &'static CStr,
-        module: &'static ThisModule,
-    ) -> Result {
+    unsafe fn register<M: ThisModule>(udrv: &Opaque<Self::RegType>, name: &'static CStr) -> Result {
         // SAFETY: It's safe to set the fields of `struct usb_driver` on initialization.
         unsafe {
             (*udrv.get()).name = name.as_char_ptr();
@@ -47,7 +42,7 @@ unsafe fn register(
 
         // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
         to_result(unsafe {
-            bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr())
+            bindings::usb_register_driver(udrv.get(), M::OWNER.as_ptr(), name.as_char_ptr())
         })
     }
 
diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs
index 0ccc7553ef39..858b2a45238d 100644
--- a/samples/rust/rust_configfs.rs
+++ b/samples/rust/rust_configfs.rs
@@ -42,7 +42,7 @@ fn new() -> impl PinInit<Self, Error> {
 }
 
 impl kernel::InPlaceModule for RustConfigfs {
-    fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+    fn init<M: ThisModule>() -> impl PinInit<Self, Error> {
         pr_info!("Rust configfs sample (init)\n");
 
         // Define a subsystem with the data type `Configuration`, two
diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs
index 5761ea314f44..e996dca19454 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -111,10 +111,10 @@ struct SampleModule {
 }
 
 impl InPlaceModule for SampleModule {
-    fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
+    fn init<M: ThisModule>() -> impl PinInit<Self, Error> {
         try_pin_init!(Self {
-            _pci_driver <- driver::Registration::new(MODULE_NAME, module),
-            _aux_driver <- driver::Registration::new(MODULE_NAME, module),
+            _pci_driver <- driver::Registration::new::<M>(MODULE_NAME),
+            _aux_driver <- driver::Registration::new::<M>(MODULE_NAME),
         })
     }
 }
diff --git a/samples/rust/rust_driver_faux.rs b/samples/rust/rust_driver_faux.rs
index ecc9fd378cbd..f66452c0390c 100644
--- a/samples/rust/rust_driver_faux.rs
+++ b/samples/rust/rust_driver_faux.rs
@@ -17,7 +17,7 @@ struct SampleModule {
 }
 
 impl Module for SampleModule {
-    fn init(_module: &'static ThisModule) -> Result<Self> {
+    fn init<M: kernel::ThisModule>() -> Result<Self> {
         pr_info!("Initialising Rust Faux Device Sample\n");
 
         let reg = faux::Registration::new(c_str!("rust-faux-sample-device"), None)?;
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 8eb9583571d7..c3696e69d67b 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -23,7 +23,7 @@ struct RustMinimal {
 }
 
 impl kernel::Module for RustMinimal {
-    fn init(_module: &'static ThisModule) -> Result<Self> {
+    fn init<M: kernel::ThisModule>() -> Result<Self> {
         pr_info!("Rust minimal sample (init)\n");
         pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
         pr_info!(
diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs
index d69bc33dbd99..3f1acb6818a5 100644
--- a/samples/rust/rust_misc_device.rs
+++ b/samples/rust/rust_misc_device.rs
@@ -129,7 +129,7 @@ struct RustMiscDeviceModule {
 }
 
 impl kernel::InPlaceModule for RustMiscDeviceModule {
-    fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+    fn init<M: ThisModule>() -> impl PinInit<Self, Error> {
         pr_info!("Initialising Rust Misc Device Sample\n");
 
         let options = MiscDeviceOptions {
diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs
index 4095c72afeab..b60ff0683a0e 100644
--- a/samples/rust/rust_print_main.rs
+++ b/samples/rust/rust_print_main.rs
@@ -59,7 +59,7 @@ fn arc_dyn_print(arc: &Arc<dyn Display>) {
 }
 
 impl kernel::Module for RustPrint {
-    fn init(_module: &'static ThisModule) -> Result<Self> {
+    fn init<M: kernel::ThisModule>() -> Result<Self> {
         pr_info!("Rust printing macros sample (init)\n");
 
         pr_emerg!("Emergency message (level 0) without args\n");

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 2/6] rust: WIP: Introduce ThisModule trait and THIS_MODULE impl
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

---
 rust/kernel/lib.rs    | 61 +++++++++++++++++++++++++++++++++++----------------
 rust/macros/module.rs | 36 ++++++++++++++++++------------
 2 files changed, 64 insertions(+), 33 deletions(-)

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 69a798fbb563..224410745734 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -205,31 +205,54 @@ pub trait ModuleMetadata {
     const NAME: &'static crate::str::CStr;
 }
 
-/// Equivalent to `THIS_MODULE` in the C API.
-///
-/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)
-pub struct ThisModule(*mut bindings::module);
+pub mod this_module {
+    //! TODO
+    //!
+    //! # For driver cretors
+    //!
+    //! For each module we define own custom THIS_MODULE in
+    //! [`macros::module::module`]. Mechanism is equivalent to `THIS_MODULE` in
+    //! the [C API](srctree/include/linux/init.h).
+    //!
+    //! TODO
+    //!
+    //! # For abstraction creators
+    //!
+    //! TODO
 
-// SAFETY: `THIS_MODULE` may be used from all threads within a module.
-unsafe impl Sync for ThisModule {}
+    /// See [`this_module`]
+    pub trait ThisModule {
+        /// TODO Doc
+        const OWNER: ModuleWrapper;
+    }
 
-impl ThisModule {
-    /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer.
-    ///
-    /// # Safety
-    ///
-    /// The pointer must be equal to the right `THIS_MODULE`.
-    pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule {
-        ThisModule(ptr)
+    /// See [`this_module`]
+    pub struct ModuleWrapper {
+        ptr: *mut bindings::module,
     }
 
-    /// Access the raw pointer for this module.
-    ///
-    /// It is up to the user to use it correctly.
-    pub const fn as_ptr(&self) -> *mut bindings::module {
-        self.0
+    impl ModuleWrapper {
+        /// Get the raw pointer to the underlying `struct module`.
+        ///
+        /// TODO: Should be only available for kernel create.
+        pub const fn as_ptr(&self) -> *mut bindings::module {
+            self.ptr
+        }
+
+        /// Only meant to use in [`macros::module::module`].
+        ///
+        /// # Safety
+        ///
+        /// - Only modules are allowed to create non null `ModuleWrapper`s.
+        /// - The pointer must point to a valid `struct module` provided by the
+        ///   kernel.
+        #[doc(hidden)]
+        pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> Self {
+            ModuleWrapper { ptr }
+        }
     }
 }
+pub use this_module::*;
 
 #[cfg(not(testlib))]
 #[panic_handler]
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 80cb9b16f5aa..6b8753d122cc 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -371,20 +371,28 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
             /// Used by the printing macros, e.g. [`info!`].
             const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
 
-            // SAFETY: `__this_module` is constructed by the kernel at load time and will not be
-            // freed until the module is unloaded.
-            #[cfg(MODULE)]
-            static THIS_MODULE: ::kernel::ThisModule = unsafe {{
-                extern \"C\" {{
-                    static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
-                }}
+            /// THIS_MODULE for \"{name}\". See more at [`kernel::this_module`].
+            #[allow(non_camel_case_types)]
+            pub struct THIS_MODULE;
+
+            impl ::kernel::prelude::ThisModule for THIS_MODULE {{
+                #[cfg(not(MODULE))] 
+                const OWNER: ::kernel::this_module::ModuleWrapper = unsafe {{
+                    ::kernel::this_module::ModuleWrapper::from_ptr(::core::ptr::null_mut())
+                }};
 
-                ::kernel::ThisModule::from_ptr(__this_module.get())
-            }};
-            #[cfg(not(MODULE))]
-            static THIS_MODULE: ::kernel::ThisModule = unsafe {{
-                ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
-            }};
+                #[cfg(MODULE)]
+                // SAFETY:
+                // - `__this_module` is constructed by the kernel at module load time.
+                // - We use check that we are module so we can create non-null ModuleWrapper.
+                const OWNER: ::kernel::this_module::ModuleWrapper = unsafe {{
+                    extern \"C\" {{
+                        static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
+                    }}
+                    
+                    ::kernel::this_module::ModuleWrapper::from_ptr(__this_module.get())
+                }};
+            }}
 
             /// The `LocalModule` type is the type of the module created by `module!`,
             /// `module_pci_driver!`, `module_platform_driver!`, etc.
@@ -502,7 +510,7 @@ mod __module_init {{
                     /// This function must only be called once.
                     unsafe fn __init() -> ::kernel::ffi::c_int {{
                         let initer =
-                            <{type_} as ::kernel::InPlaceModule>::init(&super::super::THIS_MODULE);
+                            <{type_} as ::kernel::InPlaceModule>::init::<super::super::THIS_MODULE>();
                         // SAFETY: No data race, since `__MOD` can only be accessed by this module
                         // and there only `__init` and `__exit` access it. These functions are only
                         // called once and `__exit` cannot be called before or during `__init`.

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 1/6] rust: Enable const_refs_to_static feature
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg
In-Reply-To: <20260101-this_module_fix-v1-0-46ae3e5605a0@gmail.com>

Enable the const_refs_to_static Rust feature to allow taking
references to static items in const contexts. This is required for
using ThisModule when constructing static Rust structures.

The Rust support already relies on features available in Rust 1.83, and
const_refs_to_static has been available since Rust 1.78.

Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
---
 rust/kernel/lib.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f812cf120042..69a798fbb563 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -36,6 +36,7 @@
 #![feature(const_option)]
 #![feature(const_ptr_write)]
 #![feature(const_refs_to_cell)]
+#![feature(const_refs_to_static)]
 //
 // Expected to become stable.
 #![feature(arbitrary_self_types)]

-- 
2.43.0


^ permalink raw reply related

* [PATCH RFC 0/6] rust: Reimplement ThisModule to fix ownership problems
From: Kari Argillander @ 2026-01-01  5:20 UTC (permalink / raw)
  To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Alexandre Courbot
  Cc: Greg Kroah-Hartman, rust-for-linux, linux-kernel, linux-modules,
	Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
	Aaron Tomlin, Jens Axboe, Kari Argillander, Andreas Hindborg

This is RFC and just first patch is ready. First I wanna discuss if this
is right way to go. I also need to implement this so that build does not
break and do better splitting. There is missing signed-off-by's and
commit messages so no need to comment those kind of things. Feel free to
cc more people if needed.

So currently we have problem that we are not always filling .owner field
for file_operations. I think we can enable const_refs_to_static already
as that is in 1.78 and is stable in 1.83. So that fits perfecly for us.
This also seems to be quite request feature but I did not found that no
one has ever suggested that we just enable this.

So basic idea is that we will have ThisModule trait which is used kernel
side. Module side usually just pass this trait forward if needed. Or
they can use THIS_MODULE impl which is now completly private impl for
each module and kernel create does not see this. So we can think if we
wanna pass other module data which have through this. This could also be
used to give local module storage without using static.

There is also some of github issues for ThisModule handling:

> MiscDevice missing module reference counting causes use-after-free crashes [1].

This will be tested and fixed in this series

> Ensure ThisModule invariants hold

Yeap. Now it would be really hard for kernel create to create this by
accident. Binder is still using .as_ptr() but that is easily solvable
and after that we can make .as_ptr() private so it will be nice that
kernel create can just use .as_ptr() modules can just use.

> Add ThisModule::this_module()
> 
> Add a new function ThisModule::this_module() -> &'static ThisModule
> that is only available when MODULE is set. It should return a static
> reference to bindings::__this_module. [3]

I do not this is actually good idea. But here we take little bit
different approx so basically this is done also.

> Suggested that ThisModule should have name() parameter. [4]

And that was good idea. We can remove whole ModuleMetadata and usage is
lot nicer. It is also possible that we could have example trait PciModule.
which THIS_MODULE impl based on type but that is think for another time.

> Initialise the owner field in struct file_operations. [5]

Yeap. After this is totally possible to do everywhere. This series will
already address most of those.

[1]: https://github.com/Rust-for-Linux/linux/issues/1182
[2]: https://github.com/Rust-for-Linux/linux/issues/212
[3]: https://github.com/Rust-for-Linux/linux/issues/1176
[4]: https://github.com/Rust-for-Linux/linux/issues/720
[5]: https://github.com/Rust-for-Linux/linux/issues/720

To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun.feng@gmail.com>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Danilo Krummrich <dakr@kernel.org>

To: Alexandre Courbot <acourbot@nvidia.com>

Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Petr Pavlu <petr.pavlu@suse.com>
Cc: Daniel Gomez <da.gomez@kernel.org>
Cc: Sami Tolvanen <samitolvanen@google.com>
Cc: Aaron Tomlin <atomlin@atomlin.com>

Cc: Andreas Hindborg <a.hindborg@samsung.com>
Cc: Jens Axboe <axboe@kernel.dk>

Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
---
Kari Argillander (6):
      rust: Enable const_refs_to_static feature
      rust: WIP: Introduce ThisModule trait and THIS_MODULE impl
      rust: WIP: use ThisModule trait for initializing
      rust: WIP: use ThisModule trait to fix some missing owners
      rust: debugfs: WIP: Use owner for file_operations
      rust: WIP: Replace ModuleMetadata with THIS_MODULE

 drivers/android/binder/rust_binder_main.rs |  4 +-
 drivers/block/rnull/configfs.rs            |  2 +-
 drivers/block/rnull/rnull.rs               |  3 +-
 drivers/gpu/drm/nova/driver.rs             |  1 +
 drivers/gpu/drm/tyr/driver.rs              |  1 +
 drivers/gpu/nova-core/nova_core.rs         |  2 +-
 lib/find_bit_benchmark_rust.rs             |  2 +-
 rust/kernel/auxiliary.rs                   | 15 +++---
 rust/kernel/block/mq/gen_disk.rs           | 31 ++----------
 rust/kernel/block/mq/operations.rs         | 12 +++++
 rust/kernel/configfs.rs                    | 19 ++++---
 rust/kernel/debugfs.rs                     | 79 ++++++++++++++++--------------
 rust/kernel/debugfs/file_ops.rs            | 50 ++++++++++++++-----
 rust/kernel/driver.rs                      | 19 ++-----
 rust/kernel/drm/device.rs                  |  2 +-
 rust/kernel/drm/driver.rs                  |  3 ++
 rust/kernel/drm/gem/mod.rs                 |  4 +-
 rust/kernel/firmware.rs                    |  2 +-
 rust/kernel/i2c.rs                         | 10 ++--
 rust/kernel/lib.rs                         | 78 ++++++++++++++++++-----------
 rust/kernel/miscdevice.rs                  |  4 ++
 rust/kernel/net/phy.rs                     | 16 +++---
 rust/kernel/pci.rs                         | 11 ++---
 rust/kernel/platform.rs                    | 11 ++---
 rust/kernel/usb.rs                         | 11 ++---
 rust/macros/module.rs                      | 43 +++++++++-------
 samples/rust/rust_configfs.rs              |  2 +-
 samples/rust/rust_debugfs.rs               |  6 +--
 samples/rust/rust_driver_auxiliary.rs      |  8 +--
 samples/rust/rust_driver_faux.rs           |  2 +-
 samples/rust/rust_minimal.rs               |  2 +-
 samples/rust/rust_misc_device.rs           |  3 +-
 samples/rust/rust_print_main.rs            |  2 +-
 33 files changed, 245 insertions(+), 215 deletions(-)
---
base-commit: cc3aa43b44bdb43dfbac0fcb51c56594a11338a8
change-id: 20251230-this_module_fix-a390bff24897

Best regards,
-- 
Kari Argillander <kari.argillander@gmail.com>


^ permalink raw reply

* Re: [PATCH] module: show module version directly in print_modules()
From: Yafang Shao @ 2025-12-31  9:45 UTC (permalink / raw)
  To: Aaron Tomlin; +Cc: Petr Pavlu, mcgrof, da.gomez, samitolvanen, linux-modules
In-Reply-To: <ydmepfnm647kpwuiv5a4grvvdmuntcmxknowa3nf6hnx4unlj5@5ne25xb7e46k>

On Wed, Dec 31, 2025 at 12:10 AM Aaron Tomlin <atomlin@atomlin.com> wrote:
>
> On Tue, Dec 30, 2025 at 10:12:09PM +0800, Yafang Shao wrote:
> > > As mentioned, most in-tree modules do not specify an explicit version,
> > > so in terms of bloating the information about loaded modules, the patch
> > > should have minimal impact in practice. Alternatively, the version
> > > information could be printed only for external modules.
> >
> > Good suggestion.
> > I believe it’s sufficient to print only for external modules.
> >
> > Does the following change look good to you?
> >
> > --- a/kernel/module/main.c
> > +++ b/kernel/module/main.c
> > @@ -3901,7 +3901,10 @@ void print_modules(void)
> >         list_for_each_entry_rcu(mod, &modules, list) {
> >                 if (mod->state == MODULE_STATE_UNFORMED)
> >                         continue;
> > -               pr_cont(" %s%s", mod->name, module_flags(mod, buf, true));
> > +               pr_cont(" %s", mod->name);
> > +               if (mod->version && test_bit(TAINT_OOT_MUDLE, &mod->taints))
> > +                       pr_cont("-%s", mod->version);
> > +               pr_cont("%s", module_flags(mod, buf, true));
> >         }
> >
> >         print_unloaded_tainted_modules();
> >
>
> Hi Yafang,
>
>
> This refined approach is significantly more palatable and addresses the
> primary concerns regarding log bloat. By gating the version output behind
> the TAINT_OOT_MODULE bit, we strike an excellent balance between
> operational necessity and kernel log cleanliness.
>
> From a maintenance perspective, this is a much "tidier" solution. In-tree
> modules are tied to the specific kernel version already, so printing their
> versions is redundant. However, for external drivers (like proprietary
> networking or GPU stacks), the version is the single most critical piece of
> metadata for triage.
>
> The logic is sound, though there is a minor typo in the bit name that will
> cause a build failure. Here is the corrected implementation:
>
> @@ -3901,7 +3901,10 @@ void print_modules(void)
>         list_for_each_entry_rcu(mod, &modules, list) {
>                 if (mod->state == MODULE_STATE_UNFORMED)
>                         continue;
> -               pr_cont(" %s%s", mod->name, module_flags(mod, buf, true));
> +               pr_cont(" %s", mod->name);
> +               /* Only append version for out-of-tree modules */
> +               if (mod->version && test_bit(TAINT_OOT_MODULE, &mod->taints))
> +                       pr_cont("-%s", mod->version);
> +               pr_cont("%s", module_flags(mod, buf, true));
>         }
>
>         print_unloaded_tainted_modules();
>
>
> Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>

Thanks for your review.
v2 is sent: https://lore.kernel.org/linux-modules/20251231094004.37851-1-laoar.shao@gmail.com/


-- 
Regards
Yafang

^ permalink raw reply

* [PATCH v2] module: print version for external modules in print_modules()
From: Yafang Shao @ 2025-12-31  9:40 UTC (permalink / raw)
  To: mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin
  Cc: linux-modules, Yafang Shao

We maintain a vmcore analysis script on each server that automatically
parses /var/crash/XXXX/vmcore-dmesg.txt to categorize vmcores. This helps
us save considerable effort by avoiding analysis of known bugs.

For vmcores triggered by a driver bug, the system calls print_modules() to
list the loaded modules. However, print_modules() does not output module
version information. Across a large fleet of servers, there are often many
different module versions running simultaneously, and we need to know which
driver version caused a given vmcore.

Currently, the only reliable way to obtain the module version associated
with a vmcore is to analyze the /var/crash/XXXX/vmcore file itself—an
operation that is resource-intensive. Therefore, we propose printing the
driver version directly in the log, which is far more efficient.

The motivation behind this change is that the external NVIDIA driver
[0] frequently causes kernel panics across our server fleet.
While we continuously upgrade to newer NVIDIA driver versions,
upgrading the entire fleet is time-consuming. Therefore, we need to
identify which driver version is responsible for each panic.

In-tree modules are tied to the specific kernel version already, so
printing their versions is redundant. However, for external drivers (like
proprietary networking or GPU stacks), the version is the single most
critical piece of metadata for triage. Therefore, to avoid bloating the
information about loaded modules, we only print the version for external
modules.

- Before this patch

  Modules linked in: mlx5_core(O) nvidia(PO) nvme_core

- After this patch

  Modules linked in: mlx5_core-5.8-2.0.3(O) nvidia-535.274.02(PO) nvme_core
                              ^^^^^^^^^^          ^^^^^^^^^^^

  Note: nvme_core is a in-tree module[1], so its version isn't printed.

Link: https://github.com/NVIDIA/open-gpu-kernel-modules/tags [0]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/nvme/host/core.c?h=v6.19-rc3#n5448 [1]
Suggested-by: Petr Pavlu <petr.pavlu@suse.com>
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
---
 kernel/module/main.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

---
v1->v2: 
- print it for external module only (Petr, Aaron)
- add comment for it (Aaron)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 710ee30b3bea..16263ce23e92 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3901,7 +3901,11 @@ void print_modules(void)
 	list_for_each_entry_rcu(mod, &modules, list) {
 		if (mod->state == MODULE_STATE_UNFORMED)
 			continue;
-		pr_cont(" %s%s", mod->name, module_flags(mod, buf, true));
+		pr_cont(" %s", mod->name);
+		/* Only append version for out-of-tree modules */
+		if (mod->version && test_bit(TAINT_OOT_MODULE, &mod->taints))
+			pr_cont("-%s", mod->version);
+		pr_cont("%s", module_flags(mod, buf, true));
 	}
 
 	print_unloaded_tainted_modules();
-- 
2.43.5


^ permalink raw reply related

* Re: [RFC PATCH v1] module: Fix kernel panic when a symbol st_shndx is out of bounds
From: Alexei Starovoitov @ 2025-12-30 18:54 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Nathan Chancellor, Yonghong Song, Luis Chamberlain, Petr Pavlu,
	Daniel Gomez, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, LKML,
	linux-modules, bpf, Linux Kbuild mailing list, clang-built-linux
In-Reply-To: <6f845383-563e-49a7-941c-03e9db6158cc@linux.dev>

On Tue, Dec 30, 2025 at 10:45 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 12/29/25 4:50 PM, Alexei Starovoitov wrote:
> > On Mon, Dec 29, 2025 at 4:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> On 12/29/25 1:29 PM, Nathan Chancellor wrote:
> >>> Hi Ihor,
> >>>
> >>> On Mon, Dec 29, 2025 at 12:40:10PM -0800, Ihor Solodrai wrote:
> >>>> I think the simplest workaround is this one: use objcopy from binutils
> >>>> instead of llvm-objcopy when doing --update-section.
> >>>>
> >>>> There are just 3 places where that happens, so the OBJCOPY
> >>>> substitution is going to be localized.
> >>>>
> >>>> Also binutils is a documented requirement for compiling the kernel,
> >>>> whether with clang or not [1].
> >>>>
> >>>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/changes.rst?h=v6.18#n29
> >>>
> >>> This would necessitate always specifying a CROSS_COMPILE variable when
> >>> cross compiling with LLVM=1, which I would really like to avoid. The
> >>> LLVM variants have generally been drop in substitutes for several
> >>> versions now so some groups such as Android may not even have GNU
> >>> binutils installed in their build environment (see a recent build
> >>> fix [1]).
> >>>
> >>> I would much prefer detecting llvm-objcopy in Kconfig (such as by
> >>> creating CONFIG_OBJCOPY_IS_LLVM using the existing check for
> >>> llvm-objcopy in X86_X32_ABI in arch/x86/Kconfig) and requiring a working
> >>> copy (>= 22.0.0 presuming the fix is soon merged) or an explicit opt
> >>> into GNU objcopy via OBJCOPY=...objcopy for CONFIG_DEBUG_INFO_BTF to be
> >>> selectable.
> >>
> >> I like the idea of opt into GNU objcopy, however I think we should
> >> avoid requiring kbuilds that want CONFIG_DEBUG_INFO_BTF to change any
> >> configuration (such as adding an explicit OBJCOPY= in a build command).
> >>
> >> I drafted a patch (pasted below), introducing BTF_OBJCOPY which
> >> defaults to GNU objcopy. This implements the workaround, and should be
> >> easy to update with a LLVM version check later after the bug is fixed.
> >>
> >> This bit:
> >>
> >> @@ -391,6 +391,7 @@ config DEBUG_INFO_BTF
> >>         depends on PAHOLE_VERSION >= 122
> >>         # pahole uses elfutils, which does not have support for Hexagon relocations
> >>         depends on !HEXAGON
> >> +       depends on $(success,command -v $(BTF_OBJCOPY))
> >>
> >> Will turn off DEBUG_INFO_BTF if relevant GNU objcopy happens to not be
> >> installed.
> >>
> >> However I am not sure this is the right way to fail here. Because if
> >> the kernel really does need BTF (which is effectively all kernels
> >> using BPF), then we are breaking them anyways just downstream of the
> >> build.
> >>
> >> An "objcopy: command not found" might make some pipelines red, but it
> >> is very clear how to address.
> >>
> >> Thoughts?
> >>
> >>
> >> From 7c3b9cce97cc76d0365d8948b1ca36c61faddde3 Mon Sep 17 00:00:00 2001
> >> From: Ihor Solodrai <ihor.solodrai@linux.dev>
> >> Date: Mon, 29 Dec 2025 15:49:51 -0800
> >> Subject: [PATCH] BTF_OBJCOPY
> >>
> >> ---
> >>  Makefile                             |  6 +++++-
> >>  lib/Kconfig.debug                    |  1 +
> >>  scripts/gen-btf.sh                   | 10 +++++-----
> >>  scripts/link-vmlinux.sh              |  2 +-
> >>  tools/testing/selftests/bpf/Makefile |  4 ++--
> >>  5 files changed, 14 insertions(+), 9 deletions(-)
> >
> > All the makefile hackery looks like overkill and wrong direction.
> >
> > What's wrong with kernel/module/main.c change?
> >
> > Module loading already does a bunch of sanity checks for ELF
> > in elf_validity_cache_copy().
> >
> > + if (sym[i].st_shndx >= info->hdr->e_shnum)
> > is just one more.
> >
> > Maybe it can be moved to elf_validity*() somewhere,
> > but that's a minor detail.
> >
> > iiuc llvm-objcopy affects only bpf testmod, so not a general
> > issue that needs top level makefile changes.
>
> By the way, we don't have to put BTF_OBJCOPY variable in the top level
> Makefile.  It can be defined in Makefile.btf, which is included only
> with CONFIG_DEBUG_INFO_BTF=y
>
> We have to define BTF_OBJCOPY in the top-level makefile *if* we want
> CONFIG_DEBUG_INFO_BTF to depend on it, and get disabled if BTF_OBJCOPY
> is not set/available.
>
> I was trying to address Nathan's concern, that some kernel build
> environments might not have GNU binutils installed, and kconfig should
> detect that.  IMO putting BTF_OBJCOPY in Makefile.btf is more
> appropriate, assuming the BTF_OBJCOPY variable is at all an acceptable
> workaround for the llvm-objcopy bug.

I feel that fallback to binutils objcopy is going to have its own
issues. It could have issues with cross compiling too.
Asking developers to setup cross compile with llvm toolchain and binutils
at the same time is imo too much.
If we cannot rely on objcopy then resolve_btfids should do the whole thing.

^ permalink raw reply

* Re: [RFC PATCH v1] module: Fix kernel panic when a symbol st_shndx is out of bounds
From: Ihor Solodrai @ 2025-12-30 18:44 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Nathan Chancellor, Yonghong Song, Luis Chamberlain, Petr Pavlu,
	Daniel Gomez, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, LKML,
	linux-modules, bpf, Linux Kbuild mailing list, clang-built-linux
In-Reply-To: <CAADnVQ+X-a92LEgcd-HjTJUcw2zR_jtUmD9U-Z6OtNnvpVwfiw@mail.gmail.com>

On 12/29/25 4:50 PM, Alexei Starovoitov wrote:
> On Mon, Dec 29, 2025 at 4:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> On 12/29/25 1:29 PM, Nathan Chancellor wrote:
>>> Hi Ihor,
>>>
>>> On Mon, Dec 29, 2025 at 12:40:10PM -0800, Ihor Solodrai wrote:
>>>> I think the simplest workaround is this one: use objcopy from binutils
>>>> instead of llvm-objcopy when doing --update-section.
>>>>
>>>> There are just 3 places where that happens, so the OBJCOPY
>>>> substitution is going to be localized.
>>>>
>>>> Also binutils is a documented requirement for compiling the kernel,
>>>> whether with clang or not [1].
>>>>
>>>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/changes.rst?h=v6.18#n29
>>>
>>> This would necessitate always specifying a CROSS_COMPILE variable when
>>> cross compiling with LLVM=1, which I would really like to avoid. The
>>> LLVM variants have generally been drop in substitutes for several
>>> versions now so some groups such as Android may not even have GNU
>>> binutils installed in their build environment (see a recent build
>>> fix [1]).
>>>
>>> I would much prefer detecting llvm-objcopy in Kconfig (such as by
>>> creating CONFIG_OBJCOPY_IS_LLVM using the existing check for
>>> llvm-objcopy in X86_X32_ABI in arch/x86/Kconfig) and requiring a working
>>> copy (>= 22.0.0 presuming the fix is soon merged) or an explicit opt
>>> into GNU objcopy via OBJCOPY=...objcopy for CONFIG_DEBUG_INFO_BTF to be
>>> selectable.
>>
>> I like the idea of opt into GNU objcopy, however I think we should
>> avoid requiring kbuilds that want CONFIG_DEBUG_INFO_BTF to change any
>> configuration (such as adding an explicit OBJCOPY= in a build command).
>>
>> I drafted a patch (pasted below), introducing BTF_OBJCOPY which
>> defaults to GNU objcopy. This implements the workaround, and should be
>> easy to update with a LLVM version check later after the bug is fixed.
>>
>> This bit:
>>
>> @@ -391,6 +391,7 @@ config DEBUG_INFO_BTF
>>         depends on PAHOLE_VERSION >= 122
>>         # pahole uses elfutils, which does not have support for Hexagon relocations
>>         depends on !HEXAGON
>> +       depends on $(success,command -v $(BTF_OBJCOPY))
>>
>> Will turn off DEBUG_INFO_BTF if relevant GNU objcopy happens to not be
>> installed.
>>
>> However I am not sure this is the right way to fail here. Because if
>> the kernel really does need BTF (which is effectively all kernels
>> using BPF), then we are breaking them anyways just downstream of the
>> build.
>>
>> An "objcopy: command not found" might make some pipelines red, but it
>> is very clear how to address.
>>
>> Thoughts?
>>
>>
>> From 7c3b9cce97cc76d0365d8948b1ca36c61faddde3 Mon Sep 17 00:00:00 2001
>> From: Ihor Solodrai <ihor.solodrai@linux.dev>
>> Date: Mon, 29 Dec 2025 15:49:51 -0800
>> Subject: [PATCH] BTF_OBJCOPY
>>
>> ---
>>  Makefile                             |  6 +++++-
>>  lib/Kconfig.debug                    |  1 +
>>  scripts/gen-btf.sh                   | 10 +++++-----
>>  scripts/link-vmlinux.sh              |  2 +-
>>  tools/testing/selftests/bpf/Makefile |  4 ++--
>>  5 files changed, 14 insertions(+), 9 deletions(-)
> 
> All the makefile hackery looks like overkill and wrong direction.
> 
> What's wrong with kernel/module/main.c change?
> 
> Module loading already does a bunch of sanity checks for ELF
> in elf_validity_cache_copy().
> 
> + if (sym[i].st_shndx >= info->hdr->e_shnum)
> is just one more.
> 
> Maybe it can be moved to elf_validity*() somewhere,
> but that's a minor detail.
> 
> iiuc llvm-objcopy affects only bpf testmod, so not a general
> issue that needs top level makefile changes.

By the way, we don't have to put BTF_OBJCOPY variable in the top level
Makefile.  It can be defined in Makefile.btf, which is included only
with CONFIG_DEBUG_INFO_BTF=y

We have to define BTF_OBJCOPY in the top-level makefile *if* we want
CONFIG_DEBUG_INFO_BTF to depend on it, and get disabled if BTF_OBJCOPY
is not set/available.

I was trying to address Nathan's concern, that some kernel build
environments might not have GNU binutils installed, and kconfig should
detect that.  IMO putting BTF_OBJCOPY in Makefile.btf is more
appropriate, assuming the BTF_OBJCOPY variable is at all an acceptable
workaround for the llvm-objcopy bug.




^ permalink raw reply

* [PATCH] module: Fix kernel panic when a symbol st_shndx is out of bounds
From: Ihor Solodrai @ 2025-12-30 18:32 UTC (permalink / raw)
  To: Luis Chamberlain, Petr Pavlu, Daniel Gomez, Nathan Chancellor,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Eduard Zingerman
  Cc: linux-kernel, linux-modules, bpf, linux-kbuild, llvm

The module loader doesn't check for bounds of the ELF section index in
simplify_symbols():

       for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
		const char *name = info->strtab + sym[i].st_name;

		switch (sym[i].st_shndx) {
		case SHN_COMMON:

		[...]

		default:
			/* Divert to percpu allocation if a percpu var. */
			if (sym[i].st_shndx == info->index.pcpu)
				secbase = (unsigned long)mod_percpu(mod);
			else
  /** HERE --> **/		secbase = info->sechdrs[sym[i].st_shndx].sh_addr;
			sym[i].st_value += secbase;
			break;
		}
	}

A symbol with an out-of-bounds st_shndx value, for example 0xffff
(known as SHN_XINDEX or SHN_HIRESERVE), may cause a kernel panic:

  BUG: unable to handle page fault for address: ...
  RIP: 0010:simplify_symbols+0x2b2/0x480
  ...
  Kernel panic - not syncing: Fatal exception

This can happen when module ELF is legitimately using SHN_XINDEX or
when it is corrupted.

Add a bounds check in simplify_symbols() to validate that st_shndx is
within the valid range before using it.

This issue was discovered due to a bug in llvm-objcopy, see relevant
discussion for details [1].

[1] https://lore.kernel.org/linux-modules/20251224005752.201911-1-ihor.solodrai@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 kernel/module/main.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 710ee30b3bea..5bf456fad63e 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1568,6 +1568,13 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
 			break;
 
 		default:
+			if (sym[i].st_shndx >= info->hdr->e_shnum) {
+				pr_err("%s: Symbol %s has an invalid section index %u (max %u)\n",
+				       mod->name, name, sym[i].st_shndx, info->hdr->e_shnum - 1);
+				ret = -ENOEXEC;
+				break;
+			}
+
 			/* Divert to percpu allocation if a percpu var. */
 			if (sym[i].st_shndx == info->index.pcpu)
 				secbase = (unsigned long)mod_percpu(mod);
-- 
2.52.0


^ permalink raw reply related

* Re: [PATCH v4 7/7] kernel.h: drop trace_printk.h
From: Steven Rostedt @ 2025-12-30 16:46 UTC (permalink / raw)
  To: Yury Norov
  Cc: Mathieu Desnoyers, Andy Shevchenko, Andrew Morton,
	Masami Hiramatsu, Christophe Leroy, Randy Dunlap, Ingo Molnar,
	Jani Nikula, Joonas Lahtinen, David Laight, Petr Pavlu,
	Andi Shyti, Rodrigo Vivi, Tvrtko Ursulin, Daniel Gomez,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	linux-kernel, intel-gfx, dri-devel, linux-modules,
	linux-trace-kernel
In-Reply-To: <aVP7XVtYwb4YV9gy@yury>

On Tue, 30 Dec 2025 11:18:37 -0500
Yury Norov <yury.norov@gmail.com> wrote:

> On Tue, Dec 30, 2025 at 09:21:00AM -0500, Mathieu Desnoyers wrote:
> > On 2025-12-30 03:55, Andy Shevchenko wrote:  
> > > On Mon, Dec 29, 2025 at 05:25:08PM -0500, Mathieu Desnoyers wrote:
> > > 
> > > ...
> > >   
> > > > One possible compromise would be to move it to its own header file,
> > > > and introduce a CONFIG_TRACE_PRINTK Kconfig option (default Y) that
> > > > would surround an include from linux/kernel.h with a preprocessor
> > > > conditional.  
> 
> We already have CONFIG_TRACING, and everything in the new
> trace_printk.h is conditional on it. We can protect the header in
> kernel.h with the same config.

Tracing is used in production all the time. So I think we can have a new
config just for trace_printk(). I was actually thinking of adding a
CONFIG_HIDE_TRACE_PRINTK, with the description of:

  trace_printk() is an extremely powerful utility to debug and develop
  kernel code. It is defined in kernel.h so that it can be easily accessed
  during development or having to debug existing code.

  But trace_printk() is not to be included in the final result, and having
  it in kernel.h during normal builds where the builder has no plans of
  debugging the kernel causes wasted cycles and time in compiling the kernel.

  By saying yes here, the include of trace_printk() macros will be hidden
  from kernel.h and help speed up the compile.

  If you do not plan on debugging this kernel, say Y

And then have in kernel.h:

#ifndef CONFIG_HIDE_TRACE_PRINTK
# include <linux/trace_printk.h>
#endif

This also means it gets set for allyesconfig builds, which I doubt anyone
wants to debug anyway.

> 
> > > > But please make sure the default stays as it is today:
> > > > include the trace printk header by default.  
> > > 
> > > "by default" where exactly?  
> 
> Seemingly nowhere.
> 
> > > The problem is that kernel.h is a total mess and
> > > it's included in a lot of mysterious ways (indirectly),  
> 
> Yes!
> 
> > > and in C you _must_
> > > include a header anyway for a custom API, just define *which* one.  
> >
> > This patch series moves the guts of trace_printk into its own header
> > file, which reduces clutter. So that's already progress. :)
> >   
> > > 
> > > Based on the Steven's first replies I see a compromise in having it inside
> > > printk.h. If you want to debug something with printf() (in general) the same
> > > header should provide all species. Do you agree?  
>  
> It may sound logical, but I don't like this idea. Printk() is used
> for debugging by everyone, but its main goal is to communicate to
> userspace and between different parts of the kernel. Notice how all
> debugging and development API in linux/pritnk.h is protected with the
> corresponding ifdefery. 
> 
> Contrary to that, trace_printk() is a purely debugging feature. There's
> no use for it after the debugging is done. (Or I missed something?)

I actually agree with you here. I don't think adding trace_printk.h into
printk.h is appropriate. I only said that anywhere you can add a printk()
for debugging, you should also be able to add trace_printk(). I believe
kernel.h is the appropriate place for both.

> 
> Everyone admits that kernel.h is a mess. Particularly, it's a mess of
> development and production features. So, moving trace_printk() from an
> already messy kernel.h to a less messy printk.h - to me it looks like
> spreading the mess.
> 
> > I don't have a strong opinion about including trace_printk.h from either
> > kernel.h or printk.h. As long as it's still included by a default kernel
> > config the same way it has been documented/used since 2009.  
> 
> Can you please point to the documentation and quote the exact piece
> stating that? Git history points to the commit 40ada30f9621f from Ingo
> that decouples tracers from DEBUG_KERNEL, and the following 422d3c7a577
> from Kosaki that force-enables the new TRACING_SUPPORT regardless of
> the DEBUG_KERNEL state.
> 
> To me, decoupling tracing from DEBUG_KERNEL looks accidental rather than
> intentional. So maybe simply restore that dependency?

Absolutely not. Tracing is used to debug production kernels, and things
like live kernel patching also depend on it, not to mention BPF.

> 
> Currently, even with tinyconfig, DEBUG_KERNEL is enabled (via EXPERT).
> And even if EXPERT and DEBUG_KERNEL are off, tracers are still enabled.
> This doesn't look right...

Looks fine to me.

-- Steve

^ permalink raw reply


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