* [PATCH v8 3/3] keys: asymmetric: tpm2_asymmetric
From: Jarkko Sakkinen @ 2026-05-24 5:15 UTC (permalink / raw)
To: keyrings
Cc: David Howells, linux-crypto, linux-integrity, David Woodhouse,
James Bottomley, Stefan Berger, Herbert Xu, Jarkko Sakkinen,
James Prestwood, Lukas Wunner, Ignat Korchagin, David S. Miller,
Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org>
tpm2_asymmetric is a key type for external keys generated outside the TPM
chip but later imported to the chip's key hierarchy as leaf keys.
The key type supports ECC-NIST-P256/384/521 and RSA keys and provides
signing and verification operations for each. In addition, for RSA
encryption and decryption operations are supported.
Co-developed-by: James Prestwood <prestwoj@gmail.com>
Signed-off-by: James Prestwood <prestwoj@gmail.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
crypto/asymmetric_keys/Kconfig | 17 +
crypto/asymmetric_keys/Makefile | 1 +
crypto/asymmetric_keys/tpm2_asymmetric.c | 1096 ++++++++++++++++++++++
include/linux/tpm.h | 10 +
4 files changed, 1124 insertions(+)
create mode 100644 crypto/asymmetric_keys/tpm2_asymmetric.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index e50bd9b3e27b..a93e13d5768f 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -15,6 +15,7 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select MPILIB
select CRYPTO_HASH_INFO
select CRYPTO_AKCIPHER
+ select CRYPTO_RSA
select CRYPTO_SIG
select CRYPTO_HASH
help
@@ -23,6 +24,22 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
appropriate hash algorithms (such as SHA-1) must be available.
ENOPKG will be reported if the requisite algorithm is unavailable.
+config ASYMMETRIC_TPM2_KEY_SUBTYPE
+ tristate "Asymmetric TPM2 crypto algorithm subtype"
+ depends on TCG_TPM
+ select CRYPTO_SHA256
+ select CRYPTO_HASH_INFO
+ select CRYPTO_TPM2_KEY
+ select ASN1
+ select ASN1_ENCODER
+ help
+ This option provides support for asymmetric TPM2 key type handling.
+ Asymmetric operations such as sign and verify are delegated to the
+ TPM, and bound to the kernel crypto subsystem. Both RSA and ECDSA
+ keys are supported.
+
+ ENOPKG will be reported if the requisite algorithm is unavailable.
+
config X509_CERTIFICATE_PARSER
tristate "X.509 certificate parser"
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index bc65d3b98dcb..c83b40d021ac 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -11,6 +11,7 @@ asymmetric_keys-y := \
signature.o
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_ASYMMETRIC_TPM2_KEY_SUBTYPE) += tpm2_asymmetric.o
#
# X.509 Certificate handling
diff --git a/crypto/asymmetric_keys/tpm2_asymmetric.c b/crypto/asymmetric_keys/tpm2_asymmetric.c
new file mode 100644
index 000000000000..f6598e6fd283
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm2_asymmetric.c
@@ -0,0 +1,1096 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * An asymmetric TPM2 key subtype.
+ */
+
+#include <crypto/hash_info.h>
+#include <crypto/internal/ecc.h>
+#include <crypto/public_key.h>
+#include <crypto/tpm2_key.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/asymmetric-subtype.h>
+#include <linux/asn1_encoder.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tpm.h>
+#include <linux/unaligned.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "tpm2_asymmetric: "fmt
+
+/* TPM2 Structures 12.2.3.5: TPMS_RSA_PARMS */
+struct tpm2_asymmetric_rsa_parms {
+ __be16 symmetric;
+ __be16 scheme;
+ __be16 key_bits;
+ __be32 exponent;
+ __be16 modulus_size;
+} __packed;
+
+/* TPM2 Structures 12.2.3.6: TPMS_ECC_PARMS */
+struct tpm2_asymmetric_ecc_parms {
+ __be16 symmetric;
+ __be16 scheme;
+ __be16 ecc;
+ __be16 kdf;
+};
+
+static const void *tpm2_asymmetric_parms(const struct tpm2_key *key)
+{
+ return &key->data[key->priv_len + 2 + sizeof(*key->desc)];
+}
+
+static u16 tpm2_asymmetric_rsa_mod_size(const struct tpm2_key *key)
+{
+ const struct tpm2_asymmetric_rsa_parms *p = tpm2_asymmetric_parms(key);
+
+ return be16_to_cpu(p->modulus_size);
+}
+
+static const u8 *tpm2_asymmetric_ecc_x(const struct tpm2_key *key)
+{
+ return tpm2_asymmetric_parms(key) + sizeof(struct tpm2_asymmetric_ecc_parms);
+}
+
+static const u8 *tpm2_asymmetric_ecc_y(const struct tpm2_key *key)
+{
+ const u8 *x = tpm2_asymmetric_ecc_x(key);
+ u16 x_size = get_unaligned_be16(&x[0]);
+
+ return &x[2 + x_size];
+}
+
+static unsigned int tpm2_asymmetric_ecc_key_bits(u16 ecc)
+{
+ switch (ecc) {
+ case TPM2_ECC_NIST_P256:
+ return 256;
+ case TPM2_ECC_NIST_P384:
+ return 384;
+ case TPM2_ECC_NIST_P521:
+ return 521;
+ default:
+ return 0;
+ }
+}
+
+static int tpm2_asymmetric_hash_lookup(const char *hash_algo,
+ int *hash_id, int *tpm_hash)
+{
+ int id, alg;
+
+ if (!hash_algo)
+ return -EINVAL;
+
+ id = match_string(hash_algo_name, HASH_ALGO__LAST, hash_algo);
+ if (id < 0)
+ return -ENOPKG;
+
+ alg = tpm2_find_hash_alg(id);
+ if (alg < 0)
+ return -ENOPKG;
+
+ if (hash_id)
+ *hash_id = id;
+
+ if (tpm_hash)
+ *tpm_hash = alg;
+
+ return 0;
+}
+
+static int tpm2_asymmetric_signature_scheme(const struct tpm2_key *key,
+ const char *encoding,
+ const char *hash_algo,
+ u16 *scheme,
+ int *tpm_hash)
+{
+ if (!encoding)
+ return -ENOPKG;
+
+ switch (tpm2_key_type(key)) {
+ case TPM_ALG_RSA:
+ if (strcmp(encoding, "pkcs1") != 0)
+ return -ENOPKG;
+ *scheme = TPM_ALG_RSASSA;
+ break;
+ case TPM_ALG_ECC:
+ if (strcmp(encoding, "x962") != 0)
+ return -ENOPKG;
+ *scheme = TPM_ALG_ECDSA;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return tpm2_asymmetric_hash_lookup(hash_algo, NULL, tpm_hash);
+}
+
+/*
+ * Load a TPM2 key blob into the TPM.
+ *
+ * On success, @buf is initialized and the authorization session is kept open.
+ * On failure, @buf is destroyed and the authorization session is closed.
+ */
+static int tpm2_asymmetric_load(struct tpm_chip *chip, struct tpm2_key *key,
+ struct tpm_buf *buf, u32 *handle_out)
+{
+ int ret;
+
+ ret = tpm2_start_auth_session(chip);
+ if (ret)
+ return ret;
+
+ ret = tpm_buf_init(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ if (ret < 0)
+ goto err_auth;
+
+ ret = tpm_buf_append_name(chip, buf, key->parent, NULL);
+ if (ret)
+ goto err_buf;
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_CONTINUE_SESSION |
+ TPM2_SA_ENCRYPT, NULL, 0);
+ tpm_buf_append(buf, &key->data[0], key->priv_len + key->pub_len);
+ if (buf->flags & TPM_BUF_OVERFLOW) {
+ ret = -E2BIG;
+ goto err_buf;
+ }
+ ret = tpm_buf_fill_hmac_session(chip, buf);
+ if (ret)
+ goto err_buf;
+ ret = tpm_transmit_cmd(chip, buf, 4, "TPM2_CC_LOAD");
+ ret = tpm_buf_check_hmac_response(chip, buf, ret);
+ if (ret) {
+ ret = -EIO;
+ goto err_buf;
+ }
+
+ *handle_out = be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE]);
+ return 0;
+
+err_buf:
+ tpm_buf_destroy(buf);
+
+err_auth:
+ tpm2_end_auth_session(chip);
+ return ret;
+}
+
+static void tpm2_asymmetric_key_destroy(void *payload0, void *payload3)
+{
+ kfree(payload0);
+}
+
+/*
+ * Encrypt using TPM2_RSA_Encrypt with RSAES (PKCS#1 v1.5) scheme.
+ */
+static int tpm2_asymmetric_rsa_encrypt(struct tpm_chip *chip,
+ struct tpm2_key *key,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ u32 key_handle = 0;
+ struct tpm_buf buf;
+ u16 ciphertext_len;
+ u16 scheme;
+ u8 *pos;
+ int ret;
+
+ if (!params->encoding)
+ return -EINVAL;
+
+ if (strcmp(params->encoding, "pkcs1") == 0)
+ scheme = TPM_ALG_RSAES;
+ else if (strcmp(params->encoding, "raw") == 0)
+ scheme = TPM_ALG_NULL;
+ else
+ return -ENOPKG;
+
+ ret = tpm_try_get_ops(chip);
+ if (ret)
+ return ret;
+
+ ret = tpm2_asymmetric_load(chip, key, &buf, &key_handle);
+ if (ret)
+ goto err_ops;
+
+ tpm2_end_auth_session(chip);
+ tpm_buf_destroy(&buf);
+
+ ret = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_RSA_ENCRYPT);
+ if (ret)
+ goto err_key;
+
+ tpm_buf_append_u32(&buf, key_handle);
+
+ tpm_buf_append_u16(&buf, params->in_len);
+ tpm_buf_append(&buf, in, params->in_len);
+
+ tpm_buf_append_u16(&buf, scheme);
+
+ tpm_buf_append_u16(&buf, 0);
+
+ ret = tpm_transmit_cmd(chip, &buf, 4, "TPM2_RSA_Encrypt");
+ if (ret) {
+ ret = -EIO;
+ goto err_buf;
+ }
+
+ pos = buf.data + TPM_HEADER_SIZE;
+ ciphertext_len = be16_to_cpup((__be16 *)pos);
+ pos += 2;
+ if (pos + ciphertext_len > buf.data + buf.length) {
+ ret = -EIO;
+ goto err_buf;
+ }
+
+ if (params->out_len < ciphertext_len) {
+ ret = -EMSGSIZE;
+ goto err_buf;
+ }
+
+ memcpy(out, pos, ciphertext_len);
+ ret = ciphertext_len;
+
+err_buf:
+ tpm_buf_destroy(&buf);
+
+err_key:
+ tpm2_flush_context(chip, key_handle);
+
+err_ops:
+ tpm_put_ops(chip);
+ return ret;
+}
+
+/*
+ * Convert a TPM2B_PUBLIC_KEY_RSA response into a raw RSA signature.
+ */
+static int tpm2_asymmetric_rsa_parse_signature(struct tpm_buf *buf,
+ off_t *offset,
+ struct kernel_pkey_params *params,
+ void *out)
+{
+ u16 sig_len;
+
+ sig_len = tpm_buf_read_u16(buf, offset);
+ if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ return -EIO;
+ if (*offset + sig_len > buf->length)
+ return -EIO;
+ if (sig_len > params->out_len)
+ return -EMSGSIZE;
+
+ memcpy(out, &buf->data[*offset], sig_len);
+ return sig_len;
+}
+
+/*
+ * Convert a TPMT_SIGNATURE ECDSA R/S response into DER SEQUENCE form.
+ */
+static int tpm2_asymmetric_ecc_parse_signature(struct tpm_buf *buf, off_t *offset,
+ struct kernel_pkey_params *params,
+ void *out)
+{
+ u8 der[2 * (2 + ECC_MAX_BYTES + 1)];
+ u8 *encoded, *ptr;
+ const u8 *s;
+ u16 r_size;
+ u16 s_size;
+
+ r_size = tpm_buf_read_u16(buf, offset);
+ if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ return -EIO;
+ if (r_size == 0 || r_size > ECC_MAX_BYTES ||
+ *offset + r_size + 2 > buf->length)
+ return -EIO;
+
+ s_size = get_unaligned_be16(&buf->data[*offset + r_size]);
+ s = &buf->data[*offset + r_size + 2];
+ if (s_size == 0 || s_size > ECC_MAX_BYTES ||
+ *offset + r_size + 2 + s_size > buf->length)
+ return -EIO;
+
+ ptr = der;
+ ptr = asn1_encode_integer_bytes(ptr, der + sizeof(der),
+ &buf->data[*offset], r_size);
+ ptr = asn1_encode_integer_bytes(ptr, der + sizeof(der), s, s_size);
+ if (IS_ERR(ptr))
+ return PTR_ERR(ptr);
+
+ encoded = asn1_encode_sequence(out, (u8 *)out + params->out_len,
+ der, ptr - der);
+ if (IS_ERR(encoded))
+ return PTR_ERR(encoded) == -EINVAL ? -EMSGSIZE : PTR_ERR(encoded);
+
+ return encoded - (u8 *)out;
+}
+
+static int tpm2_asymmetric_parse_signature(struct tpm_buf *buf,
+ u16 scheme, int tpm_hash,
+ struct kernel_pkey_params *params,
+ void *out)
+{
+ off_t offset = TPM_HEADER_SIZE + 4;
+ u16 hash_alg;
+ u16 sig_alg;
+
+ sig_alg = tpm_buf_read_u16(buf, &offset);
+ hash_alg = tpm_buf_read_u16(buf, &offset);
+ if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ return -EIO;
+ if (sig_alg != scheme || hash_alg != tpm_hash)
+ return -EIO;
+
+ switch (scheme) {
+ case TPM_ALG_RSASSA:
+ return tpm2_asymmetric_rsa_parse_signature(buf, &offset, params, out);
+ case TPM_ALG_ECDSA:
+ return tpm2_asymmetric_ecc_parse_signature(buf, &offset, params, out);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * Sign a digest using TPM2_Sign.
+ */
+static int tpm2_asymmetric_sign(struct tpm_chip *chip, struct tpm2_key *key,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct tpm_buf buf;
+ u32 key_handle = 0;
+ int tpm_hash;
+ u16 scheme;
+ int ret;
+
+ ret = tpm2_asymmetric_signature_scheme(key, params->encoding,
+ params->hash_algo, &scheme,
+ &tpm_hash);
+ if (ret)
+ return ret;
+
+ ret = tpm_try_get_ops(chip);
+ if (ret)
+ return ret;
+
+ ret = tpm2_asymmetric_load(chip, key, &buf, &key_handle);
+ if (ret)
+ goto err_ops;
+
+ tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_SIGN);
+ ret = tpm_buf_append_name(chip, &buf, key_handle, NULL);
+ if (ret)
+ goto err_key;
+ tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0);
+
+ /* digest (TPM2B_DIGEST) */
+ tpm_buf_append_u16(&buf, params->in_len);
+ tpm_buf_append(&buf, in, params->in_len);
+
+ /* inScheme (TPMT_SIG_SCHEME) */
+ tpm_buf_append_u16(&buf, scheme);
+ tpm_buf_append_u16(&buf, tpm_hash);
+
+ /* validation (TPMT_TK_HASHCHECK): NULL ticket */
+ tpm_buf_append_u16(&buf, TPM2_ST_HASHCHECK);
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+ tpm_buf_append_u16(&buf, 0);
+
+ if (buf.flags & TPM_BUF_OVERFLOW) {
+ tpm2_end_auth_session(chip);
+ ret = -E2BIG;
+ goto err_key;
+ }
+ ret = tpm_buf_fill_hmac_session(chip, &buf);
+ if (ret)
+ goto err_key;
+ ret = tpm_transmit_cmd(chip, &buf, 4, "TPM2_Sign");
+ ret = tpm_buf_check_hmac_response(chip, &buf, ret);
+ if (ret) {
+ ret = -EIO;
+ goto err_key;
+ }
+
+ ret = tpm2_asymmetric_parse_signature(&buf, scheme, tpm_hash, params, out);
+
+err_key:
+ tpm2_flush_context(chip, key_handle);
+ tpm_buf_destroy(&buf);
+
+err_ops:
+ tpm_put_ops(chip);
+ return ret;
+}
+
+/*
+ * Decrypt using TPM2_RSA_Decrypt with RSAES-PKCS1-v1_5 scheme.
+ */
+static int tpm2_asymmetric_rsa_decrypt(struct tpm_chip *chip,
+ struct tpm2_key *key,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ u32 key_handle = 0;
+ struct tpm_buf buf;
+ u16 decrypted_len;
+ off_t offset;
+ int ret;
+
+ if (!params->encoding || strcmp(params->encoding, "pkcs1") != 0)
+ return -ENOPKG;
+
+ ret = tpm_try_get_ops(chip);
+ if (ret)
+ return ret;
+
+ ret = tpm2_asymmetric_load(chip, key, &buf, &key_handle);
+ if (ret)
+ goto err_ops;
+
+ tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_RSA_DECRYPT);
+ ret = tpm_buf_append_name(chip, &buf, key_handle, NULL);
+ if (ret)
+ goto err_key;
+ tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, NULL, 0);
+ tpm_buf_append_u16(&buf, params->in_len);
+ tpm_buf_append(&buf, in, params->in_len);
+ tpm_buf_append_u16(&buf, TPM_ALG_RSAES);
+ tpm_buf_append_u16(&buf, 0);
+ if (buf.flags & TPM_BUF_OVERFLOW) {
+ tpm2_end_auth_session(chip);
+ ret = -E2BIG;
+ goto err_key;
+ }
+ ret = tpm_buf_fill_hmac_session(chip, &buf);
+ if (ret)
+ goto err_key;
+ ret = tpm_transmit_cmd(chip, &buf, 4, "TPM2_RSA_DECRYPT");
+ ret = tpm_buf_check_hmac_response(chip, &buf, ret);
+ if (ret) {
+ ret = -EIO;
+ goto err_key;
+ }
+
+ offset = TPM_HEADER_SIZE + 4;
+ decrypted_len = tpm_buf_read_u16(&buf, &offset);
+ if (buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+ ret = -EIO;
+ goto err_key;
+ }
+ if (offset + decrypted_len > buf.length) {
+ ret = -EIO;
+ goto err_key;
+ }
+
+ if (params->out_len < decrypted_len) {
+ ret = -EMSGSIZE;
+ goto err_key;
+ }
+
+ memcpy(out, &buf.data[offset], decrypted_len);
+ ret = decrypted_len;
+
+err_key:
+ tpm2_flush_context(chip, key_handle);
+ tpm_buf_destroy(&buf);
+
+err_ops:
+ tpm_put_ops(chip);
+ return ret;
+}
+
+/*
+ * Verify an RSA signature using TPM2_VerifySignature with RSASSA scheme.
+ */
+static int tpm2_asymmetric_rsa_verify(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ struct tpm2_key *tpm2_key = key->payload.data[asym_crypto];
+ struct tpm_chip *chip;
+ struct tpm_buf buf;
+ u32 key_handle = 0;
+ int tpm_hash;
+ int ret;
+
+ if (!sig->m)
+ return -ENOPKG;
+
+ if (!sig->encoding || strcmp(sig->encoding, "pkcs1") != 0)
+ return -ENOPKG;
+
+ if (!sig->hash_algo)
+ return -EINVAL;
+
+ chip = tpm_default_chip();
+
+ if (!chip)
+ return -ENODEV;
+
+ ret = tpm2_asymmetric_hash_lookup(sig->hash_algo, NULL, &tpm_hash);
+ if (ret)
+ goto err_chip;
+
+ ret = tpm_try_get_ops(chip);
+ if (ret)
+ goto err_chip;
+
+ ret = tpm2_asymmetric_load(chip, tpm2_key, &buf, &key_handle);
+ if (ret)
+ goto err_ops;
+
+ tpm2_end_auth_session(chip);
+ tpm_buf_destroy(&buf);
+
+ ret = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS,
+ TPM2_CC_VERIFY_SIGNATURE);
+ if (ret)
+ goto err_key;
+
+ tpm_buf_append_u32(&buf, key_handle);
+
+ tpm_buf_append_u16(&buf, sig->m_size);
+ tpm_buf_append(&buf, sig->m, sig->m_size);
+
+ tpm_buf_append_u16(&buf, TPM_ALG_RSASSA);
+ tpm_buf_append_u16(&buf, tpm_hash);
+ tpm_buf_append_u16(&buf, sig->s_size);
+ tpm_buf_append(&buf, sig->s, sig->s_size);
+
+ ret = tpm_transmit_cmd(chip, &buf, 0, "TPM2_VerifySignature");
+ if (ret)
+ ret = -EKEYREJECTED;
+
+ tpm_buf_destroy(&buf);
+
+err_key:
+ tpm2_flush_context(chip, key_handle);
+
+err_ops:
+ tpm_put_ops(chip);
+
+err_chip:
+ put_device(&chip->dev);
+ return ret;
+}
+
+static int tpm2_asymmetric_rsa_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ const struct tpm2_key *key = params->key->payload.data[asym_crypto];
+ u16 max_data_size = TPM2_MAX_DIGEST_SIZE;
+ const u16 mod_size = tpm2_asymmetric_rsa_mod_size(key);
+ int hash_id, ret;
+
+ if (!params->encoding)
+ return -EINVAL;
+
+ memset(info, 0, sizeof(*info));
+ info->key_size = mod_size * 8;
+
+ if (strcmp(params->encoding, "pkcs1") == 0) {
+ if (params->hash_algo) {
+ ret = tpm2_asymmetric_hash_lookup(params->hash_algo, &hash_id, NULL);
+ if (ret)
+ return ret;
+ max_data_size = hash_digest_size[hash_id];
+ }
+
+ info->max_data_size = max_data_size;
+ info->max_sig_size = mod_size;
+ info->max_enc_size = mod_size;
+ info->max_dec_size = mod_size;
+ info->supported_ops = KEYCTL_SUPPORTS_SIGN |
+ KEYCTL_SUPPORTS_VERIFY |
+ KEYCTL_SUPPORTS_ENCRYPT |
+ KEYCTL_SUPPORTS_DECRYPT;
+ return 0;
+ }
+
+ if (strcmp(params->encoding, "raw") == 0) {
+ info->max_data_size = mod_size;
+ info->max_enc_size = mod_size;
+ info->max_dec_size = mod_size;
+ info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT;
+ return 0;
+ }
+
+ return -ENOPKG;
+}
+
+static int tpm2_asymmetric_rsa_validate(const struct tpm2_key *key)
+{
+ const struct tpm2_asymmetric_rsa_parms *p = tpm2_asymmetric_parms(key);
+ u16 key_bits;
+ u16 mod_size;
+
+ if (tpm2_key_policy_size(key) != 0)
+ return -EBADMSG;
+
+ if (key->pub_len < 2 + sizeof(*key->desc) + sizeof(*p))
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->symmetric) != TPM_ALG_NULL)
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->scheme) != TPM_ALG_NULL)
+ return -EBADMSG;
+
+ key_bits = be16_to_cpu(p->key_bits);
+ if (key_bits != 2048 && key_bits != 3072 && key_bits != 4096)
+ return -EBADMSG;
+
+ if (be32_to_cpu(p->exponent) != 0x00000000 &&
+ be32_to_cpu(p->exponent) != 0x00010001)
+ return -EBADMSG;
+
+ mod_size = tpm2_asymmetric_rsa_mod_size(key);
+ if (mod_size != key_bits / 8)
+ return -EBADMSG;
+
+ if (key->pub_len < 2 + sizeof(*key->desc) + sizeof(*p) + mod_size)
+ return -EBADMSG;
+
+ return 0;
+}
+
+static unsigned int tpm2_asymmetric_der_len_size(unsigned int len)
+{
+ if (len < 128)
+ return 1;
+ if (len <= 255)
+ return 2;
+ return 3;
+}
+
+/*
+ * Parse a DER-encoded ECDSA signature: SEQUENCE { INTEGER r, INTEGER s }.
+ *
+ * On success, @r/@r_len and @s/@s_len point into @der with leading zero
+ * pads stripped.
+ */
+static int tpm2_asymmetric_ecc_parse_der_signature(const u8 *der, u32 der_len,
+ const u8 **r, u16 *r_len,
+ const u8 **s, u16 *s_len)
+{
+ const u8 *end = der + der_len;
+ u32 seq_len, int_len;
+ const u8 *p = der;
+
+ if (p >= end || *p++ != 0x30)
+ return -EBADMSG;
+
+ if (p >= end)
+ return -EBADMSG;
+ if (*p < 0x80) {
+ seq_len = *p++;
+ } else if (*p == 0x81) {
+ if (++p >= end)
+ return -EBADMSG;
+ seq_len = *p++;
+ } else {
+ return -EBADMSG;
+ }
+
+ if (p + seq_len > end)
+ return -EBADMSG;
+ end = p + seq_len;
+
+ /* INTEGER r */
+ if (p >= end || *p++ != 0x02)
+ return -EBADMSG;
+ if (p >= end)
+ return -EBADMSG;
+ int_len = *p++;
+ if (int_len == 0 || int_len >= 0x80 || p + int_len > end)
+ return -EBADMSG;
+ while (int_len > 1 && *p == 0x00) {
+ p++;
+ int_len--;
+ }
+ *r = p;
+ *r_len = int_len;
+ p += int_len;
+
+ /* INTEGER s */
+ if (p >= end || *p++ != 0x02)
+ return -EBADMSG;
+ if (p >= end)
+ return -EBADMSG;
+ int_len = *p++;
+ if (int_len == 0 || int_len >= 0x80 || p + int_len > end)
+ return -EBADMSG;
+ while (int_len > 1 && *p == 0x00) {
+ p++;
+ int_len--;
+ }
+ *s = p;
+ *s_len = int_len;
+ p += int_len;
+
+ if (p != end)
+ return -EBADMSG;
+
+ return 0;
+}
+
+/*
+ * Verify an ECDSA signature using TPM2_VerifySignature.
+ *
+ * A DER-encoded signature is parsed into (r, s) components for the TPM command.
+ */
+static int tpm2_asymmetric_ecc_verify(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ struct tpm2_key *tpm2_key = key->payload.data[asym_crypto];
+ struct tpm_chip *chip;
+ const u8 *r, *s_data;
+ struct tpm_buf buf;
+ u32 key_handle = 0;
+ u16 r_len, s_len;
+ int tpm_hash;
+ int ret;
+
+ if (!sig->m)
+ return -ENOPKG;
+
+ if (!sig->encoding || strcmp(sig->encoding, "x962") != 0)
+ return -ENOPKG;
+
+ if (!sig->hash_algo)
+ return -EINVAL;
+
+ chip = tpm_default_chip();
+
+ if (!chip)
+ return -ENODEV;
+
+ ret = tpm2_asymmetric_hash_lookup(sig->hash_algo, NULL, &tpm_hash);
+ if (ret)
+ goto err_chip;
+
+ ret = tpm2_asymmetric_ecc_parse_der_signature(sig->s, sig->s_size,
+ &r, &r_len, &s_data,
+ &s_len);
+ if (ret)
+ goto err_chip;
+
+ ret = tpm_try_get_ops(chip);
+ if (ret)
+ goto err_chip;
+
+ ret = tpm2_asymmetric_load(chip, tpm2_key, &buf, &key_handle);
+ if (ret)
+ goto err_ops;
+
+ tpm2_end_auth_session(chip);
+ tpm_buf_destroy(&buf);
+
+ ret = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS,
+ TPM2_CC_VERIFY_SIGNATURE);
+ if (ret)
+ goto err_key;
+
+ tpm_buf_append_u32(&buf, key_handle);
+
+ /* digest (TPM2B_DIGEST) */
+ tpm_buf_append_u16(&buf, sig->m_size);
+ tpm_buf_append(&buf, sig->m, sig->m_size);
+
+ /* signature (TPMT_SIGNATURE): ECDSA with the given hash */
+ tpm_buf_append_u16(&buf, TPM_ALG_ECDSA);
+ tpm_buf_append_u16(&buf, tpm_hash);
+
+ /* signatureR (TPM2B_ECC_PARAMETER) */
+ tpm_buf_append_u16(&buf, r_len);
+ tpm_buf_append(&buf, r, r_len);
+
+ /* signatureS (TPM2B_ECC_PARAMETER) */
+ tpm_buf_append_u16(&buf, s_len);
+ tpm_buf_append(&buf, s_data, s_len);
+
+ ret = tpm_transmit_cmd(chip, &buf, 0, "TPM2_VerifySignature");
+ if (ret)
+ ret = -EKEYREJECTED;
+
+ tpm_buf_destroy(&buf);
+
+err_key:
+ tpm2_flush_context(chip, key_handle);
+
+err_ops:
+ tpm_put_ops(chip);
+
+err_chip:
+ put_device(&chip->dev);
+ return ret;
+}
+
+static int tpm2_asymmetric_ecc_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ const struct tpm2_key *key = params->key->payload.data[asym_crypto];
+ const struct tpm2_asymmetric_ecc_parms *p = tpm2_asymmetric_parms(key);
+ unsigned int int_len, seq_payload;
+ const u8 *x;
+ u16 ecc, n;
+ int ret;
+
+ ecc = be16_to_cpu(p->ecc);
+ x = tpm2_asymmetric_ecc_x(key);
+ n = get_unaligned_be16(&x[0]);
+ int_len = n + 1;
+
+ if (!params->encoding || strcmp(params->encoding, "x962") != 0)
+ return -ENOPKG;
+
+ ret = tpm2_asymmetric_hash_lookup(params->hash_algo, NULL, NULL);
+ if (ret)
+ return ret;
+
+ /*
+ * SEQUENCE { INTEGER (<=n+1 bytes), INTEGER (<=n+1 bytes) }
+ */
+ seq_payload = 2 * (1 + tpm2_asymmetric_der_len_size(int_len) + int_len);
+
+ memset(info, 0, sizeof(*info));
+ info->key_size = tpm2_asymmetric_ecc_key_bits(ecc);
+ info->max_sig_size = 1 + tpm2_asymmetric_der_len_size(seq_payload) + seq_payload;
+ info->max_data_size = TPM2_MAX_DIGEST_SIZE;
+ info->supported_ops = KEYCTL_SUPPORTS_SIGN | KEYCTL_SUPPORTS_VERIFY;
+
+ return 0;
+}
+
+static int tpm2_asymmetric_ecc_validate(const struct tpm2_key *key)
+{
+ const struct tpm2_asymmetric_ecc_parms *p = tpm2_asymmetric_parms(key);
+ size_t min_len = 2 + sizeof(*key->desc) + sizeof(*p);
+ u16 x_size, y_size;
+ const u8 *x, *y;
+
+ if (tpm2_key_policy_size(key) != 0)
+ return -EBADMSG;
+
+ if (key->pub_len < min_len + 2)
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->symmetric) != TPM_ALG_NULL)
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->scheme) != TPM_ALG_NULL)
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P256 &&
+ be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P384 &&
+ be16_to_cpu(p->ecc) != TPM2_ECC_NIST_P521)
+ return -EBADMSG;
+
+ if (be16_to_cpu(p->kdf) != TPM_ALG_NULL)
+ return -EBADMSG;
+
+ x = tpm2_asymmetric_ecc_x(key);
+ x_size = get_unaligned_be16(&x[0]);
+ if (x_size > ECC_MAX_BYTES)
+ return -EBADMSG;
+
+ if (key->pub_len < min_len + 2 + x_size + 2)
+ return -EBADMSG;
+
+ y = tpm2_asymmetric_ecc_y(key);
+ y_size = get_unaligned_be16(&y[0]);
+ if (y_size > ECC_MAX_BYTES)
+ return -EBADMSG;
+
+ if (key->pub_len < min_len + 2 + x_size + 2 + y_size)
+ return -EBADMSG;
+
+ if (x_size != y_size)
+ return -EBADMSG;
+
+ return 0;
+}
+
+static const char *tpm2_asymmetric_ecc_name(const struct tpm2_key *key)
+{
+ const struct tpm2_asymmetric_ecc_parms *p;
+
+ p = tpm2_asymmetric_parms(key);
+
+ switch (be16_to_cpu(p->ecc)) {
+ case TPM2_ECC_NIST_P256:
+ return "ecdsa-nist-p256";
+ case TPM2_ECC_NIST_P384:
+ return "ecdsa-nist-p384";
+ case TPM2_ECC_NIST_P521:
+ return "ecdsa-nist-p521";
+ default:
+ return "ecdsa";
+ }
+}
+
+static void tpm2_asymmetric_describe(const struct key *asymmetric_key,
+ struct seq_file *m)
+{
+ const struct tpm2_key *key;
+
+ key = asymmetric_key->payload.data[asym_crypto];
+ if (!key)
+ return;
+
+ switch (tpm2_key_type(key)) {
+ case TPM_ALG_RSA:
+ seq_puts(m, "tpm2.rsa");
+ break;
+ case TPM_ALG_ECC:
+ seq_printf(m, "tpm2.%s", tpm2_asymmetric_ecc_name(key));
+ break;
+ default:
+ seq_puts(m, "tpm2.unknown");
+ break;
+ }
+}
+
+static int tpm2_asymmetric_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ struct tpm2_key *key = params->key->payload.data[asym_crypto];
+
+ switch (tpm2_key_type(key)) {
+ case TPM_ALG_RSA:
+ return tpm2_asymmetric_rsa_query(params, info);
+ case TPM_ALG_ECC:
+ return tpm2_asymmetric_ecc_query(params, info);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tpm2_asymmetric_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct tpm2_key *key = params->key->payload.data[asym_crypto];
+ struct tpm_chip *chip;
+ int ret;
+
+ chip = tpm_default_chip();
+ if (!chip)
+ return -ENODEV;
+
+ switch (params->op) {
+ case kernel_pkey_encrypt:
+ if (tpm2_key_type(key) != TPM_ALG_RSA) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ ret = tpm2_asymmetric_rsa_encrypt(chip, key, params, in, out);
+ break;
+ case kernel_pkey_decrypt:
+ if (tpm2_key_type(key) != TPM_ALG_RSA) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ ret = tpm2_asymmetric_rsa_decrypt(chip, key, params, in, out);
+ break;
+ case kernel_pkey_sign:
+ ret = tpm2_asymmetric_sign(chip, key, params, in, out);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ put_device(&chip->dev);
+ return ret;
+}
+
+static int tpm2_asymmetric_verify_signature(const struct key *asymmetric_key,
+ const struct public_key_signature *sig)
+{
+ struct tpm2_key *key = asymmetric_key->payload.data[asym_crypto];
+
+ switch (tpm2_key_type(key)) {
+ case TPM_ALG_RSA:
+ return tpm2_asymmetric_rsa_verify(asymmetric_key, sig);
+ case TPM_ALG_ECC:
+ return tpm2_asymmetric_ecc_verify(asymmetric_key, sig);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct asymmetric_key_subtype tpm2_asymmetric_subtype = {
+ .owner = THIS_MODULE,
+ .name = "tpm2_asymmetric_key",
+ .name_len = sizeof("tpm2_asymmetric_key") - 1,
+ .describe = tpm2_asymmetric_describe,
+ .destroy = tpm2_asymmetric_key_destroy,
+ .query = tpm2_asymmetric_query,
+ .eds_op = tpm2_asymmetric_eds_op,
+ .verify_signature = tpm2_asymmetric_verify_signature,
+};
+
+static int tpm2_asymmetric_preparse(struct key_preparsed_payload *prep)
+{
+ struct tpm2_key *key __free(kfree) = NULL;
+ int ret;
+
+ key = tpm2_key_decode(prep->data, prep->datalen);
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ key = NULL;
+ return ret;
+ }
+
+ if (key->oid != OID_TPMLoadableKey)
+ return -EBADMSG;
+
+ switch (tpm2_key_type(key)) {
+ case TPM_ALG_RSA:
+ ret = tpm2_asymmetric_rsa_validate(key);
+ break;
+ case TPM_ALG_ECC:
+ ret = tpm2_asymmetric_ecc_validate(key);
+ break;
+ default:
+ ret = -EBADMSG;
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ __module_get(tpm2_asymmetric_subtype.owner);
+
+ prep->payload.data[asym_subtype] = &tpm2_asymmetric_subtype;
+ prep->payload.data[asym_key_ids] = NULL;
+ prep->payload.data[asym_crypto] = no_free_ptr(key);
+ prep->payload.data[asym_auth] = NULL;
+ prep->quotalen = 100;
+
+ return 0;
+}
+
+static struct asymmetric_key_parser tpm2_asymmetric_parser = {
+ .owner = THIS_MODULE,
+ .name = "tpm2_asymmetric_parser",
+ .parse = tpm2_asymmetric_preparse,
+};
+
+static int __init tpm2_asymmetric_init(void)
+{
+ return register_asymmetric_key_parser(&tpm2_asymmetric_parser);
+}
+
+static void __exit tpm2_asymmetric_exit(void)
+{
+ unregister_asymmetric_key_parser(&tpm2_asymmetric_parser);
+}
+
+module_init(tpm2_asymmetric_init);
+module_exit(tpm2_asymmetric_exit);
+
+MODULE_DESCRIPTION("Asymmetric TPM2 key");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..d4d5ddc0173a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -45,6 +45,7 @@ enum tpm2_session_types {
/* if you add a new hash to this, increment TPM_MAX_HASHES below */
enum tpm_algorithms {
TPM_ALG_ERROR = 0x0000,
+ TPM_ALG_RSA = 0x0001,
TPM_ALG_SHA1 = 0x0004,
TPM_ALG_AES = 0x0006,
TPM_ALG_KEYEDHASH = 0x0008,
@@ -53,6 +54,9 @@ enum tpm_algorithms {
TPM_ALG_SHA512 = 0x000D,
TPM_ALG_NULL = 0x0010,
TPM_ALG_SM3_256 = 0x0012,
+ TPM_ALG_RSASSA = 0x0014,
+ TPM_ALG_RSAES = 0x0015,
+ TPM_ALG_ECDSA = 0x0018,
TPM_ALG_ECC = 0x0023,
TPM_ALG_CFB = 0x0043,
};
@@ -66,6 +70,8 @@ enum tpm_algorithms {
enum tpm2_curves {
TPM2_ECC_NONE = 0x0000,
TPM2_ECC_NIST_P256 = 0x0003,
+ TPM2_ECC_NIST_P384 = 0x0004,
+ TPM2_ECC_NIST_P521 = 0x0005,
};
struct tpm_digest {
@@ -242,6 +248,7 @@ enum tpm2_structures {
TPM2_ST_NO_SESSIONS = 0x8001,
TPM2_ST_SESSIONS = 0x8002,
TPM2_ST_CREATION = 0x8021,
+ TPM2_ST_HASHCHECK = 0x8024,
};
/* Indicates from what layer of the software stack the error comes from */
@@ -276,12 +283,15 @@ enum tpm2_command_codes {
TPM2_CC_NV_READ = 0x014E,
TPM2_CC_CREATE = 0x0153,
TPM2_CC_LOAD = 0x0157,
+ TPM2_CC_RSA_DECRYPT = 0x0159,
TPM2_CC_SEQUENCE_UPDATE = 0x015C,
+ TPM2_CC_SIGN = 0x015D,
TPM2_CC_UNSEAL = 0x015E,
TPM2_CC_CONTEXT_LOAD = 0x0161,
TPM2_CC_CONTEXT_SAVE = 0x0162,
TPM2_CC_FLUSH_CONTEXT = 0x0165,
TPM2_CC_READ_PUBLIC = 0x0173,
+ TPM2_CC_RSA_ENCRYPT = 0x0174,
TPM2_CC_START_AUTH_SESS = 0x0176,
TPM2_CC_VERIFY_SIGNATURE = 0x0177,
TPM2_CC_GET_CAPABILITY = 0x017A,
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v8 0/3]
From: Jarkko Sakkinen @ 2026-05-24 5:20 UTC (permalink / raw)
To: keyrings
Cc: David Howells, linux-crypto, linux-integrity, David Woodhouse,
James Bottomley, Stefan Berger, Herbert Xu, Mimi Zohar,
Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org>
On Sun, May 24, 2026 at 08:15:11AM +0300, Jarkko Sakkinen wrote:
> This series introduces key type for operating with asymmetric keys using
> a TPM2 chip.
>
> Change Log
> ==========
>
> v8:
> - Reset patch change logs given the overhaul of the code and patches.
> - Have only single new subkey type.
> - Make key type only use TPM operations.
> - Use TPM2_Sign for both ECC and RSA keys.
> - Align key descriptions with other key types.
>
> Previous versions
> =================
>
> * v7: https://lore.kernel.org/linux-integrity/20240528210823.28798-1-jarkko@kernel.org/
> * v6: https://lore.kernel.org/linux-integrity/20240528035136.11464-1-jarkko@kernel.org/
> * v5: https://lore.kernel.org/linux-integrity/20240523212515.4875-1-jarkko@kernel.org/
> * v4: https://lore.kernel.org/linux-integrity/20240522005252.17841-1-jarkko@kernel.org/
> * v3: https://lore.kernel.org/linux-integrity/20240521152659.26438-1-jarkko@kernel.org/
> * v2: https://lore.kernel.org/linux-integrity/336755.1716327854@warthog.procyon.org.uk/
> * v1: https://lore.kernel.org/linux-integrity/20240520184727.22038-1-jarkko@kernel.org/
> * Derived from https://lore.kernel.org/all/20200518172704.29608-1-prestwoj@gmail.com/
>
>
> Jarkko Sakkinen (3):
> lib/asn1_encoder: Add asn1_encode_integer_bytes()
> crypto: Migrate TPMKey ASN.1 objects from trusted-keys
> keys: asymmetric: tpm2_asymmetric
>
> crypto/Kconfig | 7 +
> crypto/Makefile | 6 +
> crypto/asymmetric_keys/Kconfig | 17 +
> crypto/asymmetric_keys/Makefile | 1 +
> crypto/asymmetric_keys/tpm2_asymmetric.c | 1096 +++++++++++++++++++++
> crypto/tpm2_key.asn1 | 11 +
> crypto/tpm2_key.c | 150 +++
> include/crypto/tpm2_key.h | 46 +
> include/linux/asn1_encoder.h | 3 +
> include/linux/tpm.h | 10 +
> lib/asn1_encoder.c | 62 ++
> security/keys/trusted-keys/Kconfig | 2 +-
> security/keys/trusted-keys/Makefile | 2 -
> security/keys/trusted-keys/tpm2key.asn1 | 11 -
> security/keys/trusted-keys/trusted_tpm2.c | 119 +--
> 15 files changed, 1421 insertions(+), 122 deletions(-)
> create mode 100644 crypto/asymmetric_keys/tpm2_asymmetric.c
> create mode 100644 crypto/tpm2_key.asn1
> create mode 100644 crypto/tpm2_key.c
> create mode 100644 include/crypto/tpm2_key.h
> delete mode 100644 security/keys/trusted-keys/tpm2key.asn1
>
> --
> 2.47.3
>
There's some initial test code for this too:
https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd-test.git/tree/overlay/usr/local/bin/tpmdd_tpm2_asymmetric.sh?h=main
Ugh, that's one hell of an url...
BR, Jarkko
^ permalink raw reply
* [PATCH] apparmor: Constify 'nulldfa_src' and 'stacksplitdfa_src' arrays
From: Len Bao @ 2026-05-24 11:34 UTC (permalink / raw)
To: John Johansen, Paul Moore, James Morris, Serge E. Hallyn
Cc: Len Bao, apparmor, linux-security-module, linux-kernel
The 'nulldfa_src' and 'stacksplitdfa_src' arrays are initialized in
their declarations and never changed. So, constify them to reduce the
attack surface.
To make this possible, it is also necessary to change the 'unpack_table'
and 'aa_dfa_unpack' function prototypes to pass, as a first argument, a
pointer to a 'const' blob. At the same type, define the blob exact
pointer type (pointer to const char) since all the calls to the
mentioned functions use this same type.
Before the patch (size lsm.o):
text data bss dec hex
128768 28028 704 157500 2673c
After the patch (size lsm.o):
text data bss dec hex
131264 25532 704 157500 2673c
Signed-off-by: Len Bao <len.bao@gmx.us>
---
security/apparmor/include/match.h | 2 +-
security/apparmor/lsm.c | 4 ++--
security/apparmor/match.c | 6 +++---
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 7accb1c39..4a92cd044 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -125,7 +125,7 @@ static inline size_t table_size(size_t len, size_t el_size)
#define aa_state_t unsigned int
-struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
+struct aa_dfa *aa_dfa_unpack(const char *blob, size_t size, int flags);
aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
const char *str, int len);
aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start,
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3491e9f60..3f995b6a7 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -2432,12 +2432,12 @@ static int __init apparmor_nf_ip_init(void)
}
#endif
-static char nulldfa_src[] __aligned(8) = {
+static const char nulldfa_src[] __aligned(8) = {
#include "nulldfa.in"
};
static struct aa_dfa *nulldfa;
-static char stacksplitdfa_src[] __aligned(8) = {
+static const char stacksplitdfa_src[] __aligned(8) = {
#include "stacksplitdfa.in"
};
struct aa_dfa *stacksplitdfa;
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 3a2c6cf02..c6f7bea1e 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -31,7 +31,7 @@
*
* NOTE: must be freed by kvfree (not kfree)
*/
-static struct table_header *unpack_table(char *blob, size_t bsize)
+static struct table_header *unpack_table(const char *blob, size_t bsize)
{
struct table_header *table = NULL;
struct table_header th;
@@ -311,11 +311,11 @@ static struct table_header *remap_data16_to_data32(struct table_header *old)
*
* Returns: an unpacked dfa ready for matching or ERR_PTR on failure
*/
-struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
+struct aa_dfa *aa_dfa_unpack(const char *blob, size_t size, int flags)
{
int hsize;
int error = -ENOMEM;
- char *data = blob;
+ const char *data = blob;
struct table_header *table = NULL;
struct aa_dfa *dfa = kzalloc_obj(struct aa_dfa);
if (!dfa)
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v8 1/9] landlock: Add a place for flags to layer rules
From: Justin Suess @ 2026-05-24 14:46 UTC (permalink / raw)
To: Tingmao Wang
Cc: Mickaël Salaün, Günther Noack, Jan Kara,
Abhinav Saxena, linux-security-module
In-Reply-To: <617fdd53-6612-4f6f-b0e0-16d85985487b@maowtm.org>
On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
> On 5/23/26 21:48, Mickaël Salaün wrote:
> > This patch doesn't build.
>
> Missed a hunk in this patch (ended up in the next one), will add.
>
> >> @@ -797,20 +803,28 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> >> }
> >>
> >> if (unlikely(dentry_child1)) {
> >> + /*
> >> + * Get the layer masks for the child dentries for use by domain
> >> + * check later. The rule_flags for child1 should have been
> >> + * included in rule_flags_parent1 already (cf.
> >> + * collect_domain_accesses), and is not relevant for domain check,
> >> + * so we don't have to pass it to landlock_unmask_layers.
> >> + */
> >> if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> >> &_layer_masks_child1,
> >> LANDLOCK_KEY_INODE))
> >> landlock_unmask_layers(find_rule(domain, dentry_child1),
> >> - &_layer_masks_child1);
> >> + &_layer_masks_child1, NULL);
> >> layer_masks_child1 = &_layer_masks_child1;
> >> child1_is_directory = d_is_dir(dentry_child1);
> >> }
> >> if (unlikely(dentry_child2)) {
> >> + /* See above comment for why NULL is passed as rule_flags_masks. */
> >
> > rule_flags_masks doesn't exist.
>
> I guess I was probably referring to the rule_flags argument - will fix.
>
> >> [...]
> >> @@ -647,9 +648,14 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
> >> */
> >> for (size_t i = 0; i < rule->num_layers; i++) {
> >> const struct landlock_layer *const layer = &rule->layers[i];
> >> + const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> >>
> >
> >> /* Clear the bits where the layer in the rule grants access. */
> >> masks->access[layer->level - 1] &= ~layer->access;
> >> +
> >> + /* Collect rule flags for each layer. */
> >> + if (rule_flags && layer->flags.quiet)
> >> + rule_flags->quiet_masks |= layer_bit;
> >
> > Why not store the quiet bit in masks? That would not only be "access"
> > bits anymore but it makes sense to store all this bits it the same
> > place.
> >
> > We should then probably rename struct layer_access_masks to just struct
> > layer_masks.
> >
> > We need to be careful to not increase too much the size of this struct
> > though while keeping the [LANDLOCK_MAX_NUM_LAYERS] approach if possible
> > (see Günther's commit that added it).
>
> Most uses of struct layer_access_masks do not actually care about the rule
> flags tho, e.g. in unmask_scoped_access, scope_to_request, or may_refer.
> Such a rename would touch 31 places (and only a few of them would actually
> touch the quiet flag).
>
> If we want to refactor to make this be in the layer_access_masks (then
> rename it), I guess there are 3 options, which do you prefer?
>
> 1. Add a u16 bitfield for which layers are quieted. Future rule flags
> will be additional bitfields. struct layer_masks becomes 68 bytes (+4).
>
> struct layer_masks {
> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> layer_mask_t quiet_layers;
> };
>
> 2. Make the [LANDLOCK_MAX_NUM_LAYERS] array store both the access mask and
> the quiet bit (or more bits for future rule flags). Size of struct stays
> the same.
>
This approach seems best.
> static_assert(LANDLOCK_NUM_ACCESS_NET <= LANDLOCK_NUM_ACCESS_FS);
> static_assert(LANDLOCK_NUM_SCOPE <= LANDLOCK_NUM_ACCESS_FS);
> struct layer_mask {
> access_mask_t access:LANDLOCK_NUM_ACCESS_FS;
> bool quiet:1;
> };
Other way to do it could be an (anonymous?) union.
union {
access_mask_t fs_access:LANDLOCK_NUM_ACCESS_FS;
access_mask_t net_access:LANDLOCK_NUM_ACCESS_NET;
access_mask_t scope_access:LANDLOCK_NUM_SCOPE;
}
The union should be sized to fit the largest field automatically.
That way you don't have to change this when adding new access rights
and avoid the brittle static_asserts.
Not sure about the alignment implications here though.
> struct layer_masks {
> struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> (Maybe we can just make struct layer_masks a typedef to
> layer_mask[LANDLOCK_MAX_NUM_LAYERS] instead? But currently not sure if
> there are any gotchas with a typedef like that)
>
Typedefs are generally discouraged for structs and pointers in the
kernel coding style.
https://docs.kernel.org/process/coding-style.html
> 3. Mirror layer_access_masks::access[] - add a
> rule_flags[LANDLOCK_MAX_NUM_LAYERS] too. struct layer_masks becomes 80
> bytes (+16).
>
> struct rule_flags {
> bool quiet:1;
> };
> struct layer_masks {
> /**
> * @access: The unfulfilled access rights for each layer.
> */
> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> struct rule_flags rule_flags[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> (3 seems very wasteful to me)
^ permalink raw reply
* Re: [PATCH v8 1/9] landlock: Add a place for flags to layer rules
From: Tingmao Wang @ 2026-05-24 18:20 UTC (permalink / raw)
To: Justin Suess, Mickaël Salaün
Cc: Günther Noack, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <ahMLLwnDO7g64h63@zenbox>
On 5/24/26 15:46, Justin Suess wrote:
> On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
>> On 5/23/26 21:48, Mickaël Salaün wrote:
>>> [...]
>>>> @@ -647,9 +648,14 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
>>>> */
>>>> for (size_t i = 0; i < rule->num_layers; i++) {
>>>> const struct landlock_layer *const layer = &rule->layers[i];
>>>> + const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
>>>>
>>>
>>>> /* Clear the bits where the layer in the rule grants access. */
>>>> masks->access[layer->level - 1] &= ~layer->access;
>>>> +
>>>> + /* Collect rule flags for each layer. */
>>>> + if (rule_flags && layer->flags.quiet)
>>>> + rule_flags->quiet_masks |= layer_bit;
>>>
>>> Why not store the quiet bit in masks? That would not only be "access"
>>> bits anymore but it makes sense to store all this bits it the same
>>> place.
>>>
>>> We should then probably rename struct layer_access_masks to just struct
>>> layer_masks.
>>>
>>> We need to be careful to not increase too much the size of this struct
>>> though while keeping the [LANDLOCK_MAX_NUM_LAYERS] approach if possible
>>> (see Günther's commit that added it).
>>
>> Most uses of struct layer_access_masks do not actually care about the rule
>> flags tho, e.g. in unmask_scoped_access, scope_to_request, or may_refer.
>> Such a rename would touch 31 places (and only a few of them would actually
>> touch the quiet flag).
>>
>> If we want to refactor to make this be in the layer_access_masks (then
>> rename it), I guess there are 3 options, which do you prefer?
>>
>> 1. Add a u16 bitfield for which layers are quieted. Future rule flags
>> will be additional bitfields. struct layer_masks becomes 68 bytes (+4).
>>
>> struct layer_masks {
>> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
>> layer_mask_t quiet_layers;
>> };
>>
>> 2. Make the [LANDLOCK_MAX_NUM_LAYERS] array store both the access mask and
>> the quiet bit (or more bits for future rule flags). Size of struct stays
>> the same.
>>
> This approach seems best.
>> static_assert(LANDLOCK_NUM_ACCESS_NET <= LANDLOCK_NUM_ACCESS_FS);
>> static_assert(LANDLOCK_NUM_SCOPE <= LANDLOCK_NUM_ACCESS_FS);
>> struct layer_mask {
>> access_mask_t access:LANDLOCK_NUM_ACCESS_FS;
>> bool quiet:1;
>> };
>
> Other way to do it could be an (anonymous?) union.
>
> union {
> access_mask_t fs_access:LANDLOCK_NUM_ACCESS_FS;
> access_mask_t net_access:LANDLOCK_NUM_ACCESS_NET;
> access_mask_t scope_access:LANDLOCK_NUM_SCOPE;
> }
>
> The union should be sized to fit the largest field automatically.
>
> That way you don't have to change this when adding new access rights
> and avoid the brittle static_asserts.
>
> Not sure about the alignment implications here though.
Unfortunately this forces struct layer_mask to be 2x as large:
https://godbolt.org/z/5P9b4rrMW
But it turns out I could have just used MAX, seems to compile for me:
struct layer_mask {
access_mask_t access
: MAX(LANDLOCK_NUM_ACCESS_FS,
MAX(LANDLOCK_NUM_ACCESS_NET, LANDLOCK_NUM_SCOPE));
bool quiet : 1;
};
struct layer_masks {
struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
};
Maybe we could #define LANDLOCK_NUM_ACCESS_MAX to be MAX(...) then use it
here.
I'm still not sure if putting the collected rule flags in struct
layer_(access_)masks is a good idea tho. Passing a separate struct
collected_rule_flags to the functions that needs to deal with rule flags
(quiet, and later, no inherit / has no inherit descendant) seems quite
practical to me.
^ permalink raw reply
* Re: [PATCH v8 1/9] landlock: Add a place for flags to layer rules
From: Mickaël Salaün @ 2026-05-24 20:35 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <617fdd53-6612-4f6f-b0e0-16d85985487b@maowtm.org>
On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
> On 5/23/26 21:48, Mickaël Salaün wrote:
> > This patch doesn't build.
>
> Missed a hunk in this patch (ended up in the next one), will add.
>
> >> @@ -797,20 +803,28 @@ is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
> >> }
> >>
> >> if (unlikely(dentry_child1)) {
> >> + /*
> >> + * Get the layer masks for the child dentries for use by domain
> >> + * check later. The rule_flags for child1 should have been
> >> + * included in rule_flags_parent1 already (cf.
> >> + * collect_domain_accesses), and is not relevant for domain check,
> >> + * so we don't have to pass it to landlock_unmask_layers.
> >> + */
> >> if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> >> &_layer_masks_child1,
> >> LANDLOCK_KEY_INODE))
> >> landlock_unmask_layers(find_rule(domain, dentry_child1),
> >> - &_layer_masks_child1);
> >> + &_layer_masks_child1, NULL);
> >> layer_masks_child1 = &_layer_masks_child1;
> >> child1_is_directory = d_is_dir(dentry_child1);
> >> }
> >> if (unlikely(dentry_child2)) {
> >> + /* See above comment for why NULL is passed as rule_flags_masks. */
> >
> > rule_flags_masks doesn't exist.
>
> I guess I was probably referring to the rule_flags argument - will fix.
>
> >> [...]
> >> @@ -647,9 +648,14 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
> >> */
> >> for (size_t i = 0; i < rule->num_layers; i++) {
> >> const struct landlock_layer *const layer = &rule->layers[i];
> >> + const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> >>
> >
> >> /* Clear the bits where the layer in the rule grants access. */
> >> masks->access[layer->level - 1] &= ~layer->access;
> >> +
> >> + /* Collect rule flags for each layer. */
> >> + if (rule_flags && layer->flags.quiet)
> >> + rule_flags->quiet_masks |= layer_bit;
> >
> > Why not store the quiet bit in masks? That would not only be "access"
> > bits anymore but it makes sense to store all this bits it the same
> > place.
> >
> > We should then probably rename struct layer_access_masks to just struct
> > layer_masks.
> >
> > We need to be careful to not increase too much the size of this struct
> > though while keeping the [LANDLOCK_MAX_NUM_LAYERS] approach if possible
> > (see Günther's commit that added it).
>
> Most uses of struct layer_access_masks do not actually care about the rule
> flags tho, e.g. in unmask_scoped_access, scope_to_request, or may_refer.
> Such a rename would touch 31 places (and only a few of them would actually
> touch the quiet flag).
Most of these places are tests.
>
> If we want to refactor to make this be in the layer_access_masks (then
> rename it), I guess there are 3 options, which do you prefer?
>
> 1. Add a u16 bitfield for which layers are quieted. Future rule flags
> will be additional bitfields. struct layer_masks becomes 68 bytes (+4).
>
> struct layer_masks {
> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> layer_mask_t quiet_layers;
> };
>
> 2. Make the [LANDLOCK_MAX_NUM_LAYERS] array store both the access mask and
> the quiet bit (or more bits for future rule flags). Size of struct stays
> the same.
>
> static_assert(LANDLOCK_NUM_ACCESS_NET <= LANDLOCK_NUM_ACCESS_FS);
> static_assert(LANDLOCK_NUM_SCOPE <= LANDLOCK_NUM_ACCESS_FS);
> struct layer_mask {
> access_mask_t access:LANDLOCK_NUM_ACCESS_FS;
> bool quiet:1;
> };
Yes, like Justin, I prefer this approach too. Some improvements:
// In limits.h:
#define LANDLOCK_MAX_NUM_ACCESSES \
MAX(LANDLOCK_NUM_ACCESS_FS, LANDLOCK_NUM_ACCESS_NET)
// In access.h:
struct layer_mask {
access_mask_t access:LANDLOCK_MAX_NUM_ACCESSES;
#ifdef CONFIG_AUDIT
bool quiet:1; // I'm not sure if using bool would work for all
// architectures though, but we can make sure with the following
// assert.
#endif /* CONFIG_AUDIT */
};
static_assert(sizeof(struct layer_mask), sizeof(access_mask_t));
> struct layer_masks {
> struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> (Maybe we can just make struct layer_masks a typedef to
> layer_mask[LANDLOCK_MAX_NUM_LAYERS] instead? But currently not sure if
> there are any gotchas with a typedef like that)
The only typedefs used in Landlock are for potentially growing types. So
no need for typedef here.
>
> 3. Mirror layer_access_masks::access[] - add a
> rule_flags[LANDLOCK_MAX_NUM_LAYERS] too. struct layer_masks becomes 80
> bytes (+16).
>
> struct rule_flags {
> bool quiet:1;
> };
> struct layer_masks {
> /**
> * @access: The unfulfilled access rights for each layer.
> */
> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> struct rule_flags rule_flags[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> (3 seems very wasteful to me)
>
^ permalink raw reply
* Re: [PATCH v8 2/9] landlock: Add API support and docs for the quiet flags
From: Mickaël Salaün @ 2026-05-24 20:35 UTC (permalink / raw)
To: Tingmao Wang
Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
linux-security-module
In-Reply-To: <b4d25793959493f0a8ef66f03feedac1a15e7595.1775490344.git.m@maowtm.org>
On Mon, Apr 06, 2026 at 04:52:15PM +0100, Tingmao Wang wrote:
> Adds the UAPI for the quiet flags feature (but not the implementation
> yet).
>
> According to pahole, even after adding the struct access_masks quiet_masks
> in struct landlock_hierarchy, the u32 log_* bitfield still only has a size
> of 2 bytes, so there's minimal wasted space.
>
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
>
> Changes in v8:
> - The new Landlock ABI version is now v10 as a result of rebase.
> - Allocate a rule_flags in hook_unix_find() and pass to
> is_access_to_paths_allowed().
>
> Changes in v6:
> - Fix typo in doc
>
> Changes in v5:
> - Doc fixes.
> - Fix build failure without CONFIG_AUDIT / CONFIG_INET (reported by Justin
> Suess)
>
> Changes in v4:
> - Minor update to this commit message.
> - Fix minor formatting
>
> Changes in v3:
> - Updated docs from Mickaël's suggestions.
>
> Changes in v2:
> - Per suggestion, added support for quieting only certain access bits,
> controlled by extra quiet_access_* fields in the ruleset_attr.
> - Added docs for the extra fields and made updates to doc changes in v1.
> In particular, call out that the effect of LANDLOCK_ADD_RULE_QUIET is
> independent from the access bits passed in rule_attr
> - landlock_add_rule will return -EINVAL when LANDLOCK_ADD_RULE_QUIET is
> used but the ruleset does not have any quiet access bits set for the
> given rule type.
> - ABI version bump to v8
> - Syntactic and comment changes per suggestion.
>
> include/uapi/linux/landlock.h | 64 +++++++++++++++++
> security/landlock/domain.h | 5 ++
> security/landlock/fs.c | 11 +--
> security/landlock/fs.h | 2 +-
> security/landlock/net.c | 5 +-
> security/landlock/net.h | 5 +-
> security/landlock/ruleset.c | 12 +++-
> security/landlock/ruleset.h | 12 +++-
> security/landlock/syscalls.c | 72 +++++++++++++++-----
> tools/testing/selftests/landlock/base_test.c | 6 +-
> 10 files changed, 160 insertions(+), 34 deletions(-)
>
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 10a346e55e95..9a41c65623a1 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -32,6 +32,19 @@
> * *handle* a wide range or all access rights that they know about at build time
> * (and that they have tested with a kernel that supported them all).
> *
> + * @quiet_access_fs and @quiet_access_net are bitmasks of actions for
> + * which a denial by this layer will not trigger an audit log if the
> + * corresponding object (or its children, for filesystem rules) is marked
> + * with the "quiet" bit via %LANDLOCK_ADD_RULE_QUIET, even if logging
> + * would normally take place per landlock_restrict_self() flags.
> + * quiet_scoped is similar, except that it does not require marking any
@quiet_scoped
> + * objects as quiet - if the ruleset is created with any bits set in
> + * quiet_scoped, then denial of such scoped resources will not trigger any
@quiet_scoped
> + * log. These 3 fields are available since Landlock ABI version 10.
> + *
> + * @quiet_access_fs, @quiet_access_net and @quiet_scoped must be a subset
> + * of @handled_access_fs, @handled_access_net and @scoped respectively.
> + *
> * This structure can grow in future Landlock versions.
> */
> struct landlock_ruleset_attr {
> @@ -51,6 +64,24 @@ struct landlock_ruleset_attr {
> * resources (e.g. IPCs).
> */
> __u64 scoped;
> +
> + /* Since ABI 10: */
No need for this comment and new lines.
> +
> + /**
> + * @quiet_access_fs: Bitmask of filesystem actions which should not be
> + * audit logged if per-object quiet flag is set.
Just "logged".
> + */
> + __u64 quiet_access_fs;
> + /**
> + * @quiet_access_net: Bitmask of network actions which should not be
> + * audit logged if per-object quiet flag is set.
ditto
> + */
> + __u64 quiet_access_net;
> + /**
> + * @quiet_scoped: Bitmask of scoped actions which should not be audit
> + * logged.
> + */
> + __u64 quiet_scoped;
> };
>
> /**
> @@ -69,6 +100,39 @@ struct landlock_ruleset_attr {
> #define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1)
> /* clang-format on */
>
> +/**
> + * DOC: landlock_add_rule_flags
> + *
> + * **Flags**
> + *
> + * %LANDLOCK_ADD_RULE_QUIET
> + * Together with the quiet_* fields in struct landlock_ruleset_attr,
> + * this flag controls whether Landlock will log audit messages when
> + * access to the objects covered by this rule is denied by this layer.
> + *
> + * If audit logging is enabled, when Landlock denies an access, it will
> + * suppress the audit log if all of the following are true:
> + *
> + * - this layer is the innermost layer that denied the access;
> + * - all accesses denied by this layer are part of the quiet_* fields
> + * in the related struct landlock_ruleset_attr;
> + * - the object (or one of its parents, for filesystem rules) is
> + * marked as "quiet" via %LANDLOCK_ADD_RULE_QUIET.
> + *
> + * Because logging is only suppressed by a layer if the layer denies
> + * access, a sandboxed program cannot use this flag to "hide" access
> + * denials, without denying itself the access in the first place.
This is not 100% correct: if a domain only handles/denies/quiet read, and a
parent domain denies write, open(, O_RDwR) would not generate a log,
which is OK.
> + *
> + * The effect of this flag does not depend on the value of
> + * allowed_access in the passed in rule_attr. When this flag is
> + * present, the caller is also allowed to pass in an empty
> + * allowed_access.
The audit/log part in Documentation/userspace-api/landlock.rst and
Documentation/security/landlock.rst should be updated to take this quiet
flags into account.
> + */
> +
> +/* clang-format off */
> +#define LANDLOCK_ADD_RULE_QUIET (1U << 0)
I think this name is correct because this flag will be used by the
supervisor feature, but otherwise it should be named something like
LANDLOCK_ADD_RULE_LOG_QUIET. Tingmao, do you think that makes sense?
If yes, it should be explained in the commit message that this quiet
flag may be used for some kind of notification...
> +/* clang-format on */
> +
> /**
> * DOC: landlock_restrict_self_flags
> *
> diff --git a/security/landlock/domain.h b/security/landlock/domain.h
> index a9d57db0120d..9b8aeac8ebd2 100644
> --- a/security/landlock/domain.h
> +++ b/security/landlock/domain.h
> @@ -114,6 +114,11 @@ struct landlock_hierarchy {
> * %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON. Set to false by default.
> */
> log_new_exec : 1;
> + /**
> + * @quiet_masks: Bitmasks of access that should be quieted (i.e. not
> + * logged) if the related object is marked as quiet.
> + */
> + struct access_masks quiet_masks;
> #endif /* CONFIG_AUDIT */
> };
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 6f63e0182ef0..06a8d2258558 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -325,7 +325,7 @@ static struct landlock_object *get_inode_object(struct inode *const inode)
> */
> int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> const struct path *const path,
> - access_mask_t access_rights)
> + access_mask_t access_rights, const int flags)
> {
> int err;
> struct landlock_id id = {
> @@ -346,7 +346,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> if (IS_ERR(id.key.object))
> return PTR_ERR(id.key.object);
> mutex_lock(&ruleset->lock);
> - err = landlock_insert_rule(ruleset, id, access_rights);
> + err = landlock_insert_rule(ruleset, id, access_rights, flags);
> mutex_unlock(&ruleset->lock);
> /*
> * No need to check for an error because landlock_insert_rule()
> @@ -1662,6 +1662,7 @@ static int hook_unix_find(const struct path *const path, struct sock *other,
> static const struct access_masks fs_resolve_unix = {
> .fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
> };
> + struct collected_rule_flags rule_flags = {};
>
> /* Lookup for the purpose of saving coredumps is OK. */
> if (unlikely(flags & SOCK_COREDUMP))
> @@ -1700,9 +1701,9 @@ static int hook_unix_find(const struct path *const path, struct sock *other,
> unix_state_unlock(other);
>
> /* Checks the connections to allow-listed paths. */
> - if (is_access_to_paths_allowed(subject->domain, path,
> - fs_resolve_unix.fs, &layer_masks,
> - &request, NULL, 0, NULL, NULL, NULL))
> + if (is_access_to_paths_allowed(
> + subject->domain, path, fs_resolve_unix.fs, &layer_masks,
> + &rule_flags, &request, NULL, 0, NULL, NULL, NULL, NULL))
> return 0;
>
> landlock_log_denial(subject, &request);
> diff --git a/security/landlock/fs.h b/security/landlock/fs.h
> index bf9948941f2f..cb7e654933ac 100644
> --- a/security/landlock/fs.h
> +++ b/security/landlock/fs.h
> @@ -126,6 +126,6 @@ __init void landlock_add_fs_hooks(void);
>
> int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
> const struct path *const path,
> - access_mask_t access_hierarchy);
> + access_mask_t access_hierarchy, const int flags);
>
> #endif /* _SECURITY_LANDLOCK_FS_H */
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index dc82ce4a2bd4..ade2b1750042 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -20,7 +20,8 @@
> #include "ruleset.h"
>
> int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> - const u16 port, access_mask_t access_rights)
> + const u16 port, access_mask_t access_rights,
> + const int flags)
> {
> int err;
> const struct landlock_id id = {
> @@ -35,7 +36,7 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> ~landlock_get_net_access_mask(ruleset, 0);
>
> mutex_lock(&ruleset->lock);
> - err = landlock_insert_rule(ruleset, id, access_rights);
> + err = landlock_insert_rule(ruleset, id, access_rights, flags);
> mutex_unlock(&ruleset->lock);
>
> return err;
> diff --git a/security/landlock/net.h b/security/landlock/net.h
> index 09960c237a13..72c47f4d6803 100644
> --- a/security/landlock/net.h
> +++ b/security/landlock/net.h
> @@ -16,7 +16,8 @@
> __init void landlock_add_net_hooks(void);
>
> int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> - const u16 port, access_mask_t access_rights);
> + const u16 port, access_mask_t access_rights,
> + const int flags);
> #else /* IS_ENABLED(CONFIG_INET) */
> static inline void landlock_add_net_hooks(void)
> {
> @@ -24,7 +25,7 @@ static inline void landlock_add_net_hooks(void)
>
> static inline int
> landlock_append_net_rule(struct landlock_ruleset *const ruleset, const u16 port,
> - access_mask_t access_rights)
> + access_mask_t access_rights, const int flags)
> {
> return -EAFNOSUPPORT;
> }
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index e4e6b730b581..d2d1e3fb6cf2 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -21,6 +21,7 @@
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/workqueue.h>
> +#include <uapi/linux/landlock.h>
>
> #include "access.h"
> #include "domain.h"
> @@ -255,6 +256,7 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
> if (WARN_ON_ONCE(this->layers[0].level != 0))
> return -EINVAL;
> this->layers[0].access |= (*layers)[0].access;
> + this->layers[0].flags.quiet |= (*layers)[0].flags.quiet;
> return 0;
> }
>
> @@ -305,12 +307,15 @@ static void build_check_layer(void)
> /* @ruleset must be locked by the caller. */
> int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> const struct landlock_id id,
> - const access_mask_t access)
> + const access_mask_t access, const int flags)
> {
> struct landlock_layer layers[] = { {
> .access = access,
> /* When @level is zero, insert_rule() extends @ruleset. */
> .level = 0,
> + .flags = {
> + .quiet = !!(flags & LANDLOCK_ADD_RULE_QUIET),
> + },
> } };
>
> build_check_layer();
> @@ -351,6 +356,7 @@ static int merge_tree(struct landlock_ruleset *const dst,
> return -EINVAL;
>
> layers[0].access = walker_rule->layers[0].access;
> + layers[0].flags = walker_rule->layers[0].flags;
>
> err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
> if (err)
> @@ -581,6 +587,10 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
> if (err)
> return ERR_PTR(err);
>
> +#ifdef CONFIG_AUDIT
> + new_dom->hierarchy->quiet_masks = ruleset->quiet_masks;
> +#endif /* CONFIG_AUDIT */
> +
> return no_free_ptr(new_dom);
> }
>
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 3b31552f0c95..e369f15ae885 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -172,8 +172,8 @@ struct landlock_ruleset {
> * @work_free: Enables to free a ruleset within a lockless
> * section. This is only used by
> * landlock_put_ruleset_deferred() when @usage reaches zero.
> - * The fields @lock, @usage, @num_rules, @num_layers and
> - * @access_masks are then unused.
> + * The fields @lock, @usage, @num_rules, @num_layers, @quiet_masks
> + * and @access_masks are then unused.
> */
> struct work_struct work_free;
> struct {
> @@ -199,6 +199,12 @@ struct landlock_ruleset {
> * non-merged ruleset (i.e. not a domain).
> */
> u32 num_layers;
> + /**
> + * @quiet_masks: Stores the quiet flags for an unmerged
> + * ruleset. For a merged domain, this is stored in each
> + * layer's struct landlock_hierarchy instead.
> + */
> + struct access_masks quiet_masks;
> /**
> * @access_masks: Contains the subset of filesystem and
> * network actions that are restricted by a ruleset.
> @@ -229,7 +235,7 @@ DEFINE_FREE(landlock_put_ruleset, struct landlock_ruleset *,
>
> int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> const struct landlock_id id,
> - const access_mask_t access);
> + const access_mask_t access, const int flags);
>
> struct landlock_ruleset *
> landlock_merge_ruleset(struct landlock_ruleset *const parent,
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index accfd2e5a0cd..a71068c41f76 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -105,8 +105,11 @@ static void build_check_abi(void)
> ruleset_size = sizeof(ruleset_attr.handled_access_fs);
> ruleset_size += sizeof(ruleset_attr.handled_access_net);
> ruleset_size += sizeof(ruleset_attr.scoped);
> + ruleset_size += sizeof(ruleset_attr.quiet_access_fs);
> + ruleset_size += sizeof(ruleset_attr.quiet_access_net);
> + ruleset_size += sizeof(ruleset_attr.quiet_scoped);
> BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
> - BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
> + BUILD_BUG_ON(sizeof(ruleset_attr) != 48);
>
> path_beneath_size = sizeof(path_beneath_attr.allowed_access);
> path_beneath_size += sizeof(path_beneath_attr.parent_fd);
> @@ -166,7 +169,7 @@ static const struct file_operations ruleset_fops = {
> * If the change involves a fix that requires userspace awareness, also update
> * the errata documentation in Documentation/userspace-api/landlock.rst .
> */
> -const int landlock_abi_version = 9;
> +const int landlock_abi_version = 10;
>
> /**
> * sys_landlock_create_ruleset - Create a new ruleset
> @@ -193,6 +196,8 @@ const int landlock_abi_version = 9;
> * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small
> * @size;
> + * - %EINVAL: quiet_access_fs or quiet_access_net is not a subset of the
> + * corresponding handled_access_fs or handled_access_net;
> * - %E2BIG: @attr or @size inconsistencies;
> * - %EFAULT: @attr or @size inconsistencies;
> * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
> @@ -249,6 +254,21 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
> return -EINVAL;
>
> + /*
> + * Check that quiet masks are subsets of the respective handled masks.
> + * Because of the checks above this is sufficient to also ensure that
> + * the quiet masks are valid access masks.
> + */
> + if ((ruleset_attr.quiet_access_fs | ruleset_attr.handled_access_fs) !=
> + ruleset_attr.handled_access_fs)
> + return -EINVAL;
> + if ((ruleset_attr.quiet_access_net | ruleset_attr.handled_access_net) !=
> + ruleset_attr.handled_access_net)
> + return -EINVAL;
> + if ((ruleset_attr.quiet_scoped | ruleset_attr.scoped) !=
> + ruleset_attr.scoped)
> + return -EINVAL;
> +
> /* Checks arguments and transforms to kernel struct. */
> ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
> ruleset_attr.handled_access_net,
> @@ -256,6 +276,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> if (IS_ERR(ruleset))
> return PTR_ERR(ruleset);
>
> + ruleset->quiet_masks.fs = ruleset_attr.quiet_access_fs;
> + ruleset->quiet_masks.net = ruleset_attr.quiet_access_net;
> + ruleset->quiet_masks.scope = ruleset_attr.quiet_scoped;
> +
> /* Creates anonymous FD referring to the ruleset. */
> ruleset_fd = anon_inode_getfd("[landlock-ruleset]", &ruleset_fops,
> ruleset, O_RDWR | O_CLOEXEC);
> @@ -320,7 +344,7 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
> }
>
> static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
> - const void __user *const rule_attr)
> + const void __user *const rule_attr, int flags)
> {
> struct landlock_path_beneath_attr path_beneath_attr;
> struct path path;
> @@ -335,9 +359,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>
> /*
> * Informs about useless rule: empty allowed_access (i.e. deny rules)
> - * are ignored in path walks.
> + * are ignored in path walks. However, the rule is not useless if it
> + * is there to hold a quiet flag
Missing trailing period.
> */
> - if (!path_beneath_attr.allowed_access)
> + if (!flags && !path_beneath_attr.allowed_access)
> return -ENOMSG;
>
> /* Checks that allowed_access matches the @ruleset constraints. */
> @@ -345,6 +370,10 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
> if ((path_beneath_attr.allowed_access | mask) != mask)
> return -EINVAL;
>
> + /* Check for useless quiet flag. */
Checks... (for consistency with comments above)
> + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.fs)
> + return -EINVAL;
> +
> /* Gets and checks the new rule. */
> err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
> if (err)
> @@ -352,13 +381,13 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>
> /* Imports the new rule. */
> err = landlock_append_fs_rule(ruleset, &path,
> - path_beneath_attr.allowed_access);
> + path_beneath_attr.allowed_access, flags);
> path_put(&path);
> return err;
> }
>
> static int add_rule_net_port(struct landlock_ruleset *ruleset,
> - const void __user *const rule_attr)
> + const void __user *const rule_attr, int flags)
> {
> struct landlock_net_port_attr net_port_attr;
> int res;
> @@ -371,9 +400,10 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
>
> /*
> * Informs about useless rule: empty allowed_access (i.e. deny rules)
> - * are ignored by network actions.
> + * are ignored by network actions. However, the rule is not useless
> + * if it is there to hold a quiet flag
> */
> - if (!net_port_attr.allowed_access)
> + if (!flags && !net_port_attr.allowed_access)
> return -ENOMSG;
>
> /* Checks that allowed_access matches the @ruleset constraints. */
> @@ -381,13 +411,17 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> if ((net_port_attr.allowed_access | mask) != mask)
> return -EINVAL;
>
> + /* Check for useless quiet flag. */
> + if (flags & LANDLOCK_ADD_RULE_QUIET && !ruleset->quiet_masks.net)
> + return -EINVAL;
> +
> /* Denies inserting a rule with port greater than 65535. */
> if (net_port_attr.port > U16_MAX)
> return -EINVAL;
>
> /* Imports the new rule. */
> return landlock_append_net_rule(ruleset, net_port_attr.port,
> - net_port_attr.allowed_access);
> + net_port_attr.allowed_access, flags);
> }
>
> /**
> @@ -398,7 +432,7 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> * @rule_type: Identify the structure type pointed to by @rule_attr:
> * %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_PORT.
> * @rule_attr: Pointer to a rule (matching the @rule_type).
> - * @flags: Must be 0.
> + * @flags: Must be 0 or %LANDLOCK_ADD_RULE_QUIET.
> *
> * This system call enables to define a new rule and add it to an existing
> * ruleset.
> @@ -408,20 +442,25 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> * - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not
> * supported by the running kernel;
> - * - %EINVAL: @flags is not 0;
> + * - %EINVAL: @flags is not valid;
> * - %EINVAL: The rule accesses are inconsistent (i.e.
> * &landlock_path_beneath_attr.allowed_access or
> * &landlock_net_port_attr.allowed_access is not a subset of the ruleset
> * handled accesses)
> * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
> + * - %EINVAL: LANDLOCK_ADD_RULE_QUIET is passed but the ruleset has no
> + * quiet access bits set for the corresponding rule type.
> * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
> - * 0);
> + * 0) and no flags;
> * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
> * member of @rule_attr is not a file descriptor as expected;
> * - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
> * @rule_attr is not the expected file descriptor type;
> * - %EPERM: @ruleset_fd has no write access to the underlying ruleset;
> * - %EFAULT: @rule_attr was not a valid address.
> + *
> + * .. kernel-doc:: include/uapi/linux/landlock.h
> + * :identifiers: landlock_add_rule_flags
> */
> SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
> const enum landlock_rule_type, rule_type,
> @@ -432,8 +471,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
> if (!is_initialized())
> return -EOPNOTSUPP;
>
> - /* No flag for now. */
> - if (flags)
> + if (flags && flags != LANDLOCK_ADD_RULE_QUIET)
> return -EINVAL;
>
> /* Gets and checks the ruleset. */
> @@ -443,9 +481,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>
> switch (rule_type) {
> case LANDLOCK_RULE_PATH_BENEATH:
> - return add_rule_path_beneath(ruleset, rule_attr);
> + return add_rule_path_beneath(ruleset, rule_attr, flags);
> case LANDLOCK_RULE_NET_PORT:
> - return add_rule_net_port(ruleset, rule_attr);
> + return add_rule_net_port(ruleset, rule_attr, flags);
> default:
> return -EINVAL;
> }
> diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
> index 30d37234086c..84e91fcaa1b2 100644
> --- a/tools/testing/selftests/landlock/base_test.c
> +++ b/tools/testing/selftests/landlock/base_test.c
> @@ -76,8 +76,8 @@ TEST(abi_version)
> const struct landlock_ruleset_attr ruleset_attr = {
> .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
> };
> - ASSERT_EQ(9, landlock_create_ruleset(NULL, 0,
> - LANDLOCK_CREATE_RULESET_VERSION));
> + ASSERT_EQ(10, landlock_create_ruleset(NULL, 0,
> + LANDLOCK_CREATE_RULESET_VERSION));
>
> ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
> LANDLOCK_CREATE_RULESET_VERSION));
> @@ -201,7 +201,7 @@ TEST(add_rule_checks_ordering)
> ASSERT_LE(0, ruleset_fd);
>
> /* Checks invalid flags. */
> - ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1));
> + ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 100));
> ASSERT_EQ(EINVAL, errno);
>
> /* Checks invalid ruleset FD. */
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH v8 1/9] landlock: Add a place for flags to layer rules
From: Justin Suess @ 2026-05-24 22:08 UTC (permalink / raw)
To: Tingmao Wang
Cc: Mickaël Salaün, Günther Noack, Jan Kara,
Abhinav Saxena, linux-security-module
In-Reply-To: <42a06f7c-abb5-4f2d-8428-8d122047e8d4@maowtm.org>
On Sun, May 24, 2026 at 07:20:19PM +0100, Tingmao Wang wrote:
> On 5/24/26 15:46, Justin Suess wrote:
> > On Sun, May 24, 2026 at 02:29:40AM +0100, Tingmao Wang wrote:
> >> On 5/23/26 21:48, Mickaël Salaün wrote:
> >>> [...]
> >>>> @@ -647,9 +648,14 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
> >>>> */
> >>>> for (size_t i = 0; i < rule->num_layers; i++) {
> >>>> const struct landlock_layer *const layer = &rule->layers[i];
> >>>> + const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> >>>>
> >>>
> >>>> /* Clear the bits where the layer in the rule grants access. */
> >>>> masks->access[layer->level - 1] &= ~layer->access;
> >>>> +
> >>>> + /* Collect rule flags for each layer. */
> >>>> + if (rule_flags && layer->flags.quiet)
> >>>> + rule_flags->quiet_masks |= layer_bit;
> >>>
> >>> Why not store the quiet bit in masks? That would not only be "access"
> >>> bits anymore but it makes sense to store all this bits it the same
> >>> place.
> >>>
> >>> We should then probably rename struct layer_access_masks to just struct
> >>> layer_masks.
> >>>
> >>> We need to be careful to not increase too much the size of this struct
> >>> though while keeping the [LANDLOCK_MAX_NUM_LAYERS] approach if possible
> >>> (see Günther's commit that added it).
> >>
> >> Most uses of struct layer_access_masks do not actually care about the rule
> >> flags tho, e.g. in unmask_scoped_access, scope_to_request, or may_refer.
> >> Such a rename would touch 31 places (and only a few of them would actually
> >> touch the quiet flag).
> >>
> >> If we want to refactor to make this be in the layer_access_masks (then
> >> rename it), I guess there are 3 options, which do you prefer?
> >>
> >> 1. Add a u16 bitfield for which layers are quieted. Future rule flags
> >> will be additional bitfields. struct layer_masks becomes 68 bytes (+4).
> >>
> >> struct layer_masks {
> >> access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
> >> layer_mask_t quiet_layers;
> >> };
> >>
> >> 2. Make the [LANDLOCK_MAX_NUM_LAYERS] array store both the access mask and
> >> the quiet bit (or more bits for future rule flags). Size of struct stays
> >> the same.
> >>
> > This approach seems best.
> >> static_assert(LANDLOCK_NUM_ACCESS_NET <= LANDLOCK_NUM_ACCESS_FS);
> >> static_assert(LANDLOCK_NUM_SCOPE <= LANDLOCK_NUM_ACCESS_FS);
> >> struct layer_mask {
> >> access_mask_t access:LANDLOCK_NUM_ACCESS_FS;
> >> bool quiet:1;
> >> };
> >
> > Other way to do it could be an (anonymous?) union.
> >
> > union {
> > access_mask_t fs_access:LANDLOCK_NUM_ACCESS_FS;
> > access_mask_t net_access:LANDLOCK_NUM_ACCESS_NET;
> > access_mask_t scope_access:LANDLOCK_NUM_SCOPE;
> > }
> >
> > The union should be sized to fit the largest field automatically.
> >
> > That way you don't have to change this when adding new access rights
> > and avoid the brittle static_asserts.
> >
> > Not sure about the alignment implications here though.
>
> Unfortunately this forces struct layer_mask to be 2x as large:
> https://godbolt.org/z/5P9b4rrMW
>
Yeah I guess the compiler can't pack the fields with differing types.
*In theory* you could make everything a _BitInt or something but it
seems better to do what you had below.
> But it turns out I could have just used MAX, seems to compile for me:
>
> struct layer_mask {
> access_mask_t access
> : MAX(LANDLOCK_NUM_ACCESS_FS,
> MAX(LANDLOCK_NUM_ACCESS_NET, LANDLOCK_NUM_SCOPE));
> bool quiet : 1;
> };
This works perfectly.
Mickaël's suggestion (except w/ all three access right classes like
you have here, think he missed LANDLOCK_NUM_SCOPE) is very close
to this.
> struct layer_masks {
> struct layer_mask layer[LANDLOCK_MAX_NUM_LAYERS];
> };
>
> Maybe we could #define LANDLOCK_NUM_ACCESS_MAX to be MAX(...) then use it
> here.
>
> I'm still not sure if putting the collected rule flags in struct
> layer_(access_)masks is a good idea tho. Passing a separate struct
> collected_rule_flags to the functions that needs to deal with rule flags
> (quiet, and later, no inherit / has no inherit descendant) seems quite
> practical to me.
(Not sure how stingy we gotta be with stack space)
There's a *slight* stack space advantage to keeping them together.
If you pass by value, (separate layer_access_masks, collected_rule_flags),
those structs must be individually padded and aligned. Which may or may not
make a difference, it's dependent on alignment and architecture.
Whereas if we keep them all together, we only pad once.
If you pass by pointer, you have to allocate stack space for each
pointer, so passing it all at once saves sizeof(collected_rule_flags*)
bytes in the pass by pointer case.
Either way it's probably a couple bytes at worst, so probably nothing to
worry about.
The more compelling argument is that we don't know how future paths
will use rule flags, so keeping it all together reduces churn later
if a function ends up needing to access flags. Moreover, it makes those
messy function signatures in fs.h/c a little less hairy, and easy to
refactor later.
^ permalink raw reply
* [PATCH 0/2] smack: restrict smackfs/{direct,mapped} values to 0-255
From: Konstantin Andreev @ 2026-05-24 22:37 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
Both smackfs/direct and smackfs/mapped incorrectly accept
the full range of integer values.
2nd patch restricts range.
1st patch deduplicates file_operations code
to apply the fix in a single place
The patch set applies on top of:
https://github.com/cschaufler/smack-next/commits/next
commit b78fede1c69a
Konstantin Andreev (2):
smack: deduplicate smackfs/{direct,mapped} file_operations
smack: restrict smackfs/{direct,mapped} values to 0-255
security/smack/smack.h | 5 +-
security/smack/smackfs.c | 153 +++++++++++++--------------------------
2 files changed, 53 insertions(+), 105 deletions(-)
--
2.47.3
^ permalink raw reply
* [PATCH 1/2] smack: deduplicate smackfs/{direct,mapped} file_operations
From: Konstantin Andreev @ 2026-05-24 22:37 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <20260524223749.50874-1-andreev@swemel.ru>
The file_operations for smackfs/direct and smackfs/mapped are
identical up to a textual replacement of "direct" with "mapped"
This patch combines two instances of file_operations into one,
handling both files.
Fixes: f7112e6c9abf ("Smack: allow for significantly longer Smack labels v4")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack.h | 5 +-
security/smack/smackfs.c | 135 ++++++++++++---------------------------
2 files changed, 43 insertions(+), 97 deletions(-)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 9b9eb262fe33..6febc2ecdfe8 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -317,8 +317,9 @@ int smack_populate_secattr(struct smack_known *skp);
* Shared data.
*/
extern int smack_enabled __initdata;
-extern int smack_cipso_direct;
-extern int smack_cipso_mapped;
+extern int smack_cipso_auto_level[2];
+#define smack_cipso_direct (+smack_cipso_auto_level[0])
+#define smack_cipso_mapped (+smack_cipso_auto_level[1])
extern struct smack_known *smack_net_ambient;
extern struct smack_known *smack_syslog_label;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f60d5469043e..946405645d5a 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -83,18 +83,27 @@ static DEFINE_MUTEX(smk_net6addr_lock);
struct smack_known *smack_net_ambient;
/*
- * This is the level in a CIPSO header that indicates a
+ * Sensitivity levels for automatically created CIPSO labels.
+ * See smack_access.c`smack_populate_secattr()
+ *
+ * [0] "direct" labeling, label length < SMK_CIPSOLEN(24):
* smack label is contained directly in the category set.
* It can be reset via smackfs/direct
- */
-int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
-
-/*
- * This is the level in a CIPSO header that indicates a
+ *
+ * [1] "mapped" labeling, label length >= SMK_CIPSOLEN(24):
* secid is contained directly in the category set.
* It can be reset via smackfs/mapped
*/
-int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
+int smack_cipso_auto_level[2] = {
+ SMACK_CIPSO_DIRECT_DEFAULT,
+ SMACK_CIPSO_MAPPED_DEFAULT,
+};
+
+static int
+smk_cipso_auto_level_idx(const struct file *file)
+{
+ return (file_inode(file)->i_ino != SMK_DIRECT);
+}
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
@@ -1621,15 +1630,15 @@ static const struct file_operations smk_doi_ops = {
};
/**
- * smk_read_direct - read() for /smack/direct
- * @filp: file pointer, not actually used
+ * smk_read_cipso_auto_level - read() for smackfs/direct and smackfs/mapped
+ * @filp: file pointer
* @buf: where to put the result
* @count: maximum to send along
* @ppos: where to start
*
* Returns number of bytes read or error code, as appropriate
*/
-static ssize_t smk_read_direct(struct file *filp, char __user *buf,
+static ssize_t smk_read_cipso_auto_level(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
@@ -1638,26 +1647,28 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf,
if (*ppos != 0)
return 0;
- sprintf(temp, "%d", smack_cipso_direct);
+ sprintf(temp, "%d", smack_cipso_auto_level[
+ smk_cipso_auto_level_idx(filp)]);
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
return rc;
}
/**
- * smk_write_direct - write() for /smack/direct
- * @file: file pointer, not actually used
+ * smk_write_cipso_auto_level - write() for smackfs/direct and smackfs/mapped
+ * @filp: file pointer
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
* Returns number of bytes written or error code, as appropriate
*/
-static ssize_t smk_write_direct(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t
+smk_write_cipso_auto_level(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
{
struct smack_known *skp;
- int i, ret;
+ int i, ret, idx, old_lvl;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
@@ -1669,94 +1680,28 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf,
/*
* Don't do anything if the value hasn't actually changed.
* If it is changing reset the level on entries that were
- * set up to be direct when they were created.
+ * set up to be "auto" level when they were created.
*/
- if (smack_cipso_direct != i) {
+ idx = smk_cipso_auto_level_idx(filp);
+ old_lvl = smack_cipso_auto_level[idx];
+
+ if (old_lvl != i) {
mutex_lock(&smack_known_lock);
list_for_each_entry_rcu(skp, &smack_known_list, list)
if (skp->smk_netlabel.attr.mls.lvl ==
- smack_cipso_direct)
+ old_lvl)
skp->smk_netlabel.attr.mls.lvl = i;
- smack_cipso_direct = i;
+ smack_cipso_auto_level[idx] = i;
mutex_unlock(&smack_known_lock);
}
return count;
}
-static const struct file_operations smk_direct_ops = {
- .read = smk_read_direct,
- .write = smk_write_direct,
- .llseek = default_llseek,
-};
-
-/**
- * smk_read_mapped - read() for /smack/mapped
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @count: maximum to send along
- * @ppos: where to start
- *
- * Returns number of bytes read or error code, as appropriate
- */
-static ssize_t smk_read_mapped(struct file *filp, char __user *buf,
- size_t count, loff_t *ppos)
-{
- char temp[80];
- ssize_t rc;
-
- if (*ppos != 0)
- return 0;
-
- sprintf(temp, "%d", smack_cipso_mapped);
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
-
- return rc;
-}
-
-/**
- * smk_write_mapped - write() for /smack/mapped
- * @file: file pointer, not actually used
- * @buf: where to get the data from
- * @count: bytes sent
- * @ppos: where to start
- *
- * Returns number of bytes written or error code, as appropriate
- */
-static ssize_t smk_write_mapped(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct smack_known *skp;
- int i, ret;
-
- if (!smack_privileged(CAP_MAC_ADMIN))
- return -EPERM;
-
- ret = kstrtos32_from_user(buf, count, 10, &i);
- if (unlikely(ret))
- return ret;
-
- /*
- * Don't do anything if the value hasn't actually changed.
- * If it is changing reset the level on entries that were
- * set up to be mapped when they were created.
- */
- if (smack_cipso_mapped != i) {
- mutex_lock(&smack_known_lock);
- list_for_each_entry_rcu(skp, &smack_known_list, list)
- if (skp->smk_netlabel.attr.mls.lvl ==
- smack_cipso_mapped)
- skp->smk_netlabel.attr.mls.lvl = i;
- smack_cipso_mapped = i;
- mutex_unlock(&smack_known_lock);
- }
-
- return count;
-}
-
-static const struct file_operations smk_mapped_ops = {
- .read = smk_read_mapped,
- .write = smk_write_mapped,
+static const struct file_operations
+smk_cipso_auto_level_ops = {
+ .read = smk_read_cipso_auto_level,
+ .write = smk_write_cipso_auto_level,
.llseek = default_llseek,
};
@@ -2851,7 +2796,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
[SMK_DOI] = {
"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
[SMK_DIRECT] = {
- "direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
+ "direct", &smk_cipso_auto_level_ops, 0644},
[SMK_AMBIENT] = {
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NET4ADDR] = {
@@ -2867,7 +2812,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
[SMK_ACCESSES] = {
"access", &smk_access_ops, S_IRUGO|S_IWUGO},
[SMK_MAPPED] = {
- "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR},
+ "mapped", &smk_cipso_auto_level_ops, 0644},
[SMK_LOAD2] = {
"load2", &smk_load2_ops, S_IRUGO|S_IWUSR},
[SMK_LOAD_SELF2] = {
--
2.47.3
^ permalink raw reply related
* [PATCH 2/2] smack: restrict smackfs/{direct,mapped} values to 0-255
From: Konstantin Andreev @ 2026-05-24 22:37 UTC (permalink / raw)
To: casey; +Cc: linux-security-module
In-Reply-To: <20260524223749.50874-1-andreev@swemel.ru>
Both smackfs/direct and smackfs/mapped incorrectly accept
the full range of integer values. For example:
# cd /sys/fs/smackfs/
# cat direct ; echo
250
# cat cipso2
@ 250/2
_ 250/2,4,5,6,7,8
* 250/3,5,7
^ 250/2,4,5,6,7
? 250/3,4,5,6,7,8
# echo -1234 >direct ; cat direct ; echo
-1234
# cat cipso2
@ -1234/2
_ -1234/2,4,5,6,7,8
* -1234/3,5,7
^ -1234/2,4,5,6,7
? -1234/3,4,5,6,7,8
#
I noticed two things regarding this:
1) sensitivity levels are truncated to 8 bits when labeling
outgoing packets (0x2e = 46 for the -1234 example above)
2) the reverse process fails: incoming packets with sensitivity
level 46 do not match these smackfs/cipso2 entries.
Even observation (1) on its own warrants a fix.
This patch restricts smackfs/direct and smackfs/mapped
accepted values to the 0-255 range.
Fixes: e114e473771c ("Smack: Simplified Mandatory Access Control Kernel")
Signed-off-by: Konstantin Andreev <andreev@swemel.ru>
---
security/smack/smack.h | 2 +-
security/smack/smackfs.c | 26 ++++++++++++++------------
2 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 6febc2ecdfe8..fe6a49820014 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -317,7 +317,7 @@ int smack_populate_secattr(struct smack_known *skp);
* Shared data.
*/
extern int smack_enabled __initdata;
-extern int smack_cipso_auto_level[2];
+extern u8 smack_cipso_auto_level[2];
#define smack_cipso_direct (+smack_cipso_auto_level[0])
#define smack_cipso_mapped (+smack_cipso_auto_level[1])
extern struct smack_known *smack_net_ambient;
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 946405645d5a..c7eae7c6427f 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -94,7 +94,7 @@ struct smack_known *smack_net_ambient;
* secid is contained directly in the category set.
* It can be reset via smackfs/mapped
*/
-int smack_cipso_auto_level[2] = {
+u8 smack_cipso_auto_level[2] = {
SMACK_CIPSO_DIRECT_DEFAULT,
SMACK_CIPSO_MAPPED_DEFAULT,
};
@@ -1641,17 +1641,15 @@ static const struct file_operations smk_doi_ops = {
static ssize_t smk_read_cipso_auto_level(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
- char temp[80];
- ssize_t rc;
+ char temp[sizeof "255"];
+ int n;
if (*ppos != 0)
return 0;
- sprintf(temp, "%d", smack_cipso_auto_level[
- smk_cipso_auto_level_idx(filp)]);
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
-
- return rc;
+ n = sprintf(temp, "%u", (unsigned int)smack_cipso_auto_level[
+ smk_cipso_auto_level_idx(filp)]);
+ return simple_read_from_buffer(buf, count, ppos, temp, n);
}
/**
@@ -1667,13 +1665,16 @@ static ssize_t
smk_write_cipso_auto_level(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct smack_known *skp;
- int i, ret, idx, old_lvl;
+ int ret, idx;
+ u8 i, old_lvl;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
-
- ret = kstrtos32_from_user(buf, count, 10, &i);
+ /*
+ * draft-ietf-cipso-ipsecurity-01 (CIPSO 2.2), 3.4.2.4:
+ * "Sensitivity Level is 1 octet in length. Its value is from 0 to 255"
+ */
+ ret = kstrtou8_from_user(buf, count, 10, &i);
if (unlikely(ret))
return ret;
@@ -1686,6 +1687,7 @@ smk_write_cipso_auto_level(struct file *filp, const char __user *buf,
old_lvl = smack_cipso_auto_level[idx];
if (old_lvl != i) {
+ struct smack_known *skp;
mutex_lock(&smack_known_lock);
list_for_each_entry_rcu(skp, &smack_known_list, list)
if (skp->smk_netlabel.attr.mls.lvl ==
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v8 0/3]
From: Jarkko Sakkinen @ 2026-05-24 23:18 UTC (permalink / raw)
To: keyrings
Cc: David Howells, linux-crypto, linux-integrity, David Woodhouse,
James Bottomley, Stefan Berger, Herbert Xu, Mimi Zohar,
Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org>
On Sun, May 24, 2026 at 08:15:11AM +0300, Jarkko Sakkinen wrote:
> This series introduces key type for operating with asymmetric keys using
> a TPM2 chip.
>
> Change Log
> ==========
>
> v8:
> - Reset patch change logs given the overhaul of the code and patches.
> - Have only single new subkey type.
> - Make key type only use TPM operations.
> - Use TPM2_Sign for both ECC and RSA keys.
> - Align key descriptions with other key types.
>
> Previous versions
> =================
>
> * v7: https://lore.kernel.org/linux-integrity/20240528210823.28798-1-jarkko@kernel.org/
> * v6: https://lore.kernel.org/linux-integrity/20240528035136.11464-1-jarkko@kernel.org/
> * v5: https://lore.kernel.org/linux-integrity/20240523212515.4875-1-jarkko@kernel.org/
> * v4: https://lore.kernel.org/linux-integrity/20240522005252.17841-1-jarkko@kernel.org/
> * v3: https://lore.kernel.org/linux-integrity/20240521152659.26438-1-jarkko@kernel.org/
> * v2: https://lore.kernel.org/linux-integrity/336755.1716327854@warthog.procyon.org.uk/
> * v1: https://lore.kernel.org/linux-integrity/20240520184727.22038-1-jarkko@kernel.org/
> * Derived from https://lore.kernel.org/all/20200518172704.29608-1-prestwoj@gmail.com/
>
>
> Jarkko Sakkinen (3):
> lib/asn1_encoder: Add asn1_encode_integer_bytes()
> crypto: Migrate TPMKey ASN.1 objects from trusted-keys
> keys: asymmetric: tpm2_asymmetric
>
> crypto/Kconfig | 7 +
> crypto/Makefile | 6 +
> crypto/asymmetric_keys/Kconfig | 17 +
> crypto/asymmetric_keys/Makefile | 1 +
> crypto/asymmetric_keys/tpm2_asymmetric.c | 1096 +++++++++++++++++++++
> crypto/tpm2_key.asn1 | 11 +
> crypto/tpm2_key.c | 150 +++
> include/crypto/tpm2_key.h | 46 +
> include/linux/asn1_encoder.h | 3 +
> include/linux/tpm.h | 10 +
> lib/asn1_encoder.c | 62 ++
> security/keys/trusted-keys/Kconfig | 2 +-
> security/keys/trusted-keys/Makefile | 2 -
> security/keys/trusted-keys/tpm2key.asn1 | 11 -
> security/keys/trusted-keys/trusted_tpm2.c | 119 +--
> 15 files changed, 1421 insertions(+), 122 deletions(-)
> create mode 100644 crypto/asymmetric_keys/tpm2_asymmetric.c
> create mode 100644 crypto/tpm2_key.asn1
> create mode 100644 crypto/tpm2_key.c
> create mode 100644 include/crypto/tpm2_key.h
> delete mode 100644 security/keys/trusted-keys/tpm2key.asn1
>
> --
> 2.47.3
>
Oops, I deleted the subject line, it was unintentional :-)
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v8 0/3]
From: Jarkko Sakkinen @ 2026-05-24 23:43 UTC (permalink / raw)
To: keyrings
Cc: David Howells, linux-crypto, linux-integrity, David Woodhouse,
James Bottomley, Stefan Berger, Herbert Xu, Mimi Zohar,
Paul Moore, James Morris, Serge E. Hallyn,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260524051519.3708075-1-jarkko@kernel.org>
On Sun, May 24, 2026 at 08:15:11AM +0300, Jarkko Sakkinen wrote:
> This series introduces key type for operating with asymmetric keys using
> a TPM2 chip.
This would deserve more explanation but the original trait was to
implement TPM2 parts of:
https://datatracker.ietf.org/doc/draft-woodhouse-cert-best-practice/00/
What motivated me to reiterate are actually these coding agents and how
all secrets are sprayed across the home directory. So, besides iwd one
could use this feature to provide per-session cryptography for coding
agents.
There's a lot to do with security and coding agents as we have literally
moved to an era where we host indeterministically rogues software in our
development workstations.
There's other questions too that we need to eventually answer like for
instace, how to deal with persistent agent memory stored at the
computer's hard drive?
The irony here is that LLM is really neither rogue nor a lier. It is
just a text predictor optimizing for maximum reward and those
descriptions are just human interpretations of the output text. It
understand neither evil, lying nor quality for that matter ;-)
BR, Jarkko
^ permalink raw reply
* [PATCH] Fix various spelling mistakes
From: fffsqian @ 2026-05-25 2:15 UTC (permalink / raw)
To: john.johansen, casey
Cc: paul, jmorris, serge, linux-security-module, linux-kernel,
Qingshuang Fu
From: Qingshuang Fu <fuqingshuang@kylinos.cn>
Fix three spelling errors found in code comments:
- overriden → overridden
- interated → interacted
- dont → don't
Signed-off-by: Qingshuang Fu <fuqingshuang@kylinos.cn>
---
security/apparmor/domain.c | 2 +-
security/apparmor/lsm.c | 2 +-
security/smack/smackfs.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index f02bf770f638..7e097c40720a 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -135,7 +135,7 @@ static int label_compound_match(struct aa_profile *profile,
struct label_it i;
struct path_cond cond = { };
- /* find first subcomponent that is in view and going to be interated with */
+ /* find first subcomponent that is in view and going to be interacted with */
label_for_each(i, label, tp) {
if (!aa_ns_visible(profile->ns, tp->ns, inview))
continue;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3491e9f60194..51a388cfea11 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1493,7 +1493,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
*
* Note: can not sleep may be called with locks held
*
- * dont want protocol specific in __skb_recv_datagram()
+ * don't want protocol specific in __skb_recv_datagram()
* to deny an incoming connection socket_sock_rcv_skb()
*/
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 6e62dcb36f74..2820bd3ee72e 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -115,7 +115,7 @@ struct smack_known *smack_syslog_label;
/*
* Ptrace current rule
* SMACK_PTRACE_DEFAULT regular smack ptrace rules (/proc based)
- * SMACK_PTRACE_EXACT labels must match, but can be overriden with
+ * SMACK_PTRACE_EXACT labels must match, but can be overridden with
* CAP_SYS_PTRACE
* SMACK_PTRACE_DRACONIAN labels must match, CAP_SYS_PTRACE has no effect
*/
base-commit: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
--
2.25.1
^ permalink raw reply related
* [PATCH v4 0/3] introduce IMA_INIT_LATE_SYNC option
From: Yeoreum Yun @ 2026-05-25 7:54 UTC (permalink / raw)
To: linux-security-module, linux-kernel, linux-integrity
Cc: paul, zohar, roberto.sassu, noodles, jarkko, sudeep.holla,
jmorris, serge, dmitry.kasatkin, eric.snowberg, jgg, Yeoreum Yun
To generate the boot_aggregate log in the IMA subsystem with TPM PCR values,
the TPM driver must be built as built-in and
must be probed before the IMA subsystem is initialized.
However, when the TPM device operates over the FF-A protocol using
the CRB interface, probing fails and returns -EPROBE_DEFER if
the tpm_crb_ffa device — an FF-A device that provides the communication
interface to the tpm_crb driver — has not yet been probed.
To ensure the TPM device operating over the FF-A protocol with
the CRB interface is probed before IMA initialization,
the following conditions must be met:
1. The corresponding ffa_device must be registered,
which is done via ffa_init().
2. The tpm_crb_driver must successfully probe this device via
tpm_crb_ffa_init().
3. The tpm_crb driver using CRB over FF-A can then
be probed successfully. (See crb_acpi_add() and
tpm_crb_ffa_init() for reference.)
Unfortunately, ffa_init(), tpm_crb_ffa_init(), and crb_acpi_driver_init() are
all registered with device_initcall, which means crb_acpi_driver_init() may
be invoked before ffa_init() and tpm_crb_ffa_init() are completed.
When this occurs, probing the TPM device is deferred.
However, the deferred probe can happen after the IMA subsystem
has already been initialized, since IMA initialization is performed
during late_initcall, and deferred_probe_initcall() is performed
at the same level.
And the similar situation is reported on TPM devices attached on SPI
bus[0].
To resolve this, introduce IMA_INIT_LATE_SYNC option to initialise
IMA at late_inicall_sync so that IMA is initialized with the TPM
device probed defered.
When this option is enabled, modules that access files in the
initramfs through usermode helper calls such as request_module()
during initcall must not be built-in. Otherwise, IMA may miss
measuring those files since they're the file accesses before the
initialisation of IMA [1].
Link: https://lore.kernel.org/all/aYXEepLhUouN5f99@earth.li/ [0]
Link: https://lore.kernel.org/all/2b3782398cc17ce9d355490a0c42ebce9120a9ae.camel@linux.ibm.com/ [1]
Patch history
=============
from v3 to v4:
- rebase on v7.1-rc5
- introduce IMA_INIT_LATE_SYNC option to control IMA initailisation.
- https://lore.kernel.org/all/cover.1777036497.git.noodles@meta.com/
from v2 to v3:
- Drop ff-a/pKVM diff (this seems to have a separate set of
discussion)
- Rework IMA delayed initialisation to avoid delaying when unnecessary
- Ensure IMA log clearly indicates when we've initialised late
- https://lore.kernel.org/all/20260422162449.1814615-1-yeoreum.yun@arm.com/
from v1 to v2:
- add notifier to make ffa-driver pkvm initialised.
- modify to try initailisation again when IMA coudln't find proper TPM device.
- https://lore.kernel.org/all/20260417175759.3191279-1-yeoreum.yun@arm.com/#t
Yeoreum Yun (3):
security: lsm: Allow LSMs to register for late_initcall_sync init
security: ima: introduce IMA_INIT_LATE_SYNC option
tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
include/linux/lsm_hooks.h | 2 ++
security/integrity/ima/Kconfig | 10 ++++++++++
security/integrity/ima/ima_main.c | 4 ++++
security/lsm_init.c | 13 +++++++++++--
5 files changed, 30 insertions(+), 17 deletions(-)
base-commit: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
^ permalink raw reply
* [PATCH v4 1/3] security: lsm: Allow LSMs to register for late_initcall_sync init
From: Yeoreum Yun @ 2026-05-25 7:54 UTC (permalink / raw)
To: linux-security-module, linux-kernel, linux-integrity
Cc: paul, zohar, roberto.sassu, noodles, jarkko, sudeep.holla,
jmorris, serge, dmitry.kasatkin, eric.snowberg, jgg, Yeoreum Yun
In-Reply-To: <20260525075404.3480282-1-yeoreum.yun@arm.com>
There are situations where LSMs have dependencies that might mean they
want to be initialised later in the boot process, to ensure those
dependencies are available. In particular there are some TPM setups (Arm
FF-A devices, SPI attached TPMs) required by IMA which are not
guaranteed to be initialised for regular initcall_late.
Add an initcall_late_sync option that can be used in these situations.
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
include/linux/lsm_hooks.h | 2 ++
security/lsm_init.c | 13 +++++++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb..c4488c4a6d8a 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -167,6 +167,7 @@ enum lsm_order {
* @initcall_fs: LSM callback for fs_initcall setup, optional
* @initcall_device: LSM callback for device_initcall() setup, optional
* @initcall_late: LSM callback for late_initcall() setup, optional
+ * @initcall_late_sync: LSM callback for late_initcall_sync() setup, optional
*/
struct lsm_info {
const struct lsm_id *id;
@@ -182,6 +183,7 @@ struct lsm_info {
int (*initcall_fs)(void);
int (*initcall_device)(void);
int (*initcall_late)(void);
+ int (*initcall_late_sync)(void);
};
#define DEFINE_LSM(lsm) \
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 7c0fd17f1601..a1ad641811de 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -556,13 +556,22 @@ device_initcall(security_initcall_device);
* security_initcall_late - Run the LSM late initcalls
*/
static int __init security_initcall_late(void)
+{
+ return lsm_initcall(late);
+}
+late_initcall(security_initcall_late);
+
+/**
+ * security_initcall_late_sync - Run the LSM late initcalls sync
+ */
+static int __init security_initcall_late_sync(void)
{
int rc;
- rc = lsm_initcall(late);
+ rc = lsm_initcall(late_sync);
lsm_pr_dbg("all enabled LSMs fully activated\n");
call_blocking_lsm_notifier(LSM_STARTED_ALL, NULL);
return rc;
}
-late_initcall(security_initcall_late);
+late_initcall_sync(security_initcall_late_sync);
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
^ permalink raw reply related
* [PATCH v4 2/3] security: ima: introduce IMA_INIT_LATE_SYNC option
From: Yeoreum Yun @ 2026-05-25 7:54 UTC (permalink / raw)
To: linux-security-module, linux-kernel, linux-integrity
Cc: paul, zohar, roberto.sassu, noodles, jarkko, sudeep.holla,
jmorris, serge, dmitry.kasatkin, eric.snowberg, jgg, Yeoreum Yun
In-Reply-To: <20260525075404.3480282-1-yeoreum.yun@arm.com>
To generate the boot_aggregate log in the IMA subsystem with TPM PCR values,
the TPM driver must be built as built-in and
must be probed before the IMA subsystem is initialized.
However, when the TPM device operates over the FF-A protocol using
the CRB interface, probing fails and returns -EPROBE_DEFER if
the tpm_crb_ffa device — an FF-A device that provides the communication
interface to the tpm_crb driver — has not yet been probed.
To ensure the TPM device operating over the FF-A protocol with
the CRB interface is probed before IMA initialization,
the following conditions must be met:
1. The corresponding ffa_device must be registered,
which is done via ffa_init().
2. The tpm_crb_driver must successfully probe this device via
tpm_crb_ffa_init().
3. The tpm_crb driver using CRB over FF-A can then
be probed successfully. (See crb_acpi_add() and
tpm_crb_ffa_init() for reference.)
Unfortunately, ffa_init(), tpm_crb_ffa_init(), and crb_acpi_driver_init() are
all registered with device_initcall, which means crb_acpi_driver_init() may
be invoked before ffa_init() and tpm_crb_ffa_init() are completed.
When this occurs, probing the TPM device is deferred.
However, the deferred probe can happen after the IMA subsystem
has already been initialized, since IMA initialization is performed
during late_initcall, and deferred_probe_initcall() is performed
at the same level.
And the similar situation is reported on TPM devices attached on SPI
bus[0].
To resolve this, introduce IMA_INIT_LATE_SYNC option to initialise
IMA at late_inicall_sync so that IMA is initialized with the TPM
device probed deffered.
When this option is enabled, modules that access files in the
initramfs through usermode helper calls such as request_module()
during initcall must not be built-in. Otherwise, IMA may miss
measuring those files since they're the file accesses before the
initialisation of IMA [1].
Link: https://lore.kernel.org/all/aYXEepLhUouN5f99@earth.li/ [0]
Link: https://lore.kernel.org/all/2b3782398cc17ce9d355490a0c42ebce9120a9ae.camel@linux.ibm.com/ [1]
Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
security/integrity/ima/Kconfig | 10 ++++++++++
security/integrity/ima/ima_main.c | 4 ++++
2 files changed, 14 insertions(+)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 862fbee2b174..75f71401fba3 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -332,4 +332,14 @@ config IMA_KEXEC_EXTRA_MEMORY_KB
If set to the default value of 0, an extra half page of memory for those
additional measurements will be allocated.
+config IMA_INIT_LATE_SYNC
+ bool "Initialise IMA at late_initcall_sync"
+ default n
+ help
+ This option initialises IMA at late_initcall_sync for platforms
+ where TPM device probing is deferred.
+ When this option is enabled, modules that access files in the
+ initramfs through usermode helper calls such as request_module()
+ during initcall must not be built-in. Otherwise, IMA may miss
+ file measurements for them.
endif
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5cea53fc36df..1cfae4b83dc5 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1337,5 +1337,9 @@ DEFINE_LSM(ima) = {
.order = LSM_ORDER_LAST,
.blobs = &ima_blob_sizes,
/* Start IMA after the TPM is available */
+#ifndef CONFIG_IMA_INIT_LATE_SYNC
.initcall_late = init_ima,
+#else
+ .initcall_late_sync = init_ima,
+#endif
};
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
^ permalink raw reply related
* [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Yeoreum Yun @ 2026-05-25 7:54 UTC (permalink / raw)
To: linux-security-module, linux-kernel, linux-integrity
Cc: paul, zohar, roberto.sassu, noodles, jarkko, sudeep.holla,
jmorris, serge, dmitry.kasatkin, eric.snowberg, jgg, Yeoreum Yun
In-Reply-To: <20260525075404.3480282-1-yeoreum.yun@arm.com>
commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
initialises IMA at the late_initcall_sync level, so this change is no
longer required.
Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
index 99f1c1e5644b..025c4d4b17ca 100644
--- a/drivers/char/tpm/tpm_crb_ffa.c
+++ b/drivers/char/tpm/tpm_crb_ffa.c
@@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
*/
int tpm_crb_ffa_init(void)
{
- int ret = 0;
-
- if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
- ret = ffa_register(&tpm_crb_ffa_driver);
- if (ret) {
- tpm_crb_ffa = ERR_PTR(-ENODEV);
- return ret;
- }
- }
-
if (!tpm_crb_ffa)
- ret = -ENOENT;
+ return -ENOENT;
if (IS_ERR_VALUE(tpm_crb_ffa))
- ret = -ENODEV;
+ return -ENODEV;
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
@@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
.id_table = tpm_crb_ffa_device_id,
};
-#ifdef MODULE
module_ffa_driver(tpm_crb_ffa_driver);
-#endif
MODULE_AUTHOR("Arm");
MODULE_DESCRIPTION("TPM CRB FFA driver");
--
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
^ permalink raw reply related
* Re: [PATCH 03/11] moduleparam: Add DEFINE_KERNEL_PARAM_OPS macro family
From: Petr Pavlu @ 2026-05-25 13:27 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Pengpeng Hou, Richard Weinberger, Anton Ivanov,
Johannes Berg, Rafael J. Wysocki, Len Brown, Corey Minyard,
Gabriel Somlo, Michael S. Tsirkin, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, David Airlie, Simona Vetter,
Bart Van Assche, Jason Gunthorpe, Leon Romanovsky,
Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Bjorn Helgaas, Hannes Reinecke, James E.J. Bottomley,
Martin K. Petersen, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Greg Kroah-Hartman, Jiri Slaby, Alan Stern, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Jason Baron, Jim Cromie, Tiwei Bie,
Benjamin Berg, Ilpo Järvinen, David E. Box,
Maciej W. Rozycki, Srinivas Pandruvada, Peter Zijlstra,
Heiko Carstens, Vasily Gorbik, Sean Christopherson, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Vinod Koul, Frank Li, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Alexander Potapenko, Marco Elver, Dmitry Vyukov,
Andrew Morton, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andy Shevchenko, Georgia Garcia, kvm, dmaengine,
linux-modules, kasan-dev, linux-mm, apparmor,
linux-security-module, linux-um, linux-acpi, openipmi-developer,
qemu-devel, intel-gfx, dri-devel, linux-rdma, linux-media,
linux-pci, linux-scsi, linux-pm, linuxppc-dev, linux-serial,
linux-usb, usb-storage, virtualization, linux-kernel, linux-arch,
netdev, linux-fsdevel, linux-hardening
In-Reply-To: <20260521133326.2465264-3-kees@kernel.org>
On 5/21/26 3:33 PM, Kees Cook wrote:
> Add macros that define a struct kernel_param_ops initializer through a
> macro so the underlying field layout can evolve without touching every
> call site. Three variants cover the three cases:
>
> DEFINE_KERNEL_PARAM_OPS(name, set, get) // basic
> DEFINE_KERNEL_PARAM_OPS_NOARG(name, set, get) // set KERNEL_PARAM_OPS_FL_NOARG
> DEFINE_KERNEL_PARAM_OPS_FREE(name, set, get, free) // also set .free
>
> Callers prefix their own visibility qualifiers, e.g.:
>
> static DEFINE_KERNEL_PARAM_OPS(my_ops, my_set, my_get);
>
> Also update module_param_call() and STANDARD_PARAM_DEF() to use
> DEFINE_KERNEL_PARAM_OPS internally so the generated ops table will go
> through the same macro as everything else.
>
> Subsequent commits convert all open-coded struct kernel_param_ops
> definitions to use these macros, in preparation for migrating to a
> seq_buf .get API.
>
> Signed-off-by: Kees Cook <kees@kernel.org>
> ---
> include/linux/moduleparam.h | 36 ++++++++++++++++++++++++++++++++++--
> kernel/params.c | 6 ++----
> 2 files changed, 36 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
> index 075f28585074..26bf45b36d02 100644
> --- a/include/linux/moduleparam.h
> +++ b/include/linux/moduleparam.h
> @@ -68,6 +68,39 @@ struct kernel_param_ops {
> void (*free)(void *arg);
> };
>
> +/*
> + * Define a const struct kernel_param_ops initializer. Callers prefix with
> + * any required visibility qualifiers (typically "static"):
> + *
> + * static DEFINE_KERNEL_PARAM_OPS(my_ops, my_set, my_get);
> + *
> + * Routing the @_set and @_get function pointers through the macro
> + * (rather than naming the struct fields at every call site) lets the
> + * field layout change in one place when callbacks are migrated to a
> + * new signature.
> + */
Nit: The newly introduced DEFINE_KERNEL_PARAM_OPS*() macros remain in
place at the end of the series after the migration is complete and this
comment is removed in patch 7. It would be helpful to describe in the
commit message why these macros are generally preferable to defining
kernel_param_ops instances directly.
I assume the motivation is that the structure is simple enough and using
macros then makes defining kernel_param_ops instances a bit more
concise. A minor disadvantage is that some analysis tools, such as
ctags, may no longer see the generated definition, but that is also the
case for DEFINE_MUTEX() and other similar macros.
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH 04/11] treewide: Convert struct kernel_param_ops initializers to DEFINE_KERNEL_PARAM_OPS
From: Petr Pavlu @ 2026-05-25 13:35 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Pengpeng Hou, Richard Weinberger, Anton Ivanov,
Johannes Berg, Rafael J. Wysocki, Len Brown, Corey Minyard,
Gabriel Somlo, Michael S. Tsirkin, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, David Airlie, Simona Vetter,
Bart Van Assche, Jason Gunthorpe, Leon Romanovsky,
Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Bjorn Helgaas, Hannes Reinecke, James E.J. Bottomley,
Martin K. Petersen, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Greg Kroah-Hartman, Jiri Slaby, Alan Stern, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Jason Baron, Jim Cromie, Tiwei Bie,
Benjamin Berg, Ilpo Järvinen, David E. Box,
Maciej W. Rozycki, Srinivas Pandruvada, Peter Zijlstra,
Heiko Carstens, Vasily Gorbik, Sean Christopherson, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Vinod Koul, Frank Li, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Alexander Potapenko, Marco Elver, Dmitry Vyukov,
Andrew Morton, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andy Shevchenko, Georgia Garcia, kvm, dmaengine,
linux-modules, kasan-dev, linux-mm, apparmor,
linux-security-module, linux-um, linux-acpi, openipmi-developer,
qemu-devel, intel-gfx, dri-devel, linux-rdma, linux-media,
linux-pci, linux-scsi, linux-pm, linuxppc-dev, linux-serial,
linux-usb, usb-storage, virtualization, linux-kernel, linux-arch,
netdev, linux-fsdevel, linux-hardening
In-Reply-To: <20260521133326.2465264-4-kees@kernel.org>
On 5/21/26 3:33 PM, Kees Cook wrote:
> Using Coccinelle, rewrite every struct kernel_param_ops initializer that
> sets .get into a DEFINE_KERNEL_PARAM_OPS-family macro invocation,
> for example:
>
> @@
> declarer name DEFINE_KERNEL_PARAM_OPS;
> identifier OPS;
> expression SET, GET;
> @@
> - const struct kernel_param_ops OPS = {
> - .set = SET,
> - .get = GET,
> - };
> + DEFINE_KERNEL_PARAM_OPS(OPS, SET, GET);
>
> Using the macro for initialization means future changes can manipulate
> the struct layout and callback prototypes without having to change every
> initializer.
Nit: For consistency, I suggest also converting the few remaining
kernel_param_ops instances that specify only .set and no .get, such as
simdisk_param_ops_filename.
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH] tpm-buf: memory-safe allocations
From: Srish Srinivasan @ 2026-05-25 13:46 UTC (permalink / raw)
To: Jarkko Sakkinen, linux-integrity
Cc: Jarkko Sakkinen, Arun Menon, Daniel P. Smith, Alec Brown,
Ross Philipson, Stefan Berger, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, linux-kernel, keyrings,
linux-security-module
In-Reply-To: <20260522013555.1063716-1-jarkko@kernel.org>
Tested on emulated TPM 1.2 and TPM 2.0 backends.
Coverage:
TPM 1.2: sysfs and trusted-key paths
TPM 2.0: PCR, random, and trusted-key paths
Tested-by: Srish Srinivasan <ssrish@linux.ibm.com>
On 5/22/26 7:05 AM, Jarkko Sakkinen wrote:
> From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
>
> Decouple kzalloc from buffer creation, so that a managed allocation can be
> used:
>
> struct tpm_buf *buf __free(kfree) buf = kzalloc(TPM_BUFSIZE,
> GFP_KERNEL);
> if (!buf)
> return -ENOMEM;
>
> tpm_buf_init(buf, TPM_BUFSIZE);
>
> Alternatively, stack allocations are also possible:
>
> u8 buf_data[512];
> struct tpm_buf *buf = (struct tpm_buf *)buf_data;
> tpm_buf_init(buf, sizeof(buf_data));
>
> This is achieved by embedding buffer's header inside the allocated blob,
> instead of having an outer wrapper.
>
> Cc: Arun Menon <armenon@redhat.com>
> Cc: Daniel P. Smith <dpsmith@apertussolutions.com>
> Cc: Alec Brown <alec.r.brown@oracle.com>
> Cc: Ross Philipson <ross.philipson@gmail.com>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
> ---
> Rebased the managed allocations patch, which has been probably like a
> year in circulation.
> drivers/char/tpm/tpm-buf.c | 122 ++++++----
> drivers/char/tpm/tpm-sysfs.c | 17 +-
> drivers/char/tpm/tpm.h | 1 -
> drivers/char/tpm/tpm1-cmd.c | 150 ++++++------
> drivers/char/tpm/tpm2-cmd.c | 277 +++++++++++-----------
> drivers/char/tpm/tpm2-sessions.c | 149 ++++++------
> drivers/char/tpm/tpm2-space.c | 44 ++--
> drivers/char/tpm/tpm_vtpm_proxy.c | 30 +--
> include/linux/tpm.h | 18 +-
> security/keys/trusted-keys/trusted_tpm1.c | 44 ++--
> security/keys/trusted-keys/trusted_tpm2.c | 165 ++++++-------
> 11 files changed, 505 insertions(+), 512 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> index dc882fc9fa9e..b16d824ef0af 100644
> --- a/drivers/char/tpm/tpm-buf.c
> +++ b/drivers/char/tpm/tpm-buf.c
> @@ -7,82 +7,110 @@
> #include <linux/module.h>
> #include <linux/tpm.h>
>
> -/**
> - * tpm_buf_init() - Allocate and initialize a TPM command
> - * @buf: A &tpm_buf
> - * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> - * @ordinal: A command ordinal
> - *
> - * Return: 0 or -ENOMEM
> - */
> -int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
> +static void __tpm_buf_size_invariant(struct tpm_buf *buf, u16 buf_size)
> {
> - buf->data = (u8 *)__get_free_page(GFP_KERNEL);
> - if (!buf->data)
> - return -ENOMEM;
> -
> - tpm_buf_reset(buf, tag, ordinal);
> - return 0;
> + u32 buf_size_2 = (u32)buf->capacity + (u32)sizeof(*buf);
> +
> + if (!buf->capacity) {
> + if (buf_size > TPM_BUFSIZE) {
> + WARN(1, "%s: size overflow: %u\n", __func__, buf_size);
> + buf->flags |= TPM_BUF_OVERFLOW;
> + }
> + } else {
> + if (buf_size != buf_size_2) {
> + WARN(1, "%s: size mismatch: %u != %u\n", __func__,
> + buf_size, buf_size_2);
> + buf->flags |= TPM_BUF_OVERFLOW;
> + }
> + }
> }
> -EXPORT_SYMBOL_GPL(tpm_buf_init);
>
> -/**
> - * tpm_buf_reset() - Initialize a TPM command
> - * @buf: A &tpm_buf
> - * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> - * @ordinal: A command ordinal
> - */
> -void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
> +static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag,
> + u32 ordinal)
> {
> struct tpm_header *head = (struct tpm_header *)buf->data;
>
> + __tpm_buf_size_invariant(buf, buf_size);
> +
> + if (buf->flags & TPM_BUF_OVERFLOW)
> + return;
> +
> WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
> tag != TPM2_ST_SESSIONS && tag != 0);
>
> buf->flags = 0;
> buf->length = sizeof(*head);
> + buf->capacity = buf_size - sizeof(*buf);
> + buf->handles = 0;
> head->tag = cpu_to_be16(tag);
> head->length = cpu_to_be32(sizeof(*head));
> head->ordinal = cpu_to_be32(ordinal);
> +}
> +
> +static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
> +{
> + __tpm_buf_size_invariant(buf, buf_size);
> +
> + if (buf->flags & TPM_BUF_OVERFLOW)
> + return;
> +
> + buf->flags = TPM_BUF_TPM2B;
> + buf->length = 2;
> + buf->capacity = buf_size - sizeof(*buf);
> buf->handles = 0;
> + buf->data[0] = 0;
> + buf->data[1] = 0;
> }
> -EXPORT_SYMBOL_GPL(tpm_buf_reset);
>
> /**
> - * tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
> - * @buf: A @tpm_buf
> - *
> - * Return: 0 or -ENOMEM
> + * tpm_buf_init() - Initialize a TPM command
> + * @buf: A &tpm_buf
> + * @buf_size: Size of the buffer.
> */
> -int tpm_buf_init_sized(struct tpm_buf *buf)
> +void tpm_buf_init(struct tpm_buf *buf, u16 buf_size)
> {
> - buf->data = (u8 *)__get_free_page(GFP_KERNEL);
> - if (!buf->data)
> - return -ENOMEM;
> + memset(buf, 0, buf_size);
> + __tpm_buf_reset(buf, buf_size, TPM_TAG_RQU_COMMAND, 0);
> +}
> +EXPORT_SYMBOL_GPL(tpm_buf_init);
>
> - tpm_buf_reset_sized(buf);
> - return 0;
> +/**
> + * tpm_buf_init_sized() - Initialize a sized buffer
> + * @buf: A &tpm_buf
> + * @buf_size: Size of the buffer.
> + */
> +void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size)
> +{
> + memset(buf, 0, buf_size);
> + __tpm_buf_reset_sized(buf, buf_size);
> }
> EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
>
> /**
> - * tpm_buf_reset_sized() - Initialize a sized buffer
> + * tpm_buf_reset() - Re-initialize a TPM command
> * @buf: A &tpm_buf
> + * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
> + * @ordinal: A command ordinal
> */
> -void tpm_buf_reset_sized(struct tpm_buf *buf)
> +void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
> {
> - buf->flags = TPM_BUF_TPM2B;
> - buf->length = 2;
> - buf->data[0] = 0;
> - buf->data[1] = 0;
> + u16 buf_size = buf->capacity + sizeof(*buf);
> +
> + __tpm_buf_reset(buf, buf_size, tag, ordinal);
> }
> -EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
> +EXPORT_SYMBOL_GPL(tpm_buf_reset);
>
> -void tpm_buf_destroy(struct tpm_buf *buf)
> +/**
> + * tpm_buf_reset_sized() - Re-initialize a sized buffer
> + * @buf: A &tpm_buf
> + */
> +void tpm_buf_reset_sized(struct tpm_buf *buf)
> {
> - free_page((unsigned long)buf->data);
> + u16 buf_size = buf->capacity + sizeof(*buf);
> +
> + __tpm_buf_reset_sized(buf, buf_size);
> }
> -EXPORT_SYMBOL_GPL(tpm_buf_destroy);
> +EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
>
> /**
> * tpm_buf_length() - Return the number of bytes consumed by the data
> @@ -90,7 +118,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_destroy);
> *
> * Return: The number of bytes consumed by the buffer
> */
> -u32 tpm_buf_length(struct tpm_buf *buf)
> +u16 tpm_buf_length(struct tpm_buf *buf)
> {
> return buf->length;
> }
> @@ -104,11 +132,13 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
> */
> void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
> {
> + u32 total_length = (u32)buf->length + (u32)new_length;
> +
> /* Return silently if overflow has already happened. */
> if (buf->flags & TPM_BUF_OVERFLOW)
> return;
>
> - if ((buf->length + new_length) > PAGE_SIZE) {
> + if (total_length > (u32)buf->capacity) {
> WARN(1, "tpm_buf: write overflow\n");
> buf->flags |= TPM_BUF_OVERFLOW;
> return;
> diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
> index 94231f052ea7..1de03cf340b3 100644
> --- a/drivers/char/tpm/tpm-sysfs.c
> +++ b/drivers/char/tpm/tpm-sysfs.c
> @@ -32,28 +32,31 @@ struct tpm_readpubek_out {
> static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> char *buf)
> {
> - struct tpm_buf tpm_buf;
> struct tpm_readpubek_out *out;
> int i;
> char *str = buf;
> struct tpm_chip *chip = to_tpm_chip(dev);
> char anti_replay[20];
> + struct tpm_buf *tpm_buf __free(kfree) = NULL;
>
> memset(&anti_replay, 0, sizeof(anti_replay));
>
> if (tpm_try_get_ops(chip))
> return 0;
>
> - if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
> + tpm_buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!tpm_buf)
> goto out_ops;
>
> - tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
> + tpm_buf_init(tpm_buf, TPM_BUFSIZE);
> + tpm_buf_reset(tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
> + tpm_buf_append(tpm_buf, anti_replay, sizeof(anti_replay));
>
> - if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
> + if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
> "attempting to read the PUBEK"))
> - goto out_buf;
> + goto out_ops;
>
> - out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
> + out = (struct tpm_readpubek_out *)&tpm_buf->data[10];
> str +=
> sprintf(str,
> "Algorithm: %4ph\n"
> @@ -71,8 +74,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> for (i = 0; i < 256; i += 16)
> str += sprintf(str, "%16ph\n", &out->modulus[i]);
>
> -out_buf:
> - tpm_buf_destroy(&tpm_buf);
> out_ops:
> tpm_put_ops(chip);
> return str - buf;
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index 87d68ddf270a..03f5346343ab 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -33,7 +33,6 @@
> #endif
>
> #define TPM_MINOR 224 /* officially assigned */
> -#define TPM_BUFSIZE 4096
> #define TPM_NUM_DEVICES 65536
> #define TPM_RETRY 50
>
> diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
> index b49a790f1bd5..6facc3de2c46 100644
> --- a/drivers/char/tpm/tpm1-cmd.c
> +++ b/drivers/char/tpm/tpm1-cmd.c
> @@ -323,20 +323,18 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> */
> static int tpm1_startup(struct tpm_chip *chip)
> {
> - struct tpm_buf buf;
> - int rc;
> + struct tpm_buf *buf __free(kfree) = NULL;
>
> dev_info(&chip->dev, "starting up the TPM manually\n");
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
> - if (rc < 0)
> - return rc;
> -
> - tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
> - tpm_buf_destroy(&buf);
> - return rc;
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
> + tpm_buf_append_u16(buf, TPM_ST_CLEAR);
> + return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
> }
>
> int tpm1_get_timeouts(struct tpm_chip *chip)
> @@ -463,50 +461,47 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
> int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> const char *log_msg)
> {
> - struct tpm_buf buf;
> - int rc;
> -
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
> - if (rc)
> - return rc;
> -
> - tpm_buf_append_u32(&buf, pcr_idx);
> - tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
> -
> - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
> - tpm_buf_destroy(&buf);
> - return rc;
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
> + tpm_buf_append_u32(buf, pcr_idx);
> + tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
> + return tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, log_msg);
> }
>
> #define TPM_ORD_GET_CAP 101
> ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
> const char *desc, size_t min_cap_length)
> {
> - struct tpm_buf buf;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
> - if (rc)
> - return rc;
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
>
> if (subcap_id == TPM_CAP_VERSION_1_1 ||
> subcap_id == TPM_CAP_VERSION_1_2) {
> - tpm_buf_append_u32(&buf, subcap_id);
> - tpm_buf_append_u32(&buf, 0);
> + tpm_buf_append_u32(buf, subcap_id);
> + tpm_buf_append_u32(buf, 0);
> } else {
> if (subcap_id == TPM_CAP_FLAG_PERM ||
> subcap_id == TPM_CAP_FLAG_VOL)
> - tpm_buf_append_u32(&buf, TPM_CAP_FLAG);
> + tpm_buf_append_u32(buf, TPM_CAP_FLAG);
> else
> - tpm_buf_append_u32(&buf, TPM_CAP_PROP);
> + tpm_buf_append_u32(buf, TPM_CAP_PROP);
>
> - tpm_buf_append_u32(&buf, 4);
> - tpm_buf_append_u32(&buf, subcap_id);
> + tpm_buf_append_u32(buf, 4);
> + tpm_buf_append_u32(buf, subcap_id);
> }
> - rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
> + rc = tpm_transmit_cmd(chip, buf, min_cap_length, desc);
> if (!rc)
> - *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
> - tpm_buf_destroy(&buf);
> + *cap = *(cap_t *)&buf->data[TPM_HEADER_SIZE + 4];
> return rc;
> }
> EXPORT_SYMBOL_GPL(tpm1_getcap);
> @@ -530,21 +525,24 @@ struct tpm1_get_random_out {
> int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> {
> struct tpm1_get_random_out *out;
> + struct tpm_buf *buf __free(kfree) = NULL;
> u32 num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
> - struct tpm_buf buf;
> u32 total = 0;
> int retries = 5;
> u32 recd;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> - if (rc)
> - return rc;
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
>
> do {
> - tpm_buf_append_u32(&buf, num_bytes);
> + tpm_buf_append_u32(buf, num_bytes);
>
> - rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
> + rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len),
> "attempting get random");
> if (rc) {
> if (rc > 0)
> @@ -552,7 +550,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> goto out;
> }
>
> - out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
> + out = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];
>
> recd = be32_to_cpu(out->rng_data_len);
> if (recd > num_bytes) {
> @@ -560,8 +558,8 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> goto out;
> }
>
> - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
> - sizeof(out->rng_data_len) + recd) {
> + if (tpm_buf_length(buf) < TPM_HEADER_SIZE +
> + sizeof(out->rng_data_len) + recd) {
> rc = -EFAULT;
> goto out;
> }
> @@ -571,41 +569,36 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> total += recd;
> num_bytes -= recd;
>
> - tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> } while (retries-- && total < max);
>
> rc = total ? (int)total : -EIO;
> out:
> - tpm_buf_destroy(&buf);
> return rc;
> }
>
> #define TPM_ORD_PCRREAD 21
> int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> {
> - struct tpm_buf buf;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
> - if (rc)
> - return rc;
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - tpm_buf_append_u32(&buf, pcr_idx);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
> + tpm_buf_append_u32(buf, pcr_idx);
>
> - rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
> + rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE,
> "attempting to read a pcr value");
> if (rc)
> - goto out;
> -
> - if (tpm_buf_length(&buf) < TPM_DIGEST_SIZE) {
> - rc = -EFAULT;
> - goto out;
> - }
> + return rc;
>
> - memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
> + if (tpm_buf_length(buf) < TPM_DIGEST_SIZE)
> + return -EFAULT;
>
> -out:
> - tpm_buf_destroy(&buf);
> + memcpy(res_buf, &buf->data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
> return rc;
> }
>
> @@ -619,16 +612,13 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> */
> static int tpm1_continue_selftest(struct tpm_chip *chip)
> {
> - struct tpm_buf buf;
> - int rc;
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
> - if (rc)
> - return rc;
> -
> - rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
> - tpm_buf_destroy(&buf);
> - return rc;
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
> + return tpm_transmit_cmd(chip, buf, 0, "continue selftest");
> }
>
> /**
> @@ -742,22 +732,24 @@ int tpm1_auto_startup(struct tpm_chip *chip)
> int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> {
> u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
> - struct tpm_buf buf;
> unsigned int try;
> + struct tpm_buf *buf __free(kfree) = NULL;
> int rc;
>
> -
> /* for buggy tpm, flush pcrs with extend to selected dummy */
> if (tpm_suspend_pcr)
> rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
> "extending dummy pcr before suspend");
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> - if (rc)
> - return rc;
> /* now do the actual savestate */
> for (try = 0; try < TPM_RETRY; try++) {
> - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> /*
> * If the TPM indicates that it is too busy to respond to
> * this command then retry before giving up. It can take
> @@ -772,7 +764,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> break;
> tpm_msleep(TPM_TIMEOUT_RETRY);
>
> - tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> }
>
> if (rc)
> @@ -782,8 +774,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> dev_warn(&chip->dev, "TPM savestate took %dms\n",
> try * TPM_TIMEOUT_RETRY);
>
> - tpm_buf_destroy(&buf);
> -
> return rc;
> }
>
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 52ee350da867..f619ce390f6d 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -119,12 +119,13 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> {
> int i;
> int rc;
> - struct tpm_buf buf;
> struct tpm2_pcr_read_out *out;
> u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
> u16 digest_size;
> u16 expected_digest_size = 0;
>
> + struct tpm_buf *buf __free(kfree) = NULL;
> +
> if (pcr_idx >= TPM2_PLATFORM_PCR)
> return -EINVAL;
>
> @@ -139,36 +140,35 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> expected_digest_size = chip->allocated_banks[i].digest_size;
> }
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
> - if (rc)
> - return rc;
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
>
> pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
>
> - tpm_buf_append_u32(&buf, 1);
> - tpm_buf_append_u16(&buf, digest->alg_id);
> - tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
> - tpm_buf_append(&buf, (const unsigned char *)pcr_select,
> + tpm_buf_append_u32(buf, 1);
> + tpm_buf_append_u16(buf, digest->alg_id);
> + tpm_buf_append_u8(buf, TPM2_PCR_SELECT_MIN);
> + tpm_buf_append(buf, (const unsigned char *)pcr_select,
> sizeof(pcr_select));
>
> - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
> + rc = tpm_transmit_cmd(chip, buf, 0, "attempting to read a pcr value");
> if (rc)
> - goto out;
> + return rc;
>
> - out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
> + out = (struct tpm2_pcr_read_out *)&buf->data[TPM_HEADER_SIZE];
> digest_size = be16_to_cpu(out->digest_size);
> if (digest_size > sizeof(digest->digest) ||
> - (!digest_size_ptr && digest_size != expected_digest_size)) {
> - rc = -EINVAL;
> - goto out;
> - }
> + (!digest_size_ptr && digest_size != expected_digest_size))
> + return -EINVAL;
>
> if (digest_size_ptr)
> *digest_size_ptr = digest_size;
>
> memcpy(digest->digest, out->digest, digest_size);
> -out:
> - tpm_buf_destroy(&buf);
> return rc;
> }
>
> @@ -184,56 +184,54 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> struct tpm_digest *digests)
> {
> - struct tpm_buf buf;
> int rc;
> int i;
>
> + struct tpm_buf *buf __free(kfree) = NULL;
> +
> if (!disable_pcr_integrity) {
> rc = tpm2_start_auth_session(chip);
> if (rc)
> return rc;
> }
>
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
> - if (rc) {
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> if (!disable_pcr_integrity)
> tpm2_end_auth_session(chip);
> - return rc;
> + return -ENOMEM;
> }
>
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
> +
> if (!disable_pcr_integrity) {
> - rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> - if (rc) {
> - tpm_buf_destroy(&buf);
> + rc = tpm_buf_append_name(chip, buf, pcr_idx, NULL);
> + if (rc)
> return rc;
> - }
> - tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> + tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
> } else {
> - tpm_buf_append_handle(chip, &buf, pcr_idx);
> - tpm_buf_append_auth(chip, &buf, NULL, 0);
> + tpm_buf_append_handle(chip, buf, pcr_idx);
> + tpm_buf_append_auth(chip, buf, NULL, 0);
> }
>
> - tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
> + tpm_buf_append_u32(buf, chip->nr_allocated_banks);
>
> for (i = 0; i < chip->nr_allocated_banks; i++) {
> - tpm_buf_append_u16(&buf, digests[i].alg_id);
> - tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
> + tpm_buf_append_u16(buf, digests[i].alg_id);
> + tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
> chip->allocated_banks[i].digest_size);
> }
>
> if (!disable_pcr_integrity) {
> - rc = tpm_buf_fill_hmac_session(chip, &buf);
> - if (rc) {
> - tpm_buf_destroy(&buf);
> + rc = tpm_buf_fill_hmac_session(chip, buf);
> + if (rc)
> return rc;
> - }
> }
>
> - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
> + rc = tpm_transmit_cmd(chip, buf, 0, "attempting extend a PCR value");
> if (!disable_pcr_integrity)
> - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> -
> - tpm_buf_destroy(&buf);
> + rc = tpm_buf_check_hmac_response(chip, buf, rc);
>
> return rc;
> }
> @@ -258,7 +256,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> {
> struct tpm2_get_random_out *out;
> struct tpm_header *head;
> - struct tpm_buf buf;
> u32 recd;
> u32 num_bytes = max;
> int err;
> @@ -267,6 +264,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> u8 *dest_ptr = dest;
> off_t offset;
>
> + struct tpm_buf *buf __free(kfree) = NULL;
> +
> if (!num_bytes || max > TPM_MAX_RNG_DATA)
> return -EINVAL;
>
> @@ -274,50 +273,52 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> if (err)
> return err;
>
> - err = tpm_buf_init(&buf, 0, 0);
> - if (err) {
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> tpm2_end_auth_session(chip);
> - return err;
> + return -ENOMEM;
> }
>
> + tpm_buf_init(buf, TPM_BUFSIZE);
> +
> do {
> - tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
> if (tpm2_chip_auth(chip)) {
> - tpm_buf_append_hmac_session(chip, &buf,
> + tpm_buf_append_hmac_session(chip, buf,
> TPM2_SA_ENCRYPT |
> TPM2_SA_CONTINUE_SESSION,
> NULL, 0);
> } else {
> - offset = buf.handles * 4 + TPM_HEADER_SIZE;
> - head = (struct tpm_header *)buf.data;
> - if (tpm_buf_length(&buf) == offset)
> + offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + head = (struct tpm_header *)buf->data;
> + if (tpm_buf_length(buf) == offset)
> head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> }
> - tpm_buf_append_u16(&buf, num_bytes);
> - err = tpm_buf_fill_hmac_session(chip, &buf);
> + tpm_buf_append_u16(buf, num_bytes);
> + err = tpm_buf_fill_hmac_session(chip, buf);
> if (err)
> goto out;
>
> - err = tpm_transmit_cmd(chip, &buf,
> + err = tpm_transmit_cmd(chip, buf,
> offsetof(struct tpm2_get_random_out,
> buffer),
> "attempting get random");
> - err = tpm_buf_check_hmac_response(chip, &buf, err);
> + err = tpm_buf_check_hmac_response(chip, buf, err);
> if (err) {
> if (err > 0)
> err = -EIO;
> goto out;
> }
>
> - head = (struct tpm_header *)buf.data;
> + head = (struct tpm_header *)buf->data;
> offset = TPM_HEADER_SIZE;
> /* Skip the parameter size field: */
> if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
> offset += 4;
>
> - out = (struct tpm2_get_random_out *)&buf.data[offset];
> + out = (struct tpm2_get_random_out *)&buf->data[offset];
> recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
> - if (tpm_buf_length(&buf) <
> + if (tpm_buf_length(buf) <
> TPM_HEADER_SIZE +
> offsetof(struct tpm2_get_random_out, buffer) +
> recd) {
> @@ -331,11 +332,8 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> num_bytes -= recd;
> } while (retries-- && total < max);
>
> - tpm_buf_destroy(&buf);
> -
> return total ? total : -EIO;
> out:
> - tpm_buf_destroy(&buf);
> tpm2_end_auth_session(chip);
> return err;
> }
> @@ -347,20 +345,18 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> */
> void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
> {
> - struct tpm_buf buf;
> - int rc;
> -
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
> - if (rc) {
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
> handle);
> return;
> }
>
> - tpm_buf_append_u32(&buf, handle);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
> + tpm_buf_append_u32(buf, handle);
>
> - tpm_transmit_cmd(chip, &buf, 0, "flushing context");
> - tpm_buf_destroy(&buf);
> + tpm_transmit_cmd(chip, buf, 0, "flushing context");
> }
> EXPORT_SYMBOL_GPL(tpm2_flush_context);
>
> @@ -387,19 +383,21 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
> const char *desc)
> {
> struct tpm2_get_cap_out *out;
> - struct tpm_buf buf;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> - if (rc)
> - return rc;
> - tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> - tpm_buf_append_u32(&buf, property_id);
> - tpm_buf_append_u32(&buf, 1);
> - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> + tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
> + tpm_buf_append_u32(buf, property_id);
> + tpm_buf_append_u32(buf, 1);
> + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> if (!rc) {
> out = (struct tpm2_get_cap_out *)
> - &buf.data[TPM_HEADER_SIZE];
> + &buf->data[TPM_HEADER_SIZE];
> /*
> * To prevent failing boot up of some systems, Infineon TPM2.0
> * returns SUCCESS on TPM2_Startup in field upgrade mode. Also
> @@ -411,7 +409,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
> else
> rc = -ENODATA;
> }
> - tpm_buf_destroy(&buf);
> return rc;
> }
> EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
> @@ -428,15 +425,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
> */
> void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
> {
> - struct tpm_buf buf;
> - int rc;
> -
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
> - if (rc)
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> return;
> - tpm_buf_append_u16(&buf, shutdown_type);
> - tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
> - tpm_buf_destroy(&buf);
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
> + tpm_buf_append_u16(buf, shutdown_type);
> + tpm_transmit_cmd(chip, buf, 0, "stopping the TPM");
> }
>
> /**
> @@ -454,20 +450,21 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
> */
> static int tpm2_do_selftest(struct tpm_chip *chip)
> {
> - struct tpm_buf buf;
> int full;
> int rc;
>
> for (full = 0; full < 2; full++) {
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
> - if (rc)
> - return rc;
> + struct tpm_buf *buf __free(kfree) = NULL;
>
> - tpm_buf_append_u8(&buf, full);
> - rc = tpm_transmit_cmd(chip, &buf, 0,
> - "attempting the self test");
> - tpm_buf_destroy(&buf);
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
> + tpm_buf_append_u8(buf, full);
> + rc = tpm_transmit_cmd(chip, buf, 0,
> + "attempting the self test");
> if (rc == TPM2_RC_TESTING)
> rc = TPM2_RC_SUCCESS;
> if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
> @@ -492,23 +489,24 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
> int tpm2_probe(struct tpm_chip *chip)
> {
> struct tpm_header *out;
> - struct tpm_buf buf;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> - if (rc)
> - return rc;
> - tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> - tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
> - tpm_buf_append_u32(&buf, 1);
> - rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> + tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
> + tpm_buf_append_u32(buf, TPM_PT_TOTAL_COMMANDS);
> + tpm_buf_append_u32(buf, 1);
> + rc = tpm_transmit_cmd(chip, buf, 0, NULL);
> /* We ignore TPM return codes on purpose. */
> if (rc >= 0) {
> - out = (struct tpm_header *)buf.data;
> + out = (struct tpm_header *)buf->data;
> if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
> chip->flags |= TPM_CHIP_FLAG_TPM2;
> }
> - tpm_buf_destroy(&buf);
> return 0;
> }
> EXPORT_SYMBOL_GPL(tpm2_probe);
> @@ -548,7 +546,6 @@ struct tpm2_pcr_selection {
> ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> {
> struct tpm2_pcr_selection pcr_selection;
> - struct tpm_buf buf;
> void *marker;
> void *end;
> void *pcr_select_offset;
> @@ -560,20 +557,22 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> int rc;
> int i = 0;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> - if (rc)
> - return rc;
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
> - tpm_buf_append_u32(&buf, 0);
> - tpm_buf_append_u32(&buf, 1);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> + tpm_buf_append_u32(buf, TPM2_CAP_PCRS);
> + tpm_buf_append_u32(buf, 0);
> + tpm_buf_append_u32(buf, 1);
>
> - rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
> + rc = tpm_transmit_cmd(chip, buf, 9, "get tpm pcr allocation");
> if (rc)
> goto out;
>
> nr_possible_banks = be32_to_cpup(
> - (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
> + (__be32 *)&buf->data[TPM_HEADER_SIZE + 5]);
> if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
> pr_err("tpm: out of bank capacity: %u > %u\n",
> nr_possible_banks, TPM2_MAX_PCR_BANKS);
> @@ -581,10 +580,10 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> goto out;
> }
>
> - marker = &buf.data[TPM_HEADER_SIZE + 9];
> + marker = &buf->data[TPM_HEADER_SIZE + 9];
>
> - rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
> - end = &buf.data[rsp_len];
> + rsp_len = be32_to_cpup((__be32 *)&buf->data[2]);
> + end = &buf->data[rsp_len];
>
> for (i = 0; i < nr_possible_banks; i++) {
> pcr_select_offset = marker +
> @@ -617,20 +616,19 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
>
> chip->nr_allocated_banks = nr_alloc_banks;
> out:
> - tpm_buf_destroy(&buf);
> -
> return rc;
> }
>
> int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> {
> - struct tpm_buf buf;
> u32 nr_commands;
> __be32 *attrs;
> u32 cc;
> int i;
> int rc;
>
> + struct tpm_buf *buf __free(kfree) = NULL;
> +
> rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
> if (rc)
> goto out;
> @@ -647,30 +645,31 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> goto out;
> }
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> - if (rc)
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> + rc = -ENOMEM;
> goto out;
> + }
>
> - tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
> - tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
> - tpm_buf_append_u32(&buf, nr_commands);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
> + tpm_buf_append_u32(buf, TPM2_CAP_COMMANDS);
> + tpm_buf_append_u32(buf, TPM2_CC_FIRST);
> + tpm_buf_append_u32(buf, nr_commands);
>
> - rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
> - if (rc) {
> - tpm_buf_destroy(&buf);
> + rc = tpm_transmit_cmd(chip, buf, 9 + 4 * nr_commands, NULL);
> + if (rc)
> goto out;
> - }
>
> if (nr_commands !=
> - be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
> + be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5])) {
> rc = -EFAULT;
> - tpm_buf_destroy(&buf);
> goto out;
> }
>
> chip->nr_commands = nr_commands;
>
> - attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
> + attrs = (__be32 *)&buf->data[TPM_HEADER_SIZE + 9];
> for (i = 0; i < nr_commands; i++, attrs++) {
> chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
> cc = chip->cc_attrs_tbl[i] & 0xFFFF;
> @@ -682,8 +681,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> }
> }
>
> - tpm_buf_destroy(&buf);
> -
> out:
> if (rc > 0)
> rc = -ENODEV;
> @@ -704,20 +701,18 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
>
> static int tpm2_startup(struct tpm_chip *chip)
> {
> - struct tpm_buf buf;
> - int rc;
> + struct tpm_buf *buf __free(kfree) = NULL;
>
> dev_info(&chip->dev, "starting up the TPM manually\n");
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
> - if (rc < 0)
> - return rc;
> -
> - tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
> - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
> - tpm_buf_destroy(&buf);
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - return rc;
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
> + tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
> + return tpm_transmit_cmd(chip, buf, 0, "attempting to start the TPM");
> }
>
> /**
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 795cd99dc6fe..b6a93db5a5ee 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -167,8 +167,8 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
> {
> u32 mso = tpm2_handle_mso(handle);
> off_t offset = TPM_HEADER_SIZE;
> + struct tpm_buf *buf __free(kfree) = NULL;
> int rc, name_size_alg;
> - struct tpm_buf buf;
>
> if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
> mso != TPM2_MSO_NVRAM) {
> @@ -176,50 +176,40 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
> return sizeof(u32);
> }
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
> - if (rc)
> - return rc;
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - tpm_buf_append_u32(&buf, handle);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
> + tpm_buf_append_u32(buf, handle);
>
> - rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
> - if (rc) {
> - tpm_buf_destroy(&buf);
> + rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
> + if (rc)
> return tpm_ret_to_err(rc);
> - }
>
> /* Skip TPMT_PUBLIC: */
> - offset += tpm_buf_read_u16(&buf, &offset);
> + offset += tpm_buf_read_u16(buf, &offset);
>
> /*
> * Ensure space for the length field of TPM2B_NAME and hashAlg field of
> * TPMT_HA (the extra four bytes).
> */
> - if (offset + 4 > tpm_buf_length(&buf)) {
> - tpm_buf_destroy(&buf);
> + if (offset + 4 > tpm_buf_length(buf))
> return -EIO;
> - }
> -
> - rc = tpm_buf_read_u16(&buf, &offset);
> - name_size_alg = name_size(&buf.data[offset]);
>
> - if (name_size_alg < 0) {
> - tpm_buf_destroy(&buf);
> + rc = tpm_buf_read_u16(buf, &offset);
> + name_size_alg = name_size(&buf->data[offset]);
> + if (name_size_alg < 0)
> return name_size_alg;
> - }
>
> - if (rc != name_size_alg) {
> - tpm_buf_destroy(&buf);
> + if (rc != name_size_alg)
> return -EIO;
> - }
>
> - if (offset + rc > tpm_buf_length(&buf)) {
> - tpm_buf_destroy(&buf);
> + if (offset + rc > tpm_buf_length(buf))
> return -EIO;
> - }
>
> - memcpy(name, &buf.data[offset], rc);
> - tpm_buf_destroy(&buf);
> + memcpy(name, &buf->data[offset], rc);
> return name_size_alg;
> }
> #endif /* CONFIG_TCG_TPM2_HMAC */
> @@ -987,8 +977,8 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
> */
> int tpm2_start_auth_session(struct tpm_chip *chip)
> {
> + struct tpm_buf *buf __free(kfree) = NULL;
> struct tpm2_auth *auth;
> - struct tpm_buf buf;
> u32 null_key;
> int rc;
>
> @@ -1007,41 +997,43 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
>
> auth->session = TPM_HEADER_SIZE;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> - if (rc)
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> + rc = -ENOMEM;
> goto out;
> + }
>
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
> /* salt key handle */
> - tpm_buf_append_u32(&buf, null_key);
> + tpm_buf_append_u32(buf, null_key);
> /* bind key handle */
> - tpm_buf_append_u32(&buf, TPM2_RH_NULL);
> + tpm_buf_append_u32(buf, TPM2_RH_NULL);
> /* nonce caller */
> get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
> - tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
> - tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
> + tpm_buf_append_u16(buf, sizeof(auth->our_nonce));
> + tpm_buf_append(buf, auth->our_nonce, sizeof(auth->our_nonce));
>
> /* append encrypted salt and squirrel away unencrypted in auth */
> - tpm_buf_append_salt(&buf, chip, auth);
> + tpm_buf_append_salt(buf, chip, auth);
> /* session type (HMAC, audit or policy) */
> - tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
> + tpm_buf_append_u8(buf, TPM2_SE_HMAC);
>
> /* symmetric encryption parameters */
> /* symmetric algorithm */
> - tpm_buf_append_u16(&buf, TPM_ALG_AES);
> + tpm_buf_append_u16(buf, TPM_ALG_AES);
> /* bits for symmetric algorithm */
> - tpm_buf_append_u16(&buf, AES_KEY_BITS);
> + tpm_buf_append_u16(buf, AES_KEY_BITS);
> /* symmetric algorithm mode (must be CFB) */
> - tpm_buf_append_u16(&buf, TPM_ALG_CFB);
> + tpm_buf_append_u16(buf, TPM_ALG_CFB);
> /* hash algorithm for session */
> - tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
> + tpm_buf_append_u16(buf, TPM_ALG_SHA256);
>
> - rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
> + rc = tpm_ret_to_err(tpm_transmit_cmd(chip, buf, 0, "StartAuthSession"));
> tpm2_flush_context(chip, null_key);
>
> if (rc == TPM2_RC_SUCCESS)
> - rc = tpm2_parse_start_auth_session(auth, &buf);
> -
> - tpm_buf_destroy(&buf);
> + rc = tpm2_parse_start_auth_session(auth, buf);
>
> if (rc == TPM2_RC_SUCCESS) {
> chip->auth = auth;
> @@ -1262,19 +1254,21 @@ static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
> static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
> u32 *handle, u8 *name)
> {
> + struct tpm_buf *template __free(kfree) = NULL;
> + struct tpm_buf *buf __free(kfree) = NULL;
> int rc;
> - struct tpm_buf buf;
> - struct tpm_buf template;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
> - if (rc)
> - return rc;
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
>
> - rc = tpm_buf_init_sized(&template);
> - if (rc) {
> - tpm_buf_destroy(&buf);
> - return rc;
> - }
> + template = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!template)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
> + tpm_buf_init_sized(template, TPM_BUFSIZE);
>
> /*
> * create the template. Note: in order for userspace to
> @@ -1286,75 +1280,72 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
> */
>
> /* key type */
> - tpm_buf_append_u16(&template, TPM_ALG_ECC);
> + tpm_buf_append_u16(template, TPM_ALG_ECC);
>
> /* name algorithm */
> - tpm_buf_append_u16(&template, TPM_ALG_SHA256);
> + tpm_buf_append_u16(template, TPM_ALG_SHA256);
>
> /* object properties */
> - tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
> + tpm_buf_append_u32(template, TPM2_OA_NULL_KEY);
>
> /* sauth policy (empty) */
> - tpm_buf_append_u16(&template, 0);
> + tpm_buf_append_u16(template, 0);
>
> /* BEGIN parameters: key specific; for ECC*/
>
> /* symmetric algorithm */
> - tpm_buf_append_u16(&template, TPM_ALG_AES);
> + tpm_buf_append_u16(template, TPM_ALG_AES);
>
> /* bits for symmetric algorithm */
> - tpm_buf_append_u16(&template, AES_KEY_BITS);
> + tpm_buf_append_u16(template, AES_KEY_BITS);
>
> /* algorithm mode (must be CFB) */
> - tpm_buf_append_u16(&template, TPM_ALG_CFB);
> + tpm_buf_append_u16(template, TPM_ALG_CFB);
>
> /* scheme (NULL means any scheme) */
> - tpm_buf_append_u16(&template, TPM_ALG_NULL);
> + tpm_buf_append_u16(template, TPM_ALG_NULL);
>
> /* ECC Curve ID */
> - tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
> + tpm_buf_append_u16(template, TPM2_ECC_NIST_P256);
>
> /* KDF Scheme */
> - tpm_buf_append_u16(&template, TPM_ALG_NULL);
> + tpm_buf_append_u16(template, TPM_ALG_NULL);
>
> /* unique: key specific; for ECC it is two zero size points */
> - tpm_buf_append_u16(&template, 0);
> - tpm_buf_append_u16(&template, 0);
> + tpm_buf_append_u16(template, 0);
> + tpm_buf_append_u16(template, 0);
>
> /* END parameters */
>
> /* primary handle */
> - tpm_buf_append_u32(&buf, hierarchy);
> - tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
> + tpm_buf_append_u32(buf, hierarchy);
> + tpm_buf_append_empty_auth(buf, TPM2_RS_PW);
>
> /* sensitive create size is 4 for two empty buffers */
> - tpm_buf_append_u16(&buf, 4);
> + tpm_buf_append_u16(buf, 4);
>
> /* sensitive create auth data (empty) */
> - tpm_buf_append_u16(&buf, 0);
> + tpm_buf_append_u16(buf, 0);
>
> /* sensitive create sensitive data (empty) */
> - tpm_buf_append_u16(&buf, 0);
> + tpm_buf_append_u16(buf, 0);
>
> /* the public template */
> - tpm_buf_append(&buf, template.data, template.length);
> - tpm_buf_destroy(&template);
> + tpm_buf_append(buf, template->data, template->length);
>
> /* outside info (empty) */
> - tpm_buf_append_u16(&buf, 0);
> + tpm_buf_append_u16(buf, 0);
>
> /* creation PCR (none) */
> - tpm_buf_append_u32(&buf, 0);
> + tpm_buf_append_u32(buf, 0);
>
> - rc = tpm_transmit_cmd(chip, &buf, 0,
> + rc = tpm_transmit_cmd(chip, buf, 0,
> "attempting to create NULL primary");
>
> if (rc == TPM2_RC_SUCCESS)
> - rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
> + rc = tpm2_parse_create_primary(chip, buf, handle, hierarchy,
> name);
>
> - tpm_buf_destroy(&buf);
> -
> return rc;
> }
>
> diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
> index 60354cd53b5c..cbf86ff5931f 100644
> --- a/drivers/char/tpm/tpm2-space.c
> +++ b/drivers/char/tpm/tpm2-space.c
> @@ -71,24 +71,25 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
> int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> unsigned int *offset, u32 *handle)
> {
> - struct tpm_buf tbuf;
> struct tpm2_context *ctx;
> unsigned int body_size;
> int rc;
>
> - rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
> - if (rc)
> - return rc;
> + struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!tbuf)
> + return -ENOMEM;
> +
> + tpm_buf_init(tbuf, TPM_BUFSIZE);
> + tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
>
> ctx = (struct tpm2_context *)&buf[*offset];
> body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
> - tpm_buf_append(&tbuf, &buf[*offset], body_size);
> + tpm_buf_append(tbuf, &buf[*offset], body_size);
>
> - rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
> + rc = tpm_transmit_cmd(chip, tbuf, 4, NULL);
> if (rc < 0) {
> dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> __func__, rc);
> - tpm_buf_destroy(&tbuf);
> return -EFAULT;
> } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
> rc == TPM2_RC_REFERENCE_H0) {
> @@ -103,64 +104,55 @@ int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> * flushed outside the space
> */
> *handle = 0;
> - tpm_buf_destroy(&tbuf);
> return -ENOENT;
> } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
> - tpm_buf_destroy(&tbuf);
> return -EINVAL;
> } else if (rc > 0) {
> dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> __func__, rc);
> - tpm_buf_destroy(&tbuf);
> return -EFAULT;
> }
>
> - *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
> + *handle = be32_to_cpup((__be32 *)&tbuf->data[TPM_HEADER_SIZE]);
> *offset += body_size;
> -
> - tpm_buf_destroy(&tbuf);
> return 0;
> }
>
> int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
> unsigned int buf_size, unsigned int *offset)
> {
> - struct tpm_buf tbuf;
> unsigned int body_size;
> int rc;
>
> - rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
> - if (rc)
> - return rc;
> + struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!tbuf)
> + return -ENOMEM;
>
> - tpm_buf_append_u32(&tbuf, handle);
> + tpm_buf_init(tbuf, TPM_BUFSIZE);
> + tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
> + tpm_buf_append_u32(tbuf, handle);
>
> - rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
> + rc = tpm_transmit_cmd(chip, tbuf, 0, NULL);
> if (rc < 0) {
> dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> __func__, rc);
> - tpm_buf_destroy(&tbuf);
> return -EFAULT;
> } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
> - tpm_buf_destroy(&tbuf);
> return -ENOENT;
> } else if (rc) {
> dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> __func__, rc);
> - tpm_buf_destroy(&tbuf);
> return -EFAULT;
> }
>
> - body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
> + body_size = tpm_buf_length(tbuf) - TPM_HEADER_SIZE;
> if ((*offset + body_size) > buf_size) {
> dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
> - tpm_buf_destroy(&tbuf);
> return -ENOMEM;
> }
>
> - memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
> + memcpy(&buf[*offset], &tbuf->data[TPM_HEADER_SIZE], body_size);
> *offset += body_size;
> - tpm_buf_destroy(&tbuf);
> return 0;
> }
>
> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> index 7bb0f4d4a2ed..b81fd2a537df 100644
> --- a/drivers/char/tpm/tpm_vtpm_proxy.c
> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> @@ -395,40 +395,36 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
>
> static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
> {
> - struct tpm_buf buf;
> int rc;
> const struct tpm_header *header;
> struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
>
> + struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + tpm_buf_init(buf, TPM_BUFSIZE);
> if (chip->flags & TPM_CHIP_FLAG_TPM2)
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
> - TPM2_CC_SET_LOCALITY);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
> else
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
> - TPM_ORD_SET_LOCALITY);
> - if (rc)
> - return rc;
> - tpm_buf_append_u8(&buf, locality);
> + tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
> +
> + tpm_buf_append_u8(buf, locality);
>
> proxy_dev->state |= STATE_DRIVER_COMMAND;
>
> - rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
> + rc = tpm_transmit_cmd(chip, buf, 0, "attempting to set locality");
>
> proxy_dev->state &= ~STATE_DRIVER_COMMAND;
>
> - if (rc < 0) {
> - locality = rc;
> - goto out;
> - }
> + if (rc < 0)
> + return rc;
>
> - header = (const struct tpm_header *)buf.data;
> + header = (const struct tpm_header *)buf->data;
> rc = be32_to_cpu(header->return_code);
> if (rc)
> locality = -1;
>
> -out:
> - tpm_buf_destroy(&buf);
> -
> return locality;
> }
>
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 202da079d500..14d75c1482d6 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -26,6 +26,7 @@
> #include <crypto/aes.h>
>
> #define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
> +#define TPM_BUFSIZE 4096
>
> #define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
> #define TPM2_MAX_PCR_BANKS 8
> @@ -378,13 +379,15 @@ enum tpm_buf_flags {
> };
>
> /*
> - * A string buffer type for constructing TPM commands.
> + * A buffer for constructing and parsing TPM commands, responses and sized
> + * (TPM2B) buffers.
> */
> struct tpm_buf {
> - u32 flags;
> - u32 length;
> - u8 *data;
> + u8 flags;
> + u16 length;
> + u16 capacity;
> u8 handles;
> + u8 data[];
> };
>
> enum tpm2_object_attributes {
> @@ -415,12 +418,11 @@ struct tpm2_hash {
> unsigned int tpm_id;
> };
>
> -int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal);
> +void tpm_buf_init(struct tpm_buf *buf, u16 buf_size);
> +void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size);
> void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal);
> -int tpm_buf_init_sized(struct tpm_buf *buf);
> void tpm_buf_reset_sized(struct tpm_buf *buf);
> -void tpm_buf_destroy(struct tpm_buf *buf);
> -u32 tpm_buf_length(struct tpm_buf *buf);
> +u16 tpm_buf_length(struct tpm_buf *buf);
> void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length);
> void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value);
> void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value);
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index 13513819991e..6e03fa7227e4 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -317,9 +317,8 @@ static int TSS_checkhmac2(unsigned char *buffer,
> * For key specific tpm requests, we will generate and send our
> * own TPM command packets using the drivers send function.
> */
> -static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
> +static int trusted_tpm_send(struct tpm_buf *buf)
> {
> - struct tpm_buf buf;
> int rc;
>
> if (!chip)
> @@ -329,12 +328,9 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
> if (rc)
> return rc;
>
> - buf.flags = 0;
> - buf.length = buflen;
> - buf.data = cmd;
> - dump_tpm_buf(cmd);
> - rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
> - dump_tpm_buf(cmd);
> + dump_tpm_buf(buf->data);
> + rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
> + dump_tpm_buf(buf->data);
>
> if (rc > 0)
> /* TPM error */
> @@ -380,7 +376,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
> tpm_buf_append_u32(tb, handle);
> tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
>
> - ret = trusted_tpm_send(tb->data, tb->length);
> + ret = trusted_tpm_send(tb);
> if (ret < 0)
> return ret;
>
> @@ -404,7 +400,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
> return -ENODEV;
>
> tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
> - ret = trusted_tpm_send(tb->data, tb->length);
> + ret = trusted_tpm_send(tb);
> if (ret < 0)
> return ret;
>
> @@ -513,7 +509,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
> tpm_buf_append_u8(tb, cont);
> tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
>
> - ret = trusted_tpm_send(tb->data, tb->length);
> + ret = trusted_tpm_send(tb);
> if (ret < 0)
> goto out;
>
> @@ -604,7 +600,7 @@ static int tpm_unseal(struct tpm_buf *tb,
> tpm_buf_append_u8(tb, cont);
> tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
>
> - ret = trusted_tpm_send(tb->data, tb->length);
> + ret = trusted_tpm_send(tb);
> if (ret < 0) {
> pr_info("authhmac failed (%d)\n", ret);
> return ret;
> @@ -631,23 +627,23 @@ static int tpm_unseal(struct tpm_buf *tb,
> static int key_seal(struct trusted_key_payload *p,
> struct trusted_key_options *o)
> {
> - struct tpm_buf tb;
> int ret;
>
> - ret = tpm_buf_init(&tb, 0, 0);
> - if (ret)
> - return ret;
> + struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!tb)
> + return -ENOMEM;
> +
> + tpm_buf_init(tb, TPM_BUFSIZE);
>
> /* include migratable flag at end of sealed key */
> p->key[p->key_len] = p->migratable;
>
> - ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
> + ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
> p->key, p->key_len + 1, p->blob, &p->blob_len,
> o->blobauth, o->pcrinfo, o->pcrinfo_len);
> if (ret < 0)
> pr_info("srkseal failed (%d)\n", ret);
>
> - tpm_buf_destroy(&tb);
> return ret;
> }
>
> @@ -657,14 +653,15 @@ static int key_seal(struct trusted_key_payload *p,
> static int key_unseal(struct trusted_key_payload *p,
> struct trusted_key_options *o)
> {
> - struct tpm_buf tb;
> int ret;
>
> - ret = tpm_buf_init(&tb, 0, 0);
> - if (ret)
> - return ret;
> + struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!tb)
> + return -ENOMEM;
> +
> + tpm_buf_init(tb, TPM_BUFSIZE);
>
> - ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> + ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> o->blobauth, p->key, &p->key_len);
> if (ret < 0)
> pr_info("srkunseal failed (%d)\n", ret);
> @@ -672,7 +669,6 @@ static int key_unseal(struct trusted_key_payload *p,
> /* pull migratable flag out of sealed key */
> p->migratable = p->key[--p->key_len];
>
> - tpm_buf_destroy(&tb);
> return ret;
> }
>
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 6340823f8b53..6f5c34b885fb 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -234,7 +234,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> struct trusted_key_options *options)
> {
> off_t offset = TPM_HEADER_SIZE;
> - struct tpm_buf buf, sized;
> + struct tpm_buf *buf __free(kfree) = NULL;
> + struct tpm_buf *sized __free(kfree) = NULL;
> int blob_len = 0;
> int hash;
> u32 flags;
> @@ -255,97 +256,100 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> if (rc)
> goto out_put;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
> - if (rc) {
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> + rc = -ENOMEM;
> tpm2_end_auth_session(chip);
> goto out_put;
> }
>
> - rc = tpm_buf_init_sized(&sized);
> - if (rc) {
> - tpm_buf_destroy(&buf);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
> +
> + sized = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!sized) {
> + rc = -ENOMEM;
> tpm2_end_auth_session(chip);
> goto out_put;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> + tpm_buf_init_sized(sized, TPM_BUFSIZE);
> +
> + rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
> if (rc)
> goto out;
>
> - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
> options->keyauth, TPM_DIGEST_SIZE);
>
> /* sensitive */
> - tpm_buf_append_u16(&sized, options->blobauth_len);
> + tpm_buf_append_u16(sized, options->blobauth_len);
>
> if (options->blobauth_len)
> - tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
> + tpm_buf_append(sized, options->blobauth, options->blobauth_len);
>
> - tpm_buf_append_u16(&sized, payload->key_len);
> - tpm_buf_append(&sized, payload->key, payload->key_len);
> - tpm_buf_append(&buf, sized.data, sized.length);
> + tpm_buf_append_u16(sized, payload->key_len);
> + tpm_buf_append(sized, payload->key, payload->key_len);
> + tpm_buf_append(buf, sized->data, sized->length);
>
> /* public */
> - tpm_buf_reset_sized(&sized);
> - tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
> - tpm_buf_append_u16(&sized, hash);
> + tpm_buf_reset_sized(sized);
> + tpm_buf_append_u16(sized, TPM_ALG_KEYEDHASH);
> + tpm_buf_append_u16(sized, hash);
>
> /* key properties */
> flags = 0;
> flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
> flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
> - tpm_buf_append_u32(&sized, flags);
> + tpm_buf_append_u32(sized, flags);
>
> /* policy */
> - tpm_buf_append_u16(&sized, options->policydigest_len);
> + tpm_buf_append_u16(sized, options->policydigest_len);
> if (options->policydigest_len)
> - tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
> + tpm_buf_append(sized, options->policydigest, options->policydigest_len);
>
> /* public parameters */
> - tpm_buf_append_u16(&sized, TPM_ALG_NULL);
> - tpm_buf_append_u16(&sized, 0);
> + tpm_buf_append_u16(sized, TPM_ALG_NULL);
> + tpm_buf_append_u16(sized, 0);
>
> - tpm_buf_append(&buf, sized.data, sized.length);
> + tpm_buf_append(buf, sized->data, sized->length);
>
> /* outside info */
> - tpm_buf_append_u16(&buf, 0);
> + tpm_buf_append_u16(buf, 0);
>
> /* creation PCR */
> - tpm_buf_append_u32(&buf, 0);
> + tpm_buf_append_u32(buf, 0);
>
> - if (buf.flags & TPM_BUF_OVERFLOW) {
> + if (buf->flags & TPM_BUF_OVERFLOW) {
> rc = -E2BIG;
> tpm2_end_auth_session(chip);
> goto out;
> }
>
> - rc = tpm_buf_fill_hmac_session(chip, &buf);
> + rc = tpm_buf_fill_hmac_session(chip, buf);
> if (rc)
> goto out;
>
> - rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
> - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> + rc = tpm_transmit_cmd(chip, buf, 4, "sealing data");
> + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> if (rc)
> goto out;
>
> - blob_len = tpm_buf_read_u32(&buf, &offset);
> - if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
> + blob_len = tpm_buf_read_u32(buf, &offset);
> + if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_BOUNDARY_ERROR) {
> rc = -E2BIG;
> goto out;
> }
> - if (buf.length - offset < blob_len) {
> + if (buf->length - offset < blob_len) {
> rc = -EFAULT;
> goto out;
> }
>
> - blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
> + blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len);
> if (blob_len < 0)
> rc = blob_len;
>
> out:
> - tpm_buf_destroy(&sized);
> - tpm_buf_destroy(&buf);
> -
> if (!rc)
> payload->blob_len = blob_len;
>
> @@ -373,7 +377,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> u32 *blob_handle)
> {
> u8 *blob_ref __free(kfree) = NULL;
> - struct tpm_buf buf;
> + struct tpm_buf *buf __free(kfree) = NULL;
> unsigned int private_len;
> unsigned int public_len;
> unsigned int blob_len;
> @@ -427,39 +431,38 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> if (rc)
> return rc;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
> - if (rc) {
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> tpm2_end_auth_session(chip);
> - return rc;
> + return -ENOMEM;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
> +
> + rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
> if (rc)
> - goto out;
> + return rc;
>
> - tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> + tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
> TPM_DIGEST_SIZE);
>
> - tpm_buf_append(&buf, blob, blob_len);
> + tpm_buf_append(buf, blob, blob_len);
>
> - if (buf.flags & TPM_BUF_OVERFLOW) {
> - rc = -E2BIG;
> + if (buf->flags & TPM_BUF_OVERFLOW) {
> tpm2_end_auth_session(chip);
> - goto out;
> + return -E2BIG;
> }
>
> - rc = tpm_buf_fill_hmac_session(chip, &buf);
> + rc = tpm_buf_fill_hmac_session(chip, buf);
> if (rc)
> - goto out;
> + return rc;
>
> - rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
> - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> + rc = tpm_transmit_cmd(chip, buf, 4, "loading blob");
> + rc = tpm_buf_check_hmac_response(chip, buf, rc);
> if (!rc)
> *blob_handle = be32_to_cpup(
> - (__be32 *) &buf.data[TPM_HEADER_SIZE]);
> -
> -out:
> - tpm_buf_destroy(&buf);
> + (__be32 *)&buf->data[TPM_HEADER_SIZE]);
>
> return tpm_ret_to_err(rc);
> }
> @@ -482,7 +485,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> u32 blob_handle)
> {
> struct tpm_header *head;
> - struct tpm_buf buf;
> + struct tpm_buf *buf __free(kfree) = NULL;
> u16 data_len;
> int offset;
> u8 *data;
> @@ -492,18 +495,21 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> if (rc)
> return rc;
>
> - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
> - if (rc) {
> + buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> + if (!buf) {
> tpm2_end_auth_session(chip);
> - return rc;
> + return -ENOMEM;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
> + tpm_buf_init(buf, TPM_BUFSIZE);
> + tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
> +
> + rc = tpm_buf_append_name(chip, buf, blob_handle, NULL);
> if (rc)
> - goto out;
> + return rc;
>
> if (!options->policyhandle) {
> - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
> options->blobauth,
> options->blobauth_len);
> } else {
> @@ -518,39 +524,36 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> * could repeat our actions with the exfiltrated
> * password.
> */
> - tpm2_buf_append_auth(&buf, options->policyhandle,
> + tpm2_buf_append_auth(buf, options->policyhandle,
> NULL /* nonce */, 0, 0,
> options->blobauth, options->blobauth_len);
> if (tpm2_chip_auth(chip)) {
> - tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
> + tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
> + NULL, 0);
> } else {
> - offset = buf.handles * 4 + TPM_HEADER_SIZE;
> - head = (struct tpm_header *)buf.data;
> - if (tpm_buf_length(&buf) == offset)
> + offset = buf->handles * 4 + TPM_HEADER_SIZE;
> + head = (struct tpm_header *)buf->data;
> + if (tpm_buf_length(buf) == offset)
> head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> }
> }
>
> - rc = tpm_buf_fill_hmac_session(chip, &buf);
> + rc = tpm_buf_fill_hmac_session(chip, buf);
> if (rc)
> - goto out;
> + return rc;
>
> - rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
> - rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> + rc = tpm_transmit_cmd(chip, buf, 6, "unsealing");
> + rc = tpm_buf_check_hmac_response(chip, buf, rc);
>
> if (!rc) {
> data_len = be16_to_cpup(
> - (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
> - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) {
> - rc = -EFAULT;
> - goto out;
> - }
> + (__be16 *)&buf->data[TPM_HEADER_SIZE + 4]);
> + if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE)
> + return -EFAULT;
>
> - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
> - rc = -EFAULT;
> - goto out;
> - }
> - data = &buf.data[TPM_HEADER_SIZE + 6];
> + if (tpm_buf_length(buf) < TPM_HEADER_SIZE + 6 + data_len)
> + return -EFAULT;
> + data = &buf->data[TPM_HEADER_SIZE + 6];
>
> if (payload->old_format) {
> /* migratable flag is at the end of the key */
> @@ -567,8 +570,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> }
> }
>
> -out:
> - tpm_buf_destroy(&buf);
> return tpm_ret_to_err(rc);
> }
>
> --
> 2.47.3
>
>
^ permalink raw reply
* Re: [PATCH 06/11] moduleparam: Add seq_buf-based .get callback alongside .get_str
From: Petr Pavlu @ 2026-05-25 16:19 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Pengpeng Hou, Richard Weinberger, Anton Ivanov,
Johannes Berg, Rafael J. Wysocki, Len Brown, Corey Minyard,
Gabriel Somlo, Michael S. Tsirkin, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, David Airlie, Simona Vetter,
Bart Van Assche, Jason Gunthorpe, Leon Romanovsky,
Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Bjorn Helgaas, Hannes Reinecke, James E.J. Bottomley,
Martin K. Petersen, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Greg Kroah-Hartman, Jiri Slaby, Alan Stern, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Jason Baron, Jim Cromie, Tiwei Bie,
Benjamin Berg, Ilpo Järvinen, David E. Box,
Maciej W. Rozycki, Srinivas Pandruvada, Peter Zijlstra,
Heiko Carstens, Vasily Gorbik, Sean Christopherson, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Vinod Koul, Frank Li, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Alexander Potapenko, Marco Elver, Dmitry Vyukov,
Andrew Morton, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andy Shevchenko, Georgia Garcia, kvm, dmaengine,
linux-modules, kasan-dev, linux-mm, apparmor,
linux-security-module, linux-um, linux-acpi, openipmi-developer,
qemu-devel, intel-gfx, dri-devel, linux-rdma, linux-media,
linux-pci, linux-scsi, linux-pm, linuxppc-dev, linux-serial,
linux-usb, usb-storage, virtualization, linux-kernel, linux-arch,
netdev, linux-fsdevel, linux-hardening
In-Reply-To: <20260521133326.2465264-6-kees@kernel.org>
On 5/21/26 3:33 PM, Kees Cook wrote:
> Add a new struct kernel_param_ops::get callback whose signature
> takes a struct seq_buf instead of a raw char buffer:
>
> int (*get)(struct seq_buf *sb, const struct kernel_param *kp);
>
> The previously-legacy .get field is now .get_str (char *buffer);
> .get is the new seq_buf-aware form. param_attr_show() prefers .get
> when set, otherwise falls back to .get_str. WARN_ON_ONCE() if both
> are set. Return contract for .get:
>
> < 0 : errno propagated to userspace; seq_buf contents discarded
> = 0 : success; length derived from seq_buf_used()
> > 0 : forbidden; the dispatcher WARN_ON_ONCE()s and treats as 0
>
> The default policy on seq_buf_has_overflowed() is silent truncation,
> matching scnprintf()/sysfs_emit() behaviour. Callbacks that want a
> specific overflow errno can check seq_buf_has_overflowed() and
> return their preferred error.
>
> No callbacks use .get yet; the legacy path is still the only one in use
> after this commit. A subsequent commit teaches DEFINE_KERNEL_PARAM_OPS
> to route initializers by type.
>
> Signed-off-by: Kees Cook <kees@kernel.org>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
-- Petr
^ permalink raw reply
* Re: [PATCH 07/11] moduleparam: Route DEFINE_KERNEL_PARAM_OPS get pointer via _Generic
From: Petr Pavlu @ 2026-05-25 16:24 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Pengpeng Hou, Richard Weinberger, Anton Ivanov,
Johannes Berg, Rafael J. Wysocki, Len Brown, Corey Minyard,
Gabriel Somlo, Michael S. Tsirkin, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, David Airlie, Simona Vetter,
Bart Van Assche, Jason Gunthorpe, Leon Romanovsky,
Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Bjorn Helgaas, Hannes Reinecke, James E.J. Bottomley,
Martin K. Petersen, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Greg Kroah-Hartman, Jiri Slaby, Alan Stern, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Jason Baron, Jim Cromie, Tiwei Bie,
Benjamin Berg, Ilpo Järvinen, David E. Box,
Maciej W. Rozycki, Srinivas Pandruvada, Peter Zijlstra,
Heiko Carstens, Vasily Gorbik, Sean Christopherson, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Vinod Koul, Frank Li, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Alexander Potapenko, Marco Elver, Dmitry Vyukov,
Andrew Morton, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andy Shevchenko, Georgia Garcia, kvm, dmaengine,
linux-modules, kasan-dev, linux-mm, apparmor,
linux-security-module, linux-um, linux-acpi, openipmi-developer,
qemu-devel, intel-gfx, dri-devel, linux-rdma, linux-media,
linux-pci, linux-scsi, linux-pm, linuxppc-dev, linux-serial,
linux-usb, usb-storage, virtualization, linux-kernel, linux-arch,
netdev, linux-fsdevel, linux-hardening
In-Reply-To: <20260521133326.2465264-7-kees@kernel.org>
On 5/21/26 3:33 PM, Kees Cook wrote:
> Make the DEFINE_KERNEL_PARAM_OPS family route their _get argument to
> either .get (struct seq_buf *) or .get_str (char *) at compile time
> based on the pointer's actual function signature. Two helper macros
> do the routing:
>
> _KERNEL_PARAM_OPS_GET - return the pointer if it has the seq_buf
> signature, otherwise NULL of that type
> _KERNEL_PARAM_OPS_GET_STR - mirror image for the char * signature
>
> Both use _Generic; only the two valid function-pointer types are
> listed, so any third-party type is a compile error rather than
> silently falling through.
>
> Now a callback whose body has been migrated from char * to struct
> seq_buf * needs no change at its kernel_param_ops initialization site,
> because the macro picks up the new type automatically and assigns to
> the correct field.
>
> Signed-off-by: Kees Cook <kees@kernel.org>
> ---
> include/linux/moduleparam.h | 33 ++++++++++++++++++++++++++-------
> 1 file changed, 26 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
> index c52120f6ac28..795bc7c654ef 100644
> --- a/include/linux/moduleparam.h
> +++ b/include/linux/moduleparam.h
> @@ -85,15 +85,32 @@ struct kernel_param_ops {
> *
> * static DEFINE_KERNEL_PARAM_OPS(my_ops, my_set, my_get);
> *
> - * Routing the @_set and @_get function pointers through the macro
> - * (rather than naming the struct fields at every call site) lets the
> - * field layout change in one place when callbacks are migrated to a
> - * new signature.
> + * @_get may be either of:
> + * int (*)(struct seq_buf *, const struct kernel_param *) (seq_buf)
> + * int (*)(char *, const struct kernel_param *) (legacy)
> + *
> + * The macro uses _Generic to route the function pointer to the
> + * matching field (.get or .get_str) at compile time, leaving the
> + * other field NULL. Each helper matches the wrong prototype signature
> + * and returns NULL, falling through to the default branch otherwise;
> + * if @_get has neither expected signature the assignment to the
> + * fields gets a normal compile-time type-mismatch error.
> */
> +#define _KERNEL_PARAM_OPS_GET(_get) \
> + _Generic((_get), \
> + int (*)(char *, const struct kernel_param *): NULL, \
> + default: (_get))
> +
> +#define _KERNEL_PARAM_OPS_GET_STR(_get) \
> + _Generic((_get), \
> + int (*)(struct seq_buf *, const struct kernel_param *): NULL, \
> + default: (_get))
> +
> #define DEFINE_KERNEL_PARAM_OPS(_name, _set, _get) \
> const struct kernel_param_ops _name = { \
> .set = (_set), \
> - .get_str = (_get), \
> + .get = _KERNEL_PARAM_OPS_GET(_get), \
> + .get_str = _KERNEL_PARAM_OPS_GET_STR(_get), \
> }
>
> /* As DEFINE_KERNEL_PARAM_OPS, with KERNEL_PARAM_OPS_FL_NOARG set. */
> @@ -101,14 +118,16 @@ struct kernel_param_ops {
> const struct kernel_param_ops _name = { \
> .flags = KERNEL_PARAM_OPS_FL_NOARG, \
> .set = (_set), \
> - .get_str = (_get), \
> + .get = _KERNEL_PARAM_OPS_GET(_get), \
> + .get_str = _KERNEL_PARAM_OPS_GET_STR(_get), \
> }
>
> /* As DEFINE_KERNEL_PARAM_OPS, with an additional .free callback. */
> #define DEFINE_KERNEL_PARAM_OPS_FREE(_name, _set, _get, _free) \
> const struct kernel_param_ops _name = { \
> .set = (_set), \
> - .get_str = (_get), \
> + .get = _KERNEL_PARAM_OPS_GET(_get), \
> + .get_str = _KERNEL_PARAM_OPS_GET_STR(_get), \
> .free = (_free), \
> }
>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
-- Petr
^ permalink raw reply
* Re: [PATCH] Fix various spelling mistakes
From: Casey Schaufler @ 2026-05-25 16:06 UTC (permalink / raw)
To: fffsqian, john.johansen
Cc: paul, jmorris, serge, linux-security-module, linux-kernel,
Qingshuang Fu, Casey Schaufler
In-Reply-To: <20260525021500.47667-1-fffsqian@163.com>
On 5/24/2026 7:15 PM, fffsqian@163.com wrote:
> From: Qingshuang Fu <fuqingshuang@kylinos.cn>
>
> Fix three spelling errors found in code comments:
>
> - overriden → overridden
> - interated → interacted
> - dont → don't
>
> Signed-off-by: Qingshuang Fu <fuqingshuang@kylinos.cn>
The AppArmor and Smack changes go in through different trees.
You should split this into two patches and submit them to the
appropriate maintainers.
> ---
> security/apparmor/domain.c | 2 +-
> security/apparmor/lsm.c | 2 +-
> security/smack/smackfs.c | 2 +-
> 3 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
> index f02bf770f638..7e097c40720a 100644
> --- a/security/apparmor/domain.c
> +++ b/security/apparmor/domain.c
> @@ -135,7 +135,7 @@ static int label_compound_match(struct aa_profile *profile,
> struct label_it i;
> struct path_cond cond = { };
>
> - /* find first subcomponent that is in view and going to be interated with */
> + /* find first subcomponent that is in view and going to be interacted with */
> label_for_each(i, label, tp) {
> if (!aa_ns_visible(profile->ns, tp->ns, inview))
> continue;
> diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
> index 3491e9f60194..51a388cfea11 100644
> --- a/security/apparmor/lsm.c
> +++ b/security/apparmor/lsm.c
> @@ -1493,7 +1493,7 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
> *
> * Note: can not sleep may be called with locks held
> *
> - * dont want protocol specific in __skb_recv_datagram()
> + * don't want protocol specific in __skb_recv_datagram()
> * to deny an incoming connection socket_sock_rcv_skb()
> */
> static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
> diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
> index 6e62dcb36f74..2820bd3ee72e 100644
> --- a/security/smack/smackfs.c
> +++ b/security/smack/smackfs.c
> @@ -115,7 +115,7 @@ struct smack_known *smack_syslog_label;
> /*
> * Ptrace current rule
> * SMACK_PTRACE_DEFAULT regular smack ptrace rules (/proc based)
> - * SMACK_PTRACE_EXACT labels must match, but can be overriden with
> + * SMACK_PTRACE_EXACT labels must match, but can be overridden with
> * CAP_SYS_PTRACE
> * SMACK_PTRACE_DRACONIAN labels must match, CAP_SYS_PTRACE has no effect
> */
>
> base-commit: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
^ permalink raw reply
* Re: [PATCH 08/11] params: Convert generic kernel_param_ops .get helpers to seq_buf
From: Petr Pavlu @ 2026-05-25 17:10 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Pengpeng Hou, Richard Weinberger, Anton Ivanov,
Johannes Berg, Rafael J. Wysocki, Len Brown, Corey Minyard,
Gabriel Somlo, Michael S. Tsirkin, Jani Nikula, Joonas Lahtinen,
Rodrigo Vivi, Tvrtko Ursulin, David Airlie, Simona Vetter,
Bart Van Assche, Jason Gunthorpe, Leon Romanovsky,
Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
Bjorn Helgaas, Hannes Reinecke, James E.J. Bottomley,
Martin K. Petersen, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Greg Kroah-Hartman, Jiri Slaby, Alan Stern, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Jason Baron, Jim Cromie, Tiwei Bie,
Benjamin Berg, Ilpo Järvinen, David E. Box,
Maciej W. Rozycki, Srinivas Pandruvada, Peter Zijlstra,
Heiko Carstens, Vasily Gorbik, Sean Christopherson, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Vinod Koul, Frank Li, Daniel Gomez, Sami Tolvanen,
Aaron Tomlin, Alexander Potapenko, Marco Elver, Dmitry Vyukov,
Andrew Morton, John Johansen, Paul Moore, James Morris,
Serge E. Hallyn, Andy Shevchenko, Georgia Garcia, kvm, dmaengine,
linux-modules, kasan-dev, linux-mm, apparmor,
linux-security-module, linux-um, linux-acpi, openipmi-developer,
qemu-devel, intel-gfx, dri-devel, linux-rdma, linux-media,
linux-pci, linux-scsi, linux-pm, linuxppc-dev, linux-serial,
linux-usb, usb-storage, virtualization, linux-kernel, linux-arch,
netdev, linux-fsdevel, linux-hardening
In-Reply-To: <20260521133326.2465264-8-kees@kernel.org>
On 5/21/26 3:33 PM, Kees Cook wrote:
> Convert the generic struct kernel_param_ops .get helpers in
> kernel/params.c directly to the seq_buf signature, drop their legacy
> "char *" form, and refresh prototypes in <linux/moduleparam.h>:
>
> param_get_byte/short/ushort/int/uint/long/ulong/ullong/hexint
> param_get_charp/bool/invbool/string
> param_array_get
>
> The STANDARD_PARAM_DEF() macro expands to a seq_buf body for every
> numeric helper. param_array_get() now writes element output directly
> into the parent seq_buf when the element ops provide .get; it only
> allocates the per-call PAGE_SIZE bounce buffer when the element ops
> still use the legacy .get_str path. The common "rewrite the prior
> element's trailing newline as a comma" step lives outside both
> branches so the two paths share it.
>
> The non-core changes in this commit (arch/x86/kvm, mm/kfence,
> drivers/dma/dmatest, security/apparmor) are the small set of callers that
> directly invoke one of the converted generic helpers from their own .get
> callback (e.g. an apparmor wrapper that adds a capability check and then
> delegates to param_get_bool()). Because the helpers' signature changes
> here, these wrappers must move in lockstep. Each of them is updated
> to take "struct seq_buf *" and pass it through; param_get_debug() in
> apparmor also pulls aa_print_debug_params() (and its val_mask_to_str()
> helper, in security/apparmor/lib.c) over to seq_buf, since that is the
> only consumer. No other behavioural change is intended.
>
> Custom .get callbacks that do not delegate to a generic helper (and
> therefore still match the .get_str signature) are routed automatically
> to the .get_str field by the DEFINE_KERNEL_PARAM_OPS _Generic dispatcher
> and are deliberately left alone here, to be changed separately within
> their respective subsystems.
>
> Signed-off-by: Kees Cook <kees@kernel.org>
> ---
> [...]
> @@ -453,36 +457,46 @@ static int param_array_set(const char *val, const struct kernel_param *kp)
> arr->num ?: &temp_num);
> }
>
> -static int param_array_get(char *buffer, const struct kernel_param *kp)
> +static int param_array_get(struct seq_buf *s, const struct kernel_param *kp)
> {
> - int i, off, ret;
> - char *elem_buf;
> const struct kparam_array *arr = kp->arr;
> struct kernel_param p = *kp;
> + char *elem_buf = NULL;
> + int i, ret = 0;
>
> - elem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
> - if (!elem_buf)
> - return -ENOMEM;
> + for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) {
> + size_t before = s->len;
>
> - for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
> p.arg = arr->elem + arr->elemsize * i;
> check_kparam_locked(p.mod);
> - ret = arr->ops->get_str(elem_buf, &p);
> - if (ret < 0)
> - goto out;
> - ret = min(ret, (int)(PAGE_SIZE - 1 - off));
> - if (!ret)
> +
> + if (arr->ops->get) {
> + ret = arr->ops->get(s, &p);
> + if (ret < 0)
> + goto out;
> + } else {
> + if (!elem_buf) {
> + elem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!elem_buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + }
> + ret = arr->ops->get_str(elem_buf, &p);
> + if (ret < 0)
> + goto out;
> + seq_buf_putmem(s, elem_buf, ret);
> + }
> +
> + /* Nothing got written (e.g. overflow) — stop. */
> + if (s->len == before)
> break;
> +
> /* Replace the previous element's trailing newline with a comma. */
> - if (i)
> - buffer[off - 1] = ',';
> - memcpy(buffer + off, elem_buf, ret);
> - off += ret;
> - if (off == PAGE_SIZE - 1)
> - break;
> + if (i && s->buffer[before - 1] == '\n')
> + s->buffer[before - 1] = ',';
> }
> - buffer[off] = '\0';
> - ret = off;
> + ret = 0;
> out:
> kfree(elem_buf);
> return ret;
Since you're almost completely rewriting the logic in param_array_get(),
I suggest tightening it up a bit. The function could warn or return an
error when a kernel_param_ops::get/get_str() call adds a string that
doesn't terminate with '\n', specifically, when the call adds either
a zero-length string or a non-zero-length string that ends with
a different character (unless an overflow occurred).
The updated code silently stops the loop when a get call returns
a zero-length string. Similarly, handling of a string not terminated by
'\n' is halfway there because of the added check
"s->buffer[before - 1] == '\n'".
--
Thanks,
Petr
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox