Linux Security Modules development
 help / color / mirror / Atom feed
* 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

* Re: [PATCH] tpm-buf: memory-safe allocations
From: James Bottomley @ 2026-05-25 17:50 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,
	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>

On Fri, 2026-05-22 at 04:35 +0300, Jarkko Sakkinen wrote:
> 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 isn't really a good idea from a security point of view.  Remember
the buffer has to be big enough for both the sent and the received
data.  Today we simply set TPM_BUFSIZE to the maximum amount a TPM
requires and all the send and receives just work.  If we let callers
set this size, we're asking for them to get it wrong (or at least
forget about the receive part) and for us to get a DMA overrun from the
TPM ... which might be potentially exploitable depending on how it
occurs (think of an unseal of user chosen data overrunning).

I get the desire to support some of the newer chunked commands, but
since none of them is yet present in the kernel, why not introduce an
API that works only for them to avoid the risk of a security cockup in
existing code?

Regards,

James


^ permalink raw reply

* Re: [net-next] netlabel: fix IPv6 unlabeled address add error handling
From: patchwork-bot+netdevbpf @ 2026-05-25 19:10 UTC (permalink / raw)
  To: Chenguang Zhao
  Cc: paul, davem, edumazet, kuba, pabeni, horms, netdev,
	linux-security-module
In-Reply-To: <20260522022910.398416-1-zhaochenguang@kylinos.cn>

Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Fri, 22 May 2026 10:29:10 +0800 you wrote:
> netlbl_unlhsh_add_addr6() always returned zero after
> netlbl_af6list_add(), masking failures such as duplicate
> IPv6 static label entries.
> 
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
>  net/netlabel/netlabel_unlabeled.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Here is the summary with links:
  - [net-next] netlabel: fix IPv6 unlabeled address add error handling
    https://git.kernel.org/netdev/net-next/c/56872b930fee

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v8 1/9] landlock: Add a place for flags to layer rules
From: Mickaël Salaün @ 2026-05-25 20:39 UTC (permalink / raw)
  To: Justin Suess
  Cc: Tingmao Wang, Günther Noack, Jan Kara, Abhinav Saxena,
	linux-security-module
In-Reply-To: <ahNx8CNCeqhU_Ide@zenbox>

On Sun, May 24, 2026 at 06:08:00PM -0400, Justin Suess wrote:
> 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.

LANDLOCK_NUM_ACCESS_MAX looks like a better name (even if it also
include scopes).

> > 
> > 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.

Agreed

^ permalink raw reply

* Re: [PATCH v8 3/9] landlock: Suppress logging when quiet flag is present
From: Mickaël Salaün @ 2026-05-25 20:40 UTC (permalink / raw)
  To: Tingmao Wang
  Cc: Günther Noack, Justin Suess, Jan Kara, Abhinav Saxena,
	linux-security-module
In-Reply-To: <5a253279ddbc797fa320849e46f7c88d7578a581.1775490344.git.m@maowtm.org>

On Mon, Apr 06, 2026 at 04:52:16PM +0100, Tingmao Wang wrote:
> The quietness behaviour is as documented in the previous patch.
> 
> For optional accesses, since the existing deny_masks can only store 2x4bit
> of layer index, with no way to represent "no layer", we need to either
> expand it or have another field to correctly handle quieting of those.
> This commit uses the latter approach - we add another field to store which
> optional access (of the 2) are covered by quiet rules in their respective
> layers as stored in deny_masks.
> 
> We can avoid making struct landlock_file_security larger by converting the
> existing fown_layer to a 4bit field.  This commit does that, and adds test
> to ensure that it is large enough for LANDLOCK_MAX_NUM_LAYERS-1.
> 
> Signed-off-by: Tingmao Wang <m@maowtm.org>
> ---
> 
> Changes in v8:
> - Rebase on top of mic/next
> - Populate request.rule_flags in hook_unix_find()
> 
> Changes in v7:
> - Following change in commit 1, now we need to copy rule_flags into
>   landlock_request before calling landlock_log_denial for relevant fs
>   denials
> - Remove left over param comment
> 
> Changes in v5:
> - Update code style and comment in get_layer_from_deny_masks() and
>   landlock_log_denial()
> - Now that rule_flags is moved into landlock_request, this version removes
>   the extra parameter for landlock_log_denial and gets rid of
>   no_rule_flags, simplifying some code.
> - Fix build failure without CONFIG_AUDIT (reported by Justin Suess)
> 
> Changes in v3:
> - Renamed patch title from "Check for quiet flag in landlock_log_denial"
>   to this given the growth.
> - Moved quiet bit check after domain_exec check
> - Rename, style and comment fixes suggested by Mickaël.
> - Squashed patch 6/6 from v2 "Implement quiet for optional accesses" into
>   this one.  Changes to that below:
> - Refactor the quiet flag setting in get_layer_from_deny_masks() to be
>   more clear.
> - Add KUnit tests
> - Fix comments, add WARN_ON_ONCE, use __const_hweight64() as suggested by
>   review
> - Move build_check_file_security to fs.c
> - Use a typedef for quiet_optional_accesses, add static_assert, and
>   improve docs on landlock_get_quiet_optional_accesses.
> 
> Changes in v2:
> - Supports the new quiet access masks.
> - Support quieting scope requests (but not ptrace and attempted mounting
>   for now)
> 
>  security/landlock/access.h |   5 +
>  security/landlock/audit.c  | 255 +++++++++++++++++++++++++++++++++++--
>  security/landlock/audit.h  |   3 +
>  security/landlock/domain.c |  33 +++++
>  security/landlock/domain.h |   5 +
>  security/landlock/fs.c     |  35 +++++
>  security/landlock/fs.h     |  17 ++-
>  security/landlock/net.c    |  16 +--
>  8 files changed, 340 insertions(+), 29 deletions(-)
> 
> diff --git a/security/landlock/access.h b/security/landlock/access.h
> index c19d5bc13944..2775df80c7da 100644
> --- a/security/landlock/access.h
> +++ b/security/landlock/access.h
> @@ -120,4 +120,9 @@ static inline bool access_mask_subset(access_mask_t subset,
>  	return (subset | superset) == superset;
>  }
>  
> +/* A bitmask that is large enough to hold set of optional accesses. */
> +typedef u8 optional_access_t;
> +static_assert(BITS_PER_TYPE(optional_access_t) >=
> +	      HWEIGHT(_LANDLOCK_ACCESS_FS_OPTIONAL));
> +
>  #endif /* _SECURITY_LANDLOCK_ACCESS_H */
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index 8d0edf94037d..2941b6d88688 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> @@ -246,7 +246,8 @@ static void test_get_denied_layer(struct kunit *const test)
>  static size_t
>  get_layer_from_deny_masks(access_mask_t *const access_request,
>  			  const access_mask_t all_existing_optional_access,
> -			  const deny_masks_t deny_masks)
> +			  const deny_masks_t deny_masks,
> +			  u8 quiet_optional_accesses, bool *quiet)

optional_access_t quiet_optional_accesses

This type should be used everywhere.

>  {
>  	const unsigned long access_opt = all_existing_optional_access;
>  	const unsigned long access_req = *access_request;
> @@ -254,6 +255,7 @@ get_layer_from_deny_masks(access_mask_t *const access_request,
>  	size_t youngest_layer = 0;
>  	size_t access_index = 0;
>  	unsigned long access_bit;
> +	bool should_quiet = false;
>  
>  	/* This will require change with new object types. */
>  	WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
> @@ -264,18 +266,29 @@ get_layer_from_deny_masks(access_mask_t *const access_request,
>  			const size_t layer =
>  				(deny_masks >> (access_index * 4)) &
>  				(LANDLOCK_MAX_NUM_LAYERS - 1);
> +			const bool layer_has_quiet =
> +				!!(quiet_optional_accesses & BIT(access_index));
>  
>  			if (layer > youngest_layer) {
>  				youngest_layer = layer;
>  				missing = BIT(access_bit);
> +				should_quiet = layer_has_quiet;
>  			} else if (layer == youngest_layer) {
>  				missing |= BIT(access_bit);
> +				/*
> +				 * Whether the layer has rules with quiet flag covering
> +				 * the file accessed does not depend on the access, and so
> +				 * the following WARN_ON_ONCE() should not fail.
> +				 */
> +				WARN_ON_ONCE(should_quiet && !layer_has_quiet);
> +				should_quiet = layer_has_quiet;
>  			}
>  		}
>  		access_index++;
>  	}
>  
>  	*access_request = missing;
> +	*quiet = should_quiet;
>  	return youngest_layer;
>  }
>  
> @@ -285,42 +298,188 @@ static void test_get_layer_from_deny_masks(struct kunit *const test)
>  {
>  	deny_masks_t deny_mask;
>  	access_mask_t access;
> +	u8 quiet_optional_accesses;

ditto

> diff --git a/security/landlock/audit.h b/security/landlock/audit.h
> index 56778331b58c..c2da854d4405 100644
> --- a/security/landlock/audit.h
> +++ b/security/landlock/audit.h
> @@ -48,6 +48,9 @@ struct landlock_request {
>  	/* Required fields for requests with deny masks. */
>  	const access_mask_t all_existing_optional_access;
>  	deny_masks_t deny_masks;
> +	u8 quiet_optional_accesses;

ditto, use optional_access_t

> +
> +	struct collected_rule_flags rule_flags;
>  };
>  
>  #ifdef CONFIG_AUDIT
> diff --git a/security/landlock/domain.c b/security/landlock/domain.c
> index 06b6bd845060..f365721050b7 100644
> --- a/security/landlock/domain.c
> +++ b/security/landlock/domain.c
> @@ -156,6 +156,39 @@ get_layer_deny_mask(const access_mask_t all_existing_optional_access,
>  	       << ((access_weight - 1) * HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1));
>  }
>  
> +/**
> + * landlock_get_quiet_optional_accesses - Get optional accesses which are
> + * "covered" by quiet rule flags.
> + *
> + * Returns a bitmask of which optional access are denied by layers for
> + * which rule_flags.quiet_masks has the corresponding bit set.
> + */
> +optional_access_t landlock_get_quiet_optional_accesses(
> +	const access_mask_t all_existing_optional_access,
> +	const deny_masks_t deny_masks,
> +	const struct collected_rule_flags rule_flags)
> +{
> +	const unsigned long access_opt = all_existing_optional_access;
> +	size_t access_index = 0;
> +	unsigned long access_bit;
> +	optional_access_t quiet_optional_accesses = 0;

It's only correct here.

> +
> +	/* This will require change with new object types. */
> +	WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
> +
> +	for_each_set_bit(access_bit, &access_opt,
> +			 BITS_PER_TYPE(access_mask_t)) {
> +		const u8 layer = (deny_masks >> (access_index * 4)) &
> +				 (LANDLOCK_MAX_NUM_LAYERS - 1);
> +		const bool is_quiet = !!(rule_flags.quiet_masks & BIT(layer));
> +
> +		if (is_quiet)
> +			quiet_optional_accesses |= BIT(access_index);
> +		access_index++;
> +	}
> +	return quiet_optional_accesses;
> +}
> +
>  #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
>  
>  static void test_get_layer_deny_mask(struct kunit *const test)

^ permalink raw reply

* Re: [PATCH v4 0/7] landlock: Add UDP access control support
From: Mickaël Salaün @ 2026-05-25 20:28 UTC (permalink / raw)
  To: Matthieu Buffet
  Cc: Günther Noack, linux-security-module, Mikhail Ivanov,
	konstantin.meskhidze, Tingmao Wang, netdev
In-Reply-To: <20260502124306.3975990-1-matthieu@buffet.re>

FYI, I pushed this patch series in -next (except the doc patch which
conflict).

Also, some interesting (and some other not relevant) findings here:
https://sashiko.dev/#/patchset/20260502124306.3975990-1-matthieu%40buffet.re

On Sat, May 02, 2026 at 02:42:59PM +0200, Matthieu Buffet wrote:
> Hi,
> 
> This is V4 of UDP access control in Landlock. Thanks to the round of
> review of v3, access rights have changed to something that seems easier
> to use and understand. It adds only two access rights, to restrict
> configuring local and remote addresses on UDP sockets. The one that
> restricts setting a remote address also controls sending datagrams to
> explicit remote addresses -ignoring any remote address preset on the
> socket-. The one that restricts binding to a local port also applies
> when the kernel auto-binds an ephemeral port.
> v1:
> Link: https://lore.kernel.org/all/20240916122230.114800-1-matthieu@buffet.re/
> v2:
> Link: https://lore.kernel.org/all/20241214184540.3835222-1-matthieu@buffet.re/
> v3:
> Link: https://lore.kernel.org/all/20251212163704.142301-1-matthieu@buffet.re/
> 
> The limitation around allowing a process to send but not receive is
> still there, and could warrant another patch if there is a real user
> need.
> I'm just not super happy about the clarity of logs generated for denied
> autobinds ("domain=xxxxxx blockers=net.bind_udp"), due to the fact that
> addresses and ports are currently only logged if they are non-0. A later
> (coordinated LSM-wide) patch could improve readability by replacing != 0
> checks with new booleans in struct lsm_network_audit. I'm also not
> exactly happy with the integration in existing TCP selftests, but
> refactoring them has already been discussed earlier.
> 
> Changes v1->v2
> ==============
> - recvmsg hook is gone and sendmsg hook doesn't apply when sending to a
>   remote address pre-set on socket, to improve performance
> - don't add a get_addr_port() helper function, which required a weird
>   "am I in IPv4 or IPv6 context"
> - reorder hook prologue for consistency: check domain, then type and
>   family
> 
> Changes v2->v3
> ==============
> - removed support for sending datagrams with explicit destination
>   address of family AF_UNSPEC, which allowed to bypass restrictions with
>   a race condition
> - rebased on linux-mic/next => add support for auditing
> - fixed mistake in selftests when using unspec_srv variables, which were
>   implicitly of type SOCK_STREAM and did not actually test UDP code
> - add tests for IPPROTO_IP
> - improved docs, split off TCP-related refactoring
> 
> Changes v3->v4
> ==============
> - merge LANDLOCK_ACCESS_NET_CONNECT_UDP and
>   LANDLOCK_ACCESS_NET_SENDTO_UDP into
>   LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP (everything that might set the
>   destination of a datagram)
> - make LANDLOCK_ACCESS_NET_BIND_UDP apply when kernel is about to
>   auto-bind an ephemeral port for the caller. Block it if policy would
>   not allow an explicit call to bind(0)
> - only deny sending AF_UNSPEC datagrams on IPv6 sockets, where there is
>   a risk of the address family changing midway
> 
> Patch is based on https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
> 3457a5ccacd3 ("landlock: Document fallocate(2) as another truncation corner case")
> All lines added are covered with selftests, except the "default: return
> 0" in current_check_autobind_udp_socket() which is not currently
> reachable (net.c goes from 92.9%->94.6% line coverage).
> 
> Let me know what you think!
> 
> Closes: https://github.com/landlock-lsm/linux/issues/10
> 
> Matthieu Buffet (7):
>   landlock: Add UDP bind() access control
>   landlock: Add UDP connect() access control
>   landlock: Add UDP send access control
>   selftests/landlock: Add UDP bind/connect tests
>   selftests/landlock: Add tests for sendmsg()
>   samples/landlock: Add sandboxer UDP access control
>   landlock: Add documentation for UDP support
> 
>  Documentation/userspace-api/landlock.rst     |   89 +-
>  include/uapi/linux/landlock.h                |   35 +-
>  samples/landlock/sandboxer.c                 |   40 +-
>  security/landlock/audit.c                    |    3 +
>  security/landlock/limits.h                   |    2 +-
>  security/landlock/net.c                      |  161 ++-
>  security/landlock/syscalls.c                 |    2 +-
>  tools/testing/selftests/landlock/base_test.c |    4 +-
>  tools/testing/selftests/landlock/net_test.c  | 1146 ++++++++++++++++--
>  9 files changed, 1341 insertions(+), 141 deletions(-)
> 
> 
> base-commit: 3457a5ccacd34fdd5ebd3a4745e721b5a1239690
> -- 
> 2.39.5
> 
> 

^ permalink raw reply

* [PATCH] apparmor: fix use-after-free in rawdata dedup loop
From: Ruslan Valiyev @ 2026-05-25 22:04 UTC (permalink / raw)
  To: John Johansen
  Cc: Paul Moore, James Morris, Serge E . Hallyn, Georgia Garcia,
	Cengiz Can, Colin Ian King, apparmor, linux-security-module,
	linux-kernel, stable

aa_replace_profiles() walks ns->rawdata_list to dedup the incoming
policy blob against entries already attached to existing profiles.
Per the kernel-doc on struct aa_loaddata, list membership does not
hold a reference: profiles hold pcount, and when the last pcount
drops, do_ploaddata_rmfs() is queued on a workqueue that takes
ns->lock and removes the entry. Between dropping the last pcount
and the workqueue running, an entry remains on the list with
pcount == 0.

aa_get_profile_loaddata() is an unconditional kref_get() on
pcount, so when the dedup loop hits such an entry, refcount
hardening reports

  refcount_t: addition on 0; use-after-free.

inside aa_replace_profiles(), and the poisoned counter then
trips "saturated" and "underflow" warnings on the subsequent
uses of the same loaddata.

Before commit a0b7091c4de4 ("apparmor: fix race on rawdata
dereference") the dedup path used a get_unless_zero-style helper
on a single counter, so the existing "if (tmp)" guard was
meaningful. The split-refcount refactor introduced
aa_get_profile_loaddata(), which has plain kref_get() semantics,
and the guard quietly became a no-op.

Introduce aa_get_profile_loaddata_not0(), matching the existing
_not0 convention used by aa_get_profile_not0(), and use it for
the rawdata_list dedup lookup so dying entries are skipped.

Reproduced on x86_64 with v7.1-rc5 in QEMU+KVM running Ubuntu
24.04 + stress-ng 0.17.06:

  stress-ng --apparmor 1 --klog-check --timeout 60s

Without this patch the three refcount_t warnings fire within a
few seconds. With it the same 60 s run is clean. Coverage is a
smoke-test only; a longer soak with CONFIG_KASAN, CONFIG_KCSAN
and CONFIG_PROVE_LOCKING would be welcome from anyone with the
cycles.

Fixes: a0b7091c4de4 ("apparmor: fix race on rawdata dereference")
Reported-by: Colin Ian King <colin.i.king@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221513
Cc: stable@vger.kernel.org
Signed-off-by: Ruslan Valiyev <linuxoid@gmail.com>
---
 security/apparmor/include/policy_unpack.h | 19 +++++++++++++++++++
 security/apparmor/policy.c                |  8 ++++++--
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index e5a95dc4da1f..b9de0fdf9ee5 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -163,6 +163,25 @@ aa_get_profile_loaddata(struct aa_loaddata *data)
 	return data;
 }
 
+/**
+ * aa_get_profile_loaddata_not0 - get a profile reference count if not zero
+ * @data: reference to get a count on
+ *
+ * Like aa_get_profile_loaddata(), but safe to call on an entry that may
+ * be on a list (e.g. ns->rawdata_list) where the last pcount has already
+ * dropped and the deferred cleanup has not yet run.
+ *
+ * Returns: pointer to reference, or %NULL if @data is NULL or its
+ *          profile refcount has already reached zero.
+ */
+static inline struct aa_loaddata *
+aa_get_profile_loaddata_not0(struct aa_loaddata *data)
+{
+	if (data && kref_get_unless_zero(&data->pcount))
+		return data;
+	return NULL;
+}
+
 void __aa_loaddata_update(struct aa_loaddata *data, long revision);
 bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r);
 void aa_loaddata_kref(struct kref *kref);
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index b6a5eb4021db..e103cce6f4af 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1223,8 +1223,12 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
 			if (aa_rawdata_eq(rawdata_ent, udata)) {
 				struct aa_loaddata *tmp;
 
-				tmp = aa_get_profile_loaddata(rawdata_ent);
-				/* check we didn't fail the race */
+				/*
+				 * Entries remain on rawdata_list with
+				 * pcount == 0 until do_ploaddata_rmfs()
+				 * runs; only take a live profile ref.
+				 */
+				tmp = aa_get_profile_loaddata_not0(rawdata_ent);
 				if (tmp) {
 					aa_put_profile_loaddata(udata);
 					udata = tmp;

base-commit: e7ae89a0c97ce2b68b0983cd01eda67cf373517d
-- 
2.43.0


^ permalink raw reply related

* [PATCH] tomoyo: Fix NULL pointer dereference in tomoyo_init_request_info() when domain is NULL
From: Jiakai Xu @ 2026-05-26  1:23 UTC (permalink / raw)
  To: linux-kernel, linux-security-module
  Cc: Serge E . Hallyn, James Morris, Kentaro Takeda, Paul Moore,
	Tetsuo Handa, Jiakai Xu

tomoyo_domain() can return NULL when the current task has no TOMOYO
domain_info set.  When this happens, tomoyo_init_request_info() sets 
r->domain = NULL and then dereferences the NULL domain via 
domain->profile and later domain->acl_info_list in tomoyo_check_acl(), 
causing a kernel page fault.

Add a NULL check after tomoyo_domain() and return TOMOYO_CONFIG_DISABLED
when domain is NULL.  All callers that can reach this path already check
for TOMOYO_CONFIG_DISABLED and bail out, so this prevents the crash
without changing the control flow for those callers.

Fixes: c3ef1500ec8338 ("TOMOYO: Split files into some pieces.")
Signed-off-by: Jiakai Xu <xujiakai24@mails.ucas.ac.cn>
---
 security/tomoyo/util.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 6799b1122c9d8..cdc085390949c 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -1024,6 +1024,8 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r,
 	memset(r, 0, sizeof(*r));
 	if (!domain)
 		domain = tomoyo_domain();
+	if (!domain)
+		return TOMOYO_CONFIG_DISABLED;
 	r->domain = domain;
 	profile = domain->profile;
 	r->profile = profile;
-- 
2.34.1

Found by fuzzing. Here is the report:

Unable to handle kernel paging request at virtual address dfffffff00000003
Current syz-executor pgtable: 4K pagesize, 57-bit VAs, pgdp=0x000000012edec000
[dfffffff00000003] pgd=000000005fffe401, p4d=000000005fffe001, pud=0000000000000000
Oops [#1]
Modules linked in:
CPU: 0 UID: 0 PID: 3126 Comm: syz-executor Tainted: G        W           7.1.0-rc1-gdb909bd7986c #1 PREEMPT 
Tainted: [W]=WARN
Hardware name: riscv-virtio,qemu (DT)
epc : tomoyo_check_acl+0x90/0x4bc security/tomoyo/domain.c:173
 ra : tomoyo_check_acl+0x86/0x4bc security/tomoyo/domain.c:173
epc : ffffffff8149cf64 ra : ffffffff8149cf5a sp : ff200000040c7a90
 gp : ffffffff8a395420 tp : ff60000089d05040 t0 : ff200000040c7960
 t1 : 000000000000000f t2 : ffffffff86c068b0 s0 : ff200000040c7b10
 s1 : 0000000000000000 a0 : 0000000000000018 a1 : 0000000000000000
 a2 : 0000000000000002 a3 : ffffffff8149cf5a a4 : 0000000000000000
 a5 : dfffffff00000003 a6 : 0000000000000003 a7 : 000000003dfe34af
 s2 : dfffffff00000000 s3 : ff200000040c7b80 s4 : ff600000872a1510
 s5 : ffe3ffff00818f79 s6 : 0000000000000000 s7 : ffffffff814a2e18
 s8 : ff600000872a1500 s9 : ff200000040c7bc8 s10: 0000000000000002
 s11: 0000000000000000 t3 : 6a92f41f00000000 t4 : 0000000000001fff
 t5 : 00000000000000c8 t6 : 0000000000000002 ssp : 0000000000000000
status: 0000000200000120 badaddr: dfffffff00000003 cause: 000000000000000d
[<ffffffff8149cf64>] tomoyo_check_acl+0x90/0x4bc security/tomoyo/domain.c:173
[<ffffffff814a4108>] tomoyo_path_number_perm+0x384/0x5a4 security/tomoyo/file.c:738
[<ffffffff814b0cc4>] tomoyo_file_ioctl+0x28/0x34 security/tomoyo/tomoyo.c:350
[<ffffffff81454e8c>] security_file_ioctl+0xaa/0x2c2 security/security.c:2512
[<ffffffff80d45c5e>] __do_sys_ioctl fs/ioctl.c:591 [inline]
[<ffffffff80d45c5e>] __se_sys_ioctl fs/ioctl.c:583 [inline]
[<ffffffff80d45c5e>] __riscv_sys_ioctl+0xae/0x1e4 fs/ioctl.c:583
[<ffffffff80078fb2>] syscall_handler+0x94/0x118 arch/riscv/include/asm/syscall.h:112
[<ffffffff866fa9ea>] do_trap_ecall_u+0x43e/0x5de arch/riscv/kernel/traps.c:342
[<ffffffff867267f6>] handle_exception+0x15e/0x16a arch/riscv/kernel/entry.S:232
Code: 2544 1097 ff0a 80e7 e9c0 8513 0184 5793 0035 97ca (8703) 0007 
---[ end trace 0000000000000000 ]---
----------------
Code disassembly (best guess):
   0:	2544                	fld	fs1,136(a0)
   2:	ff0a1097          	auipc	ra,0xff0a1
   6:	e9c080e7          	jalr	-356(ra) # 0xffffffffff0a0e9e
   a:	01848513          	addi	a0,s1,24
   e:	00355793          	srli	a5,a0,0x3
  12:	97ca                	add	a5,a5,s2
* 14:	00078703          	lb	a4,0(a5) <-- trapping instruction

<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>


^ permalink raw reply related

* [PATCH v2 1/2] security: apparmor: fix two spelling mistakes
From: fffsqian @ 2026-05-26  1:38 UTC (permalink / raw)
  To: john.johansen, paul, jmorris, serge
  Cc: apparmor, linux-security-module, linux-kernel, Qingshuang Fu

From: Qingshuang Fu <fuqingshuang@kylinos.cn>

Fix two spelling errors in comment:
- interated  →  interacted
- dont      →  don't

Signed-off-by: Qingshuang Fu <fuqingshuang@kylinos.cn>

Changes since v1:
- Split original single patch into two standalone patches,
  separate AppArmor and Smack changes for different maintainer trees.
---
 security/apparmor/domain.c | 2 +-
 security/apparmor/lsm.c    | 2 +-
 2 files changed, 2 insertions(+), 2 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)
-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 2/2] security: smack: fix spelling mistake
From: fffsqian @ 2026-05-26  1:38 UTC (permalink / raw)
  To: casey, paul, jmorris, serge
  Cc: linux-security-module, linux-kernel, Qingshuang Fu

From: Qingshuang Fu <fuqingshuang@kylinos.cn>

Fix misspelling: overriden → overridden

Signed-off-by: Qingshuang Fu <fuqingshuang@kylinos.cn>

Changes since v1:
- Split original single patch into two standalone patches,
  separate AppArmor and Smack changes for different maintainer trees.
---
 security/smack/smackfs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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
  */
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH] tomoyo: Fix NULL pointer dereference in tomoyo_init_request_info() when domain is NULL
From: Tetsuo Handa @ 2026-05-26  2:04 UTC (permalink / raw)
  To: Jiakai Xu
  Cc: Serge E . Hallyn, James Morris, Kentaro Takeda, Paul Moore,
	linux-kernel, linux-security-module
In-Reply-To: <20260526012315.762144-1-xujiakai24@mails.ucas.ac.cn>

Thank you for a patch, but I don't think we need this change.

TOMOYO's initial domain is &tomoyo_kernel_domain, and each thread belongs to
a non-NULL domain. Therefore, tomoyo_domain() is not supposed to return NULL.

> Found by fuzzing. Here is the report:
> 
> Unable to handle kernel paging request at virtual address dfffffff00000003

Is this a NULL pointer dereference?
It seems to me that this is just a random memory corruption.


^ permalink raw reply

* [PATCH] keys: Pin request_key_auth payload in instantiate paths
From: Shaomin Chen @ 2026-05-26  2:48 UTC (permalink / raw)
  To: keyrings, linux-security-module, linux-kernel
  Cc: David Howells, Jarkko Sakkinen, Paul Moore, James Morris,
	Serge E. Hallyn

keyctl_instantiate_key_common() reads request_key_auth from the assumed
auth key before copying an instantiation payload from userspace.  The copy
can fault and sleep.  If the request completes and revokes the auth key in
that window, the auth payload can be detached and freed before the
instantiate path uses it again.

A request-key helper reproducer can trigger this race.  One helper child
blocks in KEYCTL_INSTANTIATE_IOV while the original helper instantiates the
requested key and returns.  KASAN then reports a use-after-free from the
stale request_key_auth payload in keyctl_instantiate_key_common().

Give request_key_auth payloads a refcount.  Take a payload reference while
authkey->sem stabilizes the payload and revocation state.  Hold that
reference across the instantiate and reject paths.  Drop the auth key
owning reference from revoke and destroy.

Reported-by: Shaomin Chen <eeesssooo020@gmail.com>
Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com
Signed-off-by: Shaomin Chen <eeesssooo020@gmail.com>
---
 include/keys/request_key_auth-type.h |  2 ++
 security/keys/internal.h             |  2 ++
 security/keys/keyctl.c               | 24 +++++++++++++++-----
 security/keys/request_key_auth.c     | 33 ++++++++++++++++++++++++++--
 4 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h
index 36b89a933310..01e42ee5f409 100644
--- a/include/keys/request_key_auth-type.h
+++ b/include/keys/request_key_auth-type.h
@@ -9,12 +9,14 @@
 #define _KEYS_REQUEST_KEY_AUTH_TYPE_H
 
 #include <linux/key.h>
+#include <linux/refcount.h>
 
 /*
  * Authorisation record for request_key().
  */
 struct request_key_auth {
 	struct rcu_head		rcu;
+	refcount_t		usage;
 	struct key		*target_key;
 	struct key		*dest_keyring;
 	const struct cred	*cred;
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 2cffa6dc8255..b7b622bc36a1 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target,
 					const void *callout_info,
 					size_t callout_len,
 					struct key *dest_keyring);
+struct request_key_auth *request_key_auth_get(struct key *authkey);
+void request_key_auth_put(struct request_key_auth *rka);
 
 extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
 
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ef855d69c97a..d14ace88e529 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id,
 	if (!instkey)
 		goto error;
 
-	rka = instkey->payload.data[0];
-	if (rka->target_key->serial != id)
+	rka = request_key_auth_get(instkey);
+	if (!rka) {
+		ret = -EKEYREVOKED;
 		goto error;
+	}
+	if (rka->target_key->serial != id)
+		goto error_put_rka;
 
 	/* pull the payload in if one was supplied */
 	payload = NULL;
@@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id,
 		ret = -ENOMEM;
 		payload = kvmalloc(plen, GFP_KERNEL);
 		if (!payload)
-			goto error;
+			goto error_put_rka;
 
 		ret = -EFAULT;
 		if (!copy_from_iter_full(payload, plen, from))
@@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id,
 
 error2:
 	kvfree_sensitive(payload, plen);
+error_put_rka:
+	request_key_auth_put(rka);
 error:
 	return ret;
 }
@@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
 	if (!instkey)
 		goto error;
 
-	rka = instkey->payload.data[0];
-	if (rka->target_key->serial != id)
+	rka = request_key_auth_get(instkey);
+	if (!rka) {
+		ret = -EKEYREVOKED;
 		goto error;
+	}
+	if (rka->target_key->serial != id)
+		goto error_put_rka;
 
 	/* find the destination keyring if present (which must also be
 	 * writable) */
 	ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
 	if (ret < 0)
-		goto error;
+		goto error_put_rka;
 
 	/* instantiate the key and link it into a keyring */
 	ret = key_reject_and_link(rka->target_key, timeout, error,
@@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
 	if (ret == 0)
 		keyctl_change_reqkey_auth(NULL);
 
+error_put_rka:
+	request_key_auth_put(rka);
 error:
 	return ret;
 }
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index a7d7538c1f70..282e09d8fa46 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *);
 static void request_key_auth_revoke(struct key *);
 static void request_key_auth_destroy(struct key *);
 static long request_key_auth_read(const struct key *, char *, size_t);
+static void request_key_auth_rcu_disposal(struct rcu_head *);
 
 /*
  * The request-key authorisation key type definition.
@@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_auth *rka)
 	kfree(rka);
 }
 
+/*
+ * Take a reference to the request-key authorisation payload so callers can
+ * drop authkey->sem before doing operations that may sleep.
+ */
+struct request_key_auth *request_key_auth_get(struct key *authkey)
+{
+	struct request_key_auth *rka;
+
+	down_read(&authkey->sem);
+	rka = dereference_key_locked(authkey);
+	if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags))
+		refcount_inc(&rka->usage);
+	else
+		rka = NULL;
+	up_read(&authkey->sem);
+
+	return rka;
+}
+
+void request_key_auth_put(struct request_key_auth *rka)
+{
+	if (rka && refcount_dec_and_test(&rka->usage))
+		call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+}
+
 /*
  * Dispose of the request_key_auth record under RCU conditions
  */
@@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key)
 	struct request_key_auth *rka = dereference_key_locked(key);
 
 	kenter("{%d}", key->serial);
+	if (!rka)
+		return;
 	rcu_assign_keypointer(key, NULL);
-	call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+	request_key_auth_put(rka);
 }
 
 /*
@@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key)
 	kenter("{%d}", key->serial);
 	if (rka) {
 		rcu_assign_keypointer(key, NULL);
-		call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+		request_key_auth_put(rka);
 	}
 }
 
@@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
 	rka = kzalloc_obj(*rka);
 	if (!rka)
 		goto error;
+	refcount_set(&rka->usage, 1);
 	rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
 	if (!rka->callout_info)
 		goto error_free_rka;
-- 
2.47.3

^ permalink raw reply related


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