Linux Integrity Measurement development
 help / color / mirror / Atom feed
* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-07  9:28 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Peter Huewe, Jason Gunthorpe, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTVGiubK5EKitM9u@kernel.org>

On Sun, Dec 07, 2025 at 11:19:10AM +0200, Jarkko Sakkinen wrote:
> On Sun, Dec 07, 2025 at 03:33:17PM +0900, James Bottomley wrote:
> > On Fri, 2025-12-05 at 02:49 +0200, Jarkko Sakkinen wrote:
> > > On Fri, Dec 05, 2025 at 01:20:30AM +0200, Jarkko Sakkinen wrote:
> > > > On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> > > > > tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and
> > > > > calls to
> > > > > tpm_buf_append_name() cause the exact same TPM2_ReadPublic
> > > > > command to be
> > > > > sent to the chip, causing unnecessary traffic.
> > > > > 
> > > > > 1. Export tpm2_read_public in order to make it callable from
> > > > > 'trusted_tpm2'.
> > > > > 2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted()
> > > > > in order to
> > > > >    halve the name resolutions required:
> > > > > 2a. Move tpm2_read_public() calls into trusted_tpm2.
> > > > > 2b. Pass TPM name to tpm_buf_append_name().
> > > > > 2c. Rework tpm_buf_append_name() to use the pre-resolved name.
> > > > > 
> > > > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > > > 
> > > > If ASN.1 blob would contain also name of the parent then zero
> > > > tpm2_read_public() calls would be required i.e., the main
> > > > bottleneck here inherits from the limitations of the file format
> > > > itself.
> > > 
> > > Along the lines of attached patch.
> > 
> > Well firstly [6] is already being taken by the creation data proposal,
> > so this would introduce an incompatibility between the kernel and the
> > spec, but secondly, if you want something like this in the spec before
> > it goes to the IETF you really need to propose it now.
> 
> What is the mailing list for the working group, or is this still
> unclear as of today?
> 
> > 
> > The problem with this particular addition is that it would be Linux
> > Kernel specific.  All the current TSSs already do a cached read public
> > under the covers when they add the session wrappings so the user facing
> > API they expose has nowhere to insert (or easily extract) a name field
> > and thus TSS based implementations would have no incentive to either
> > output or consume this field.  That's not to say the standard can't
> > have additions for crazy or niche use cases (that's what the rsaParent
> > flag is: a one off to support a niche SUSE use case) but it would be
> > hard to persuade user implementations to do this so the kernel would
> > have to interoperate with the case where it didn't exist anyway.
> 
> It is just matter of conditionally calling tpm2_read_public(). Not
> a big deal.
> 
> > 
> > The standard use today is with permanent handles for parents, where the
> > parent is created on the fly, so the name is actually returned from
> > TPM2_CreatePrimary for this use case.  It should be a simple matter to
> > bring the kernel implementation up to doing this as well.
> 
> Intercepting TPM2_CreatePrimary does not provide full coverage, as
> it does not address persistent keys, which kernel interface does
> support.

It also weaker solution in the sense that it lacks support for secondary
keys as parents.

Name digest is universal as it scales primary keys, secondary keys both
persistent and transient.

I'm not really sure why we should use TSS implementations as reference
for anything in this space. We always want to support "superset' because
it actually creates innovation and distruption to the pre-existing
science.

BR, Jarkko



^ permalink raw reply

* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-07  9:19 UTC (permalink / raw)
  To: James Bottomley
  Cc: linux-integrity, Peter Huewe, Jason Gunthorpe, Mimi Zohar,
	David Howells, Paul Moore, James Morris, Serge E. Hallyn,
	open list, open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <89d90617ba9b7a5eff1d5fad6bb9773033d3c18c.camel@HansenPartnership.com>

On Sun, Dec 07, 2025 at 03:33:17PM +0900, James Bottomley wrote:
> On Fri, 2025-12-05 at 02:49 +0200, Jarkko Sakkinen wrote:
> > On Fri, Dec 05, 2025 at 01:20:30AM +0200, Jarkko Sakkinen wrote:
> > > On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> > > > tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and
> > > > calls to
> > > > tpm_buf_append_name() cause the exact same TPM2_ReadPublic
> > > > command to be
> > > > sent to the chip, causing unnecessary traffic.
> > > > 
> > > > 1. Export tpm2_read_public in order to make it callable from
> > > > 'trusted_tpm2'.
> > > > 2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted()
> > > > in order to
> > > >    halve the name resolutions required:
> > > > 2a. Move tpm2_read_public() calls into trusted_tpm2.
> > > > 2b. Pass TPM name to tpm_buf_append_name().
> > > > 2c. Rework tpm_buf_append_name() to use the pre-resolved name.
> > > > 
> > > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > > 
> > > If ASN.1 blob would contain also name of the parent then zero
> > > tpm2_read_public() calls would be required i.e., the main
> > > bottleneck here inherits from the limitations of the file format
> > > itself.
> > 
> > Along the lines of attached patch.
> 
> Well firstly [6] is already being taken by the creation data proposal,
> so this would introduce an incompatibility between the kernel and the
> spec, but secondly, if you want something like this in the spec before
> it goes to the IETF you really need to propose it now.

What is the mailing list for the working group, or is this still
unclear as of today?

> 
> The problem with this particular addition is that it would be Linux
> Kernel specific.  All the current TSSs already do a cached read public
> under the covers when they add the session wrappings so the user facing
> API they expose has nowhere to insert (or easily extract) a name field
> and thus TSS based implementations would have no incentive to either
> output or consume this field.  That's not to say the standard can't
> have additions for crazy or niche use cases (that's what the rsaParent
> flag is: a one off to support a niche SUSE use case) but it would be
> hard to persuade user implementations to do this so the kernel would
> have to interoperate with the case where it didn't exist anyway.

It is just matter of conditionally calling tpm2_read_public(). Not
a big deal.

> 
> The standard use today is with permanent handles for parents, where the
> parent is created on the fly, so the name is actually returned from
> TPM2_CreatePrimary for this use case.  It should be a simple matter to
> bring the kernel implementation up to doing this as well.

Intercepting TPM2_CreatePrimary does not provide full coverage, as
it does not address persistent keys, which kernel interface does
support.

> 
> Regards,
> 
> James
>   
> 

BR, Jarkko

^ permalink raw reply

* Re: [PATCH] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: James Bottomley @ 2025-12-07  6:33 UTC (permalink / raw)
  To: Jarkko Sakkinen, linux-integrity
  Cc: Peter Huewe, Jason Gunthorpe, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <aTIsFJobEyISTO_y@kernel.org>

On Fri, 2025-12-05 at 02:49 +0200, Jarkko Sakkinen wrote:
> On Fri, Dec 05, 2025 at 01:20:30AM +0200, Jarkko Sakkinen wrote:
> > On Fri, Dec 05, 2025 at 12:31:27AM +0200, Jarkko Sakkinen wrote:
> > > tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and
> > > calls to
> > > tpm_buf_append_name() cause the exact same TPM2_ReadPublic
> > > command to be
> > > sent to the chip, causing unnecessary traffic.
> > > 
> > > 1. Export tpm2_read_public in order to make it callable from
> > > 'trusted_tpm2'.
> > > 2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted()
> > > in order to
> > >    halve the name resolutions required:
> > > 2a. Move tpm2_read_public() calls into trusted_tpm2.
> > > 2b. Pass TPM name to tpm_buf_append_name().
> > > 2c. Rework tpm_buf_append_name() to use the pre-resolved name.
> > > 
> > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > 
> > If ASN.1 blob would contain also name of the parent then zero
> > tpm2_read_public() calls would be required i.e., the main
> > bottleneck here inherits from the limitations of the file format
> > itself.
> 
> Along the lines of attached patch.

Well firstly [6] is already being taken by the creation data proposal,
so this would introduce an incompatibility between the kernel and the
spec, but secondly, if you want something like this in the spec before
it goes to the IETF you really need to propose it now.

The problem with this particular addition is that it would be Linux
Kernel specific.  All the current TSSs already do a cached read public
under the covers when they add the session wrappings so the user facing
API they expose has nowhere to insert (or easily extract) a name field
and thus TSS based implementations would have no incentive to either
output or consume this field.  That's not to say the standard can't
have additions for crazy or niche use cases (that's what the rsaParent
flag is: a one off to support a niche SUSE use case) but it would be
hard to persuade user implementations to do this so the kernel would
have to interoperate with the case where it didn't exist anyway.

The standard use today is with permanent handles for parents, where the
parent is created on the fly, so the name is actually returned from
TPM2_CreatePrimary for this use case.  It should be a simple matter to
bring the kernel implementation up to doing this as well.

Regards,

James
  


^ permalink raw reply

* Re: [PATCH 3/3] tpm-buf: Implement managed allocations
From: Jarkko Sakkinen @ 2025-12-06 17:00 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Stefan Berger, Peter Huewe, Jason Gunthorpe,
	James Bottomley, Mimi Zohar, David Howells, Paul Moore,
	James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, ross.philipson, daniel.kiper,
	dpsmith
In-Reply-To: <20251206113110.1793407-4-jarkko@kernel.org>

On Sat, Dec 06, 2025 at 01:31:10PM +0200, 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.
> 
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  drivers/char/tpm/tpm-buf.c                | 124 +++++----
>  drivers/char/tpm/tpm-sysfs.c              |  21 +-
>  drivers/char/tpm/tpm.h                    |   1 -
>  drivers/char/tpm/tpm1-cmd.c               | 166 +++++------
>  drivers/char/tpm/tpm2-cmd.c               | 320 ++++++++++------------
>  drivers/char/tpm/tpm2-sessions.c          | 142 +++++-----
>  drivers/char/tpm/tpm2-space.c             |  44 ++-
>  drivers/char/tpm/tpm_vtpm_proxy.c         |  30 +-
>  include/linux/tpm.h                       |  20 +-
>  security/keys/trusted-keys/trusted_tpm1.c |  36 +--
>  security/keys/trusted-keys/trusted_tpm2.c | 170 ++++++------
>  11 files changed, 508 insertions(+), 566 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> index 1b9dee0d0681..73be8a87b472 100644
> --- a/drivers/char/tpm/tpm-buf.c
> +++ b/drivers/char/tpm/tpm-buf.c
> @@ -7,82 +7,109 @@
>  #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_INVALID;
> +		}
> +	} 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_INVALID;
> +		}
> +	}
>  }
> -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_INVALID)
> +		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_INVALID)
> +		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,8 +117,11 @@ 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)
>  {
> +	if (buf->flags & TPM_BUF_INVALID)
> +		return 0;
> +
>  	return buf->length;
>  }
>  EXPORT_SYMBOL_GPL(tpm_buf_length);
> @@ -104,10 +134,12 @@ 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;
> +
>  	if (buf->flags & TPM_BUF_INVALID)
>  		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_INVALID;
>  		return;
> diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
> index 94231f052ea7..f5dcadb1ab3c 100644
> --- a/drivers/char/tpm/tpm-sysfs.c
> +++ b/drivers/char/tpm/tpm-sysfs.c
> @@ -32,28 +32,29 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!tpm_buf)
> +		return -ENOMEM;
> +
>  	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))
> -		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,
> -			     "attempting to read the PUBEK"))
> -		goto out_buf;
> +	if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, "TPM_ReadPubek"))
> +		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 +72,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 02c07fef41ba..5395927c62fc 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -32,7 +32,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..11090053ef54 100644
> --- a/drivers/char/tpm/tpm1-cmd.c
> +++ b/drivers/char/tpm/tpm1-cmd.c
> @@ -323,20 +323,14 @@ 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;
> -
> -	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);
> -
> -	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
> -	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_STARTUP);
> +	tpm_buf_append_u16(buf, TPM_ST_CLEAR);
> +	return tpm_transmit_cmd(chip, buf, 0, "TPM_Startup");
>  }
>  
>  int tpm1_get_timeouts(struct tpm_chip *chip)
> @@ -463,50 +457,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);
> @@ -531,81 +522,69 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
>  {
>  	struct tpm1_get_random_out *out;
>  	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;
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
>  
> +	tpm_buf_init(buf, TPM_BUFSIZE);
>  	do {
> -		tpm_buf_append_u32(&buf, num_bytes);
> +		tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> +		tpm_buf_append_u32(buf, num_bytes);
>  
> -		rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
> -				      "attempting get random");
> +		rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len), "TPM_GetRandom");
>  		if (rc) {
>  			if (rc > 0)
>  				rc = -EIO;
> -			goto out;
> +			return rc;
>  		}
>  
> -		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) {
> -			rc = -EFAULT;
> -			goto out;
> -		}
> +		if (recd > num_bytes)
> +			return -EFAULT;
> +
> +		if (buf->length < TPM_HEADER_SIZE +
> +				  sizeof(out->rng_data_len) + recd)
> +			return -EFAULT;
>  
> -		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
> -					   sizeof(out->rng_data_len) + recd) {
> -			rc = -EFAULT;
> -			goto out;
> -		}
>  		memcpy(dest, out->rng_data, recd);
>  
>  		dest += recd;
>  		total += recd;
>  		num_bytes -= recd;
> -
> -		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,
> -			      "attempting to read a pcr value");
> +	rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, "TPM_PCRRead");
>  	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 (buf->length < 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 +598,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;
> -
> -	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
> -	if (rc)
> -		return rc;
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
>  
> -	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, "TPM_ContinueSelfTest");
>  }
>  
>  /**
> @@ -742,22 +718,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;
>  	int rc;
>  
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
>  
>  	/* 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");
>  
> -	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> -	if (rc)
> -		return rc;
> +	tpm_buf_init(buf, TPM_BUFSIZE);
> +	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
> +
>  	/* 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 +750,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 +760,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 82076b6cfcf7..2682f5ec09cb 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -119,12 +119,15 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	if (pcr_idx >= TPM2_PLATFORM_PCR)
>  		return -EINVAL;
>  
> @@ -139,36 +142,31 @@ 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;
> +	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, "TPM2_PCR_Read");
>  	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 rc;
>  
>  	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 +182,52 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	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) {
> -		if (!disable_pcr_integrity)
> -			tpm2_end_auth_session(chip);
> -		return rc;
> -	}
> +	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(&buf, pcr_idx);
> -		tpm_buf_append_auth(chip, &buf, NULL, 0);
> +		tpm_buf_append_handle(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 (buf->flags & TPM_BUF_INVALID)
> +		return -EINVAL;
>  
>  	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");
> -	if (!disable_pcr_integrity)
> -		rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> +	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_PCR_Extend");
>  
> -	tpm_buf_destroy(&buf);
> +	if (!disable_pcr_integrity)
> +		rc = tpm_buf_check_hmac_response(chip, buf, rc);
>  
>  	return rc;
>  }
> @@ -258,7 +252,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;
> @@ -270,61 +263,58 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
>  	if (!num_bytes || max > TPM_MAX_RNG_DATA)
>  		return -EINVAL;
>  
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	err = tpm2_start_auth_session(chip);
>  	if (err)
>  		return err;
>  
> -	err = tpm_buf_init(&buf, 0, 0);
> -	if (err) {
> -		tpm2_end_auth_session(chip);
> -		return err;
> -	}
> -
> +	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);
> -		if (err) {
> -			tpm_buf_destroy(&buf);
> +		tpm_buf_append_u16(buf, num_bytes);
> +
> +		err = tpm_buf_fill_hmac_session(chip, buf);
> +		if (err)
>  			return err;
> -		}
>  
> -		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);
> +				       "TPM2_GetRandom");
> +		err = tpm_buf_check_hmac_response(chip, buf, err);
>  		if (err) {
>  			if (err > 0)
>  				err = -EIO;
> -			goto out;
> +			return err;
>  		}
>  
> -		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) {
> -			err = -EFAULT;
> -			goto out;
> +			return -EFAULT;
>  		}
>  		memcpy(dest_ptr, out->buffer, recd);
>  
> @@ -333,13 +323,7 @@ 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;
>  }
>  
>  /**
> @@ -349,20 +333,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, "TPM2_FlushContext");
>  }
>  EXPORT_SYMBOL_GPL(tpm2_flush_context);
>  
> @@ -389,19 +371,20 @@ 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];
> +		out = (struct tpm2_get_cap_out *)&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
> @@ -413,7 +396,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);
> @@ -430,15 +412,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, "TPM2_Shutdown");
>  }
>  
>  /**
> @@ -456,20 +437,19 @@ 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;
> -
> -		tpm_buf_append_u8(&buf, full);
> -		rc = tpm_transmit_cmd(chip, &buf, 0,
> -				      "attempting the self test");
> -		tpm_buf_destroy(&buf);
> +	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_SELF_TEST);
> +	for (full = 0; full < 2; full++) {
> +		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, "TPM2_SelfTest");
>  		if (rc == TPM2_RC_TESTING)
>  			rc = TPM2_RC_SUCCESS;
>  		if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
> @@ -494,23 +474,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);
> @@ -550,7 +531,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;
> @@ -562,39 +542,37 @@ 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, "TPM2_GetCapability(PCRS)");
>  	if (rc)
> -		goto out;
> +		return rc;
>  
> -	nr_possible_banks = be32_to_cpup(
> -		(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
> +	nr_possible_banks = be32_to_cpup((__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);
> -		rc = -ENOMEM;
> -		goto out;
> +		return -ENOMEM;
>  	}
>  
> -	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 +
>  			offsetof(struct tpm2_pcr_selection, size_of_select);
> -		if (pcr_select_offset >= end) {
> -			rc = -EFAULT;
> -			break;
> -		}
> +		if (pcr_select_offset >= end)
> +			return -EFAULT;
>  
>  		memcpy(&pcr_selection, marker, sizeof(pcr_selection));
>  		hash_alg = be16_to_cpu(pcr_selection.hash_alg);
> @@ -606,7 +584,7 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
>  
>  			rc = tpm2_init_bank_info(chip, nr_alloc_banks);
>  			if (rc < 0)
> -				break;
> +				return rc;
>  
>  			nr_alloc_banks++;
>  		}
> @@ -618,21 +596,21 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
>  	}
>  
>  	chip->nr_allocated_banks = nr_alloc_banks;
> -out:
> -	tpm_buf_destroy(&buf);
> -
> -	return rc;
> +	return 0;
>  }
>  
>  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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
>  	if (rc)
>  		goto out;
> @@ -649,30 +627,24 @@ 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)
> -		goto out;
> +	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);
>  
> -	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])) {
> +	if (nr_commands != 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;
> @@ -684,8 +656,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
>  		}
>  	}
>  
> -	tpm_buf_destroy(&buf);
> -
>  out:
>  	if (rc > 0)
>  		rc = -ENODEV;
> @@ -706,20 +676,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
>  
>  static int tpm2_startup(struct tpm_chip *chip)
>  {
> -	struct tpm_buf buf;
> -	int rc;
> -
> -	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);
> -
> -	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, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
> +	tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
> +	return tpm_transmit_cmd(chip, buf, 0, "TPM2_Startup");
>  }
>  
>  /**
> diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> index 92d116d73c6f..79f27a46bd7f 100644
> --- a/drivers/char/tpm/tpm2-sessions.c
> +++ b/drivers/char/tpm/tpm2-sessions.c
> @@ -168,7 +168,6 @@ 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;
>  	int rc, name_size_alg;
> -	struct tpm_buf buf;
>  
>  	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
>  	    mso != TPM2_MSO_NVRAM) {
> @@ -176,47 +175,41 @@ 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;
> +	struct tpm_buf *buf __free(kfree) = 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]);
> +	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);
> +	memcpy(name, &buf->data[offset], rc);
>  	return name_size_alg;
>  }
>  #endif /* CONFIG_TCG_TPM2_HMAC */
> @@ -982,7 +975,6 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
>  int tpm2_start_auth_session(struct tpm_chip *chip)
>  {
>  	struct tpm2_auth *auth;
> -	struct tpm_buf buf;
>  	u32 null_key;
>  	int rc;
>  
> @@ -991,6 +983,10 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
>  		return 0;
>  	}
>  
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	auth = kzalloc(sizeof(*auth), GFP_KERNEL);
>  	if (!auth)
>  		return -ENOMEM;
> @@ -1001,41 +997,37 @@ 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)
> -		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, "TPM2_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;
> @@ -1257,18 +1249,18 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
>  			       u32 *handle, u8 *name)
>  {
>  	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;
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
>  
> -	rc = tpm_buf_init_sized(&template);
> -	if (rc) {
> -		tpm_buf_destroy(&buf);
> -		return rc;
> -	}
> +	struct tpm_buf *template __free(kfree) = 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
> @@ -1280,75 +1272,71 @@ 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,
> -			      "attempting to create NULL primary");
> +	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_CreatePrimary");
>  
>  	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 0818bb517805..682dfc93845d 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 1e0585e45371..42e2a091f43d 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -25,7 +25,8 @@
>  #include <crypto/hash_info.h>
>  #include <crypto/aes.h>
>  
> -#define TPM_DIGEST_SIZE 20	/* Max TPM v1.2 PCR size */
> +#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
> @@ -376,13 +377,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;
>  	u8 handles;
> +	u16 length;
> +	u16 capacity;
> +	u8 data[];
>  };
>  
>  enum tpm2_object_attributes {
> @@ -413,12 +416,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 636acb66a4f6..759c1ecb0435 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -310,9 +310,10 @@ 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(void *cmd, size_t cmd_len)
>  {
> -	struct tpm_buf buf;
> +	u8 buf_data[512];
> +	struct tpm_buf *buf = (struct tpm_buf *)buf_data;
>  	int rc;
>  
>  	if (!chip)
> @@ -322,11 +323,10 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
>  	if (rc)
>  		return rc;
>  
> -	buf.flags = 0;
> -	buf.length = buflen;
> -	buf.data = cmd;
> +	tpm_buf_init(buf, sizeof(buf_data));
> +	tpm_buf_append(buf, cmd, cmd_len);
>  	dump_tpm_buf(cmd);
> -	rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
> +	rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
>  	dump_tpm_buf(cmd);
>  
>  	if (rc > 0)
> @@ -624,23 +624,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;
>  }
>  
> @@ -650,14 +650,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);
> @@ -665,7 +666,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 fdba641fefdb..fb76c4ea496f 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -234,7 +234,6 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>  		      struct trusted_key_options *options)
>  {
>  	off_t offset = TPM_HEADER_SIZE;
> -	struct tpm_buf buf, sized;
>  	int blob_len = 0;
>  	int hash;
>  	u32 flags;
> @@ -255,97 +254,99 @@ 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) {
> +	struct tpm_buf *buf __free(kfree) = 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);
> -		tpm2_end_auth_session(chip);
> -		goto out_put;
> -	}
> +	tpm_buf_init(buf, TPM_BUFSIZE);
> +	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
>  
> -	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> +	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
>  	if (rc)
> -		goto out;
> +		goto out_put;
>  
> -	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);
>  
> +	struct tpm_buf *sized __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!sized) {
> +		rc = -ENOMEM;
> +		tpm2_end_auth_session(chip);
> +		goto out_put;
> +	}
> +
>  	/* sensitive */
> -	tpm_buf_append_u16(&sized, options->blobauth_len);
> +	tpm_buf_init_sized(sized, TPM_BUFSIZE);
> +	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_init_sized(sized, TPM_BUFSIZE);
> +	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_INVALID) {
> +	if (buf->flags & TPM_BUF_INVALID) {
>  		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_INVALID) {
> +	blob_len = tpm_buf_read_u32(buf, &offset);
> +	if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_INVALID) {
>  		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 +374,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
>  			 u32 *blob_handle)
>  {
>  	u8 *blob_ref __free(kfree) = NULL;
> -	struct tpm_buf buf;
>  	unsigned int private_len;
>  	unsigned int public_len;
>  	unsigned int blob_len;
> @@ -427,39 +427,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) {
> +	struct tpm_buf *buf __free(kfree) = 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_INVALID) {
> -		rc = -E2BIG;
> +	if (buf->flags & TPM_BUF_INVALID) {
>  		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,28 +481,28 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  			   u32 blob_handle)
>  {
>  	struct tpm_header *head;
> -	struct tpm_buf buf;
>  	u16 data_len;
>  	int offset;
>  	u8 *data;
>  	int rc;
>  
> +	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	rc = tpm2_start_auth_session(chip);
>  	if (rc)
>  		return rc;
>  
> -	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
> -	if (rc) {
> -		tpm2_end_auth_session(chip);
> -		return rc;
> -	}
> +	tpm_buf_init(buf, TPM_BUFSIZE);
> +	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
>  
> -	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> +	rc = tpm_buf_append_name(chip, buf, options->keyhandle, 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 +517,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 +563,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
>  		}
>  	}
>  
> -out:
> -	tpm_buf_destroy(&buf);
>  	return tpm_ret_to_err(rc);
>  }
>  
> -- 
> 2.39.5
> 

I applied these patches to my master branch for these reasons:

1. It is a change for better, as managed allocations objectively
   deliver safer memory semantics overall.
2. The patch has been in my testing cycle for months now already.
   I've tested *everything* with it and without it in order to
   stress the change a bit.
3. Despite being large change it does fit to the "single logical
   change" category. It would only make things more dissolved if
   it was split in any possible way.
4. It's a great idea roughly base it on 6.19-rc1 so that it can
   be applied easily top of the mainline release (if wanted).

I will appreciate any testing results etc. and stay put on fixing
any possible issues. Also, e.g. tested-by tags can be added up
until the first pull request for 6.20.

BR, Jarkko

^ permalink raw reply

* [PATCH v5 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-06 16:13 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251206161345.2038730-1-jarkko@kernel.org>

Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
attribute containing TPM name of the parent key (in other words, TPMT_HA
blob).

The life-cycle for trusted keys will now proceed as follows:

1. Encode parent's name to the 'paretName' during tpm2_key_encode().
2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
   the attribute is not available, fallback on doing tpm2_read_public().

In other words, in the common (i.e., not loading a legacy key blob),
tpm2_read_public() will now only happen at the time when a key is first
created.

In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
unconditional so that the binary format of the saved keys is not dependent
on kernel configuration.

[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v5:
- Rebased to the mainline.
v4:
- No changes.
v3:
- Fixed three low-hanging fruit issues I found myself.
v2:
- A new patch.
---
 drivers/char/tpm/tpm2-cmd.c               | 90 ++++++++++++++++++++++
 drivers/char/tpm/tpm2-sessions.c          | 92 ----------------------
 include/linux/tpm.h                       |  9 +--
 security/keys/trusted-keys/tpm2key.asn1   | 17 ++++-
 security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++-------
 5 files changed, 173 insertions(+), 128 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 1f561ad3bdcf..360bc014d895 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -793,3 +793,93 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
 
 	return -1;
 }
+
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+	u16 hash_alg = get_unaligned_be16(name);
+
+	switch (hash_alg) {
+	case TPM_ALG_SHA1:
+		return SHA1_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA256:
+		return SHA256_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA384:
+		return SHA384_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA512:
+		return SHA512_DIGEST_SIZE + 2;
+	default:
+		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+		return -EINVAL;
+	}
+}
+
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+{
+	u32 mso = tpm2_handle_mso(handle);
+	off_t offset = TPM_HEADER_SIZE;
+	int rc, name_size_alg;
+	struct tpm_buf buf;
+
+	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+	    mso != TPM2_MSO_NVRAM) {
+		memcpy(name, &handle, sizeof(u32));
+		return sizeof(u32);
+	}
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+	if (rc)
+		return rc;
+
+	tpm_buf_append_u32(&buf, handle);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
+	if (rc) {
+		tpm_buf_destroy(&buf);
+		return tpm_ret_to_err(rc);
+	}
+
+	/* Skip TPMT_PUBLIC: */
+	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);
+		return -EIO;
+	}
+
+	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);
+		return -EIO;
+	}
+
+	if (offset + rc > tpm_buf_length(&buf)) {
+		tpm_buf_destroy(&buf);
+		return -EIO;
+	}
+
+	memcpy(name, &buf.data[offset], rc);
+	return name_size_alg;
+}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index e33be09446ff..c8b44b1a6cb6 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -140,98 +140,6 @@ struct tpm2_auth {
 	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
-#ifdef CONFIG_TCG_TPM2_HMAC
-/*
- * Name Size based on TPM algorithm (assumes no hash bigger than 255)
- */
-static int name_size(const u8 *name)
-{
-	u16 hash_alg = get_unaligned_be16(name);
-
-	switch (hash_alg) {
-	case TPM_ALG_SHA1:
-		return SHA1_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA256:
-		return SHA256_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA384:
-		return SHA384_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA512:
-		return SHA512_DIGEST_SIZE + 2;
-	default:
-		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
-		return -EINVAL;
-	}
-}
-
-/**
- * tpm2_read_public: Resolve TPM name for a handle
- * @chip:		TPM chip to use.
- * @handle:		TPM handle.
- * @name:		A buffer for returning the name blob. Must have a
- *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
- *
- * Returns size of TPM handle name of success.
- * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
- */
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
-{
-	u32 mso = tpm2_handle_mso(handle);
-	off_t offset = TPM_HEADER_SIZE;
-	int rc, name_size_alg;
-	struct tpm_buf buf;
-
-	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
-	    mso != TPM2_MSO_NVRAM) {
-		memcpy(name, &handle, sizeof(u32));
-		return sizeof(u32);
-	}
-
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
-	if (rc)
-		return rc;
-
-	tpm_buf_append_u32(&buf, handle);
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
-	if (rc) {
-		tpm_buf_destroy(&buf);
-		return tpm_ret_to_err(rc);
-	}
-
-	/* Skip TPMT_PUBLIC: */
-	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);
-		return -EIO;
-	}
-
-	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);
-		return -EIO;
-	}
-
-	if (offset + rc > tpm_buf_length(&buf)) {
-		tpm_buf_destroy(&buf);
-		return -EIO;
-	}
-
-	memcpy(name, &buf.data[offset], rc);
-	return name_size_alg;
-}
-EXPORT_SYMBOL_GPL(tpm2_read_public);
-#endif /* CONFIG_TCG_TPM2_HMAC */
-
 /**
  * tpm_buf_append_name() - Append a handle and store TPM name
  * @chip:		TPM chip to use.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 319ba75dd79a..704fceb46a40 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -478,6 +478,7 @@ extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
 extern struct tpm_chip *tpm_default_chip(void);
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
 int tpm2_find_hash_alg(unsigned int crypto_id);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 
 static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
 {
@@ -544,7 +545,6 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -568,13 +568,6 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
-
-static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
-				   void *name)
-{
-	memcpy(name, &handle, sizeof(u32));
-	return sizeof(u32);
-}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..70cca3b7c3d4 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
 ---
 --- ASN.1 for TPM 2.0 keys
 ---
+TPMPolicy ::= SEQUENCE {
+	commandCode	[0] EXPLICIT INTEGER,
+	commandPolicy	[1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+	name		[0] EXPLICIT UTF8String OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy
+}
 
 TPMKey ::= SEQUENCE {
 	type		OBJECT IDENTIFIER ({tpm2_key_type}),
 	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	authPolicy	[3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+	description	[4] EXPLICIT UTF8String OPTIONAL,
+	rsaParent	[5] EXPLICIT BOOLEAN OPTIONAL,
+	parentName	[6] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
 	parent		INTEGER ({tpm2_key_parent}),
 	pubkey		OCTET STRING ({tpm2_key_pub}),
 	privkey		OCTET STRING ({tpm2_key_priv})
-	}
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..07f04cc010bc 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
 
 static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
 
+enum tpm_key_tag {
+	TPM_KEY_TAG_EMPTY_AUTH	= 0,
+	TPM_KEY_TAG_POLICY	= 1,
+	TPM_KEY_TAG_SECRET	= 2,
+	TPM_KEY_TAG_AUTH_POLICY	= 3,
+	TPM_KEY_TAG_DESCRIPTION = 4,
+	TPM_KEY_TAG_RSA_PARENT	= 5,
+	TPM_KEY_TAG_PARENT_NAME	= 6,
+};
+
 static int tpm2_key_encode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 *src, u32 len)
+			   u8 *src, u32 len, u8 *parent_name,
+			   u16 parent_name_size)
 {
 	const int SCRATCH_SIZE = PAGE_SIZE;
-	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
-	u8 *work = scratch, *work1;
-	u8 *end_work = scratch + SCRATCH_SIZE;
-	u8 *priv, *pub;
+	u8 *end_work, *name_end;
 	u16 priv_len, pub_len;
+	u8 *work, *work1;
+	u8 *priv, *pub;
 	int ret;
 
 	priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	pub_len = get_unaligned_be16(src) + 2;
 	pub = src;
 
+	u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
 	if (!scratch)
 		return -ENOMEM;
 
+	work = scratch;
+	end_work = scratch + SCRATCH_SIZE;
+
 	work = asn1_encode_oid(work, end_work, tpm2key_oid,
 			       asn1_oid_len(tpm2key_oid));
 
 	if (options->blobauth_len == 0) {
-		unsigned char bool[3], *w = bool;
-		/* tag 0 is emptyAuth */
-		w = asn1_encode_boolean(w, w + sizeof(bool), true);
-		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
-			ret = PTR_ERR(w);
-			goto err;
+		u8 *bool_end;
+		u8 bool[3];
+
+		bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+					       true);
+		if (IS_ERR(bool_end)) {
+			pr_err("BUG: Boolean failed to encode\n");
+			return PTR_ERR(bool_end);
 		}
-		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+		work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+				       bool, bool_end - bool);
 	}
 
+	u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	if (!name_encoded)
+		return -ENOMEM;
+
+	name_end = asn1_encode_octet_string(&name_encoded[0],
+					    &name_encoded[SCRATCH_SIZE],
+					    parent_name, parent_name_size);
+	work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+			       name_encoded, name_end - name_encoded);
+
 	/*
 	 * Assume both octet strings will encode to a 2 byte definite length
 	 *
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	 */
 	if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
 		 "BUG: scratch buffer is too small")) {
-		ret = -EINVAL;
-		goto err;
+		return -EINVAL;
 	}
 
 	work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	if (IS_ERR(work1)) {
 		ret = PTR_ERR(work1);
 		pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
-		goto err;
+		return ret;
 	}
 
-	kfree(scratch);
 	return work1 - payload->blob;
-
-err:
-	kfree(scratch);
-	return ret;
 }
 
 struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
 	u32 pub_len;
 	const u8 *priv;
 	u32 priv_len;
+	const u8 *name;
+	u32 name_len;
 };
 
 static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 **buf)
+			   u8 **buf, u8 *parent_name, u16 *parent_name_size)
 {
 	int ret;
 	struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 	blob += ctx.priv_len;
 
 	memcpy(blob, ctx.pub, ctx.pub_len);
+	memcpy(parent_name, ctx.name, ctx.name_len);
+	*parent_name_size = ctx.name_len;
 
 	return 0;
 }
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
 	return 0;
 }
 
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->name = value;
+	ctx->name_len = vlen;
+
+	return 0;
+}
 /**
  * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		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, parent_name, parent_name_size);
 	if (blob_len < 0)
 		rc = blob_len;
 
@@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
 	 * blob is in the legacy format if decoding does not end successfully.
 	 */
-	rc = tpm2_key_decode(payload, options, &blob);
+	rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
 	if (rc) {
 		blob = payload->blob;
 		payload->old_format = 1;
@@ -617,11 +654,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
-	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
-	if (rc < 0)
-		goto out;
+	if (!parent_name_size) {
+		rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+		if (rc < 0)
+			goto out;
 
-	parent_name_size = rc;
+		parent_name_size = rc;
+	}
 
 	rc = tpm2_load_cmd(chip, payload, options, parent_name,
 			   parent_name_size, blob, &blob_handle);
-- 
2.39.5


^ permalink raw reply related

* [PATCH v5 1/2] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-06 16:13 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251206161345.2038730-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v5:
- Rebased to the mainline.
v4:
- No changes
v3:
- No changes.
v2:
- No changes.
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	}
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc) {
 			tpm_buf_destroy(&buf);
 			return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -219,14 +229,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf.data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -243,15 +255,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -276,36 +287,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -613,22 +603,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -689,23 +665,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -544,6 +544,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -567,6 +568,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
 	struct tpm_buf buf, sized;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -251,6 +253,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out_put;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -355,21 +364,25 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -465,20 +465,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -573,30 +577,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.39.5


^ permalink raw reply related

* [PATCH v5 0/2] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-06 16:13 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list

The main goal is fairly straight-forwrd here.

The aim of these patches is optimize the number of tpm2_read_public()
calls to the bare minimum. The last patch is a bit out of scope but
I included because it builds on top of this work.

Jarkko Sakkinen (2):
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  KEYS: trusted: Store parent's name to the encoded keys

 drivers/char/tpm/tpm2-cmd.c               |  93 +++++++++-
 drivers/char/tpm/tpm2-sessions.c          | 163 ++---------------
 include/linux/tpm.h                       |   3 +-
 security/keys/trusted-keys/tpm2key.asn1   |  17 +-
 security/keys/trusted-keys/trusted_tpm2.c | 207 +++++++++++++++-------
 5 files changed, 266 insertions(+), 217 deletions(-)

-- 
2.39.5


^ permalink raw reply

* [PATCH 3/3] tpm-buf: Implement managed allocations
From: Jarkko Sakkinen @ 2025-12-06 11:31 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Stefan Berger, Peter Huewe, Jarkko Sakkinen,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251206113110.1793407-1-jarkko@kernel.org>

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.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
 drivers/char/tpm/tpm-buf.c                | 124 +++++----
 drivers/char/tpm/tpm-sysfs.c              |  21 +-
 drivers/char/tpm/tpm.h                    |   1 -
 drivers/char/tpm/tpm1-cmd.c               | 166 +++++------
 drivers/char/tpm/tpm2-cmd.c               | 320 ++++++++++------------
 drivers/char/tpm/tpm2-sessions.c          | 142 +++++-----
 drivers/char/tpm/tpm2-space.c             |  44 ++-
 drivers/char/tpm/tpm_vtpm_proxy.c         |  30 +-
 include/linux/tpm.h                       |  20 +-
 security/keys/trusted-keys/trusted_tpm1.c |  36 +--
 security/keys/trusted-keys/trusted_tpm2.c | 170 ++++++------
 11 files changed, 508 insertions(+), 566 deletions(-)

diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 1b9dee0d0681..73be8a87b472 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -7,82 +7,109 @@
 #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_INVALID;
+		}
+	} 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_INVALID;
+		}
+	}
 }
-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_INVALID)
+		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_INVALID)
+		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,8 +117,11 @@ 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)
 {
+	if (buf->flags & TPM_BUF_INVALID)
+		return 0;
+
 	return buf->length;
 }
 EXPORT_SYMBOL_GPL(tpm_buf_length);
@@ -104,10 +134,12 @@ 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;
+
 	if (buf->flags & TPM_BUF_INVALID)
 		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_INVALID;
 		return;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 94231f052ea7..f5dcadb1ab3c 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -32,28 +32,29 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!tpm_buf)
+		return -ENOMEM;
+
 	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))
-		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,
-			     "attempting to read the PUBEK"))
-		goto out_buf;
+	if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, "TPM_ReadPubek"))
+		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 +72,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 02c07fef41ba..5395927c62fc 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -32,7 +32,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..11090053ef54 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -323,20 +323,14 @@ 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;
-
-	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);
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
-	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_STARTUP);
+	tpm_buf_append_u16(buf, TPM_ST_CLEAR);
+	return tpm_transmit_cmd(chip, buf, 0, "TPM_Startup");
 }
 
 int tpm1_get_timeouts(struct tpm_chip *chip)
@@ -463,50 +457,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);
@@ -531,81 +522,69 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
 	struct tpm1_get_random_out *out;
 	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;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
+	tpm_buf_init(buf, TPM_BUFSIZE);
 	do {
-		tpm_buf_append_u32(&buf, num_bytes);
+		tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
+		tpm_buf_append_u32(buf, num_bytes);
 
-		rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
-				      "attempting get random");
+		rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len), "TPM_GetRandom");
 		if (rc) {
 			if (rc > 0)
 				rc = -EIO;
-			goto out;
+			return rc;
 		}
 
-		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) {
-			rc = -EFAULT;
-			goto out;
-		}
+		if (recd > num_bytes)
+			return -EFAULT;
+
+		if (buf->length < TPM_HEADER_SIZE +
+				  sizeof(out->rng_data_len) + recd)
+			return -EFAULT;
 
-		if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
-					   sizeof(out->rng_data_len) + recd) {
-			rc = -EFAULT;
-			goto out;
-		}
 		memcpy(dest, out->rng_data, recd);
 
 		dest += recd;
 		total += recd;
 		num_bytes -= recd;
-
-		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,
-			      "attempting to read a pcr value");
+	rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, "TPM_PCRRead");
 	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 (buf->length < 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 +598,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;
-
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
-	if (rc)
-		return rc;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
-	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, "TPM_ContinueSelfTest");
 }
 
 /**
@@ -742,22 +718,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;
 	int rc;
 
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
 	/* 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");
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
-	if (rc)
-		return rc;
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+
 	/* 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 +750,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 +760,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 82076b6cfcf7..2682f5ec09cb 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -119,12 +119,15 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	if (pcr_idx >= TPM2_PLATFORM_PCR)
 		return -EINVAL;
 
@@ -139,36 +142,31 @@ 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;
+	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, "TPM2_PCR_Read");
 	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 rc;
 
 	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 +182,52 @@ 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	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) {
-		if (!disable_pcr_integrity)
-			tpm2_end_auth_session(chip);
-		return rc;
-	}
+	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(&buf, pcr_idx);
-		tpm_buf_append_auth(chip, &buf, NULL, 0);
+		tpm_buf_append_handle(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 (buf->flags & TPM_BUF_INVALID)
+		return -EINVAL;
 
 	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");
-	if (!disable_pcr_integrity)
-		rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_PCR_Extend");
 
-	tpm_buf_destroy(&buf);
+	if (!disable_pcr_integrity)
+		rc = tpm_buf_check_hmac_response(chip, buf, rc);
 
 	return rc;
 }
@@ -258,7 +252,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;
@@ -270,61 +263,58 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 	if (!num_bytes || max > TPM_MAX_RNG_DATA)
 		return -EINVAL;
 
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	err = tpm2_start_auth_session(chip);
 	if (err)
 		return err;
 
-	err = tpm_buf_init(&buf, 0, 0);
-	if (err) {
-		tpm2_end_auth_session(chip);
-		return err;
-	}
-
+	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);
-		if (err) {
-			tpm_buf_destroy(&buf);
+		tpm_buf_append_u16(buf, num_bytes);
+
+		err = tpm_buf_fill_hmac_session(chip, buf);
+		if (err)
 			return err;
-		}
 
-		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);
+				       "TPM2_GetRandom");
+		err = tpm_buf_check_hmac_response(chip, buf, err);
 		if (err) {
 			if (err > 0)
 				err = -EIO;
-			goto out;
+			return err;
 		}
 
-		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) {
-			err = -EFAULT;
-			goto out;
+			return -EFAULT;
 		}
 		memcpy(dest_ptr, out->buffer, recd);
 
@@ -333,13 +323,7 @@ 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;
 }
 
 /**
@@ -349,20 +333,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, "TPM2_FlushContext");
 }
 EXPORT_SYMBOL_GPL(tpm2_flush_context);
 
@@ -389,19 +371,20 @@ 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];
+		out = (struct tpm2_get_cap_out *)&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
@@ -413,7 +396,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);
@@ -430,15 +412,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, "TPM2_Shutdown");
 }
 
 /**
@@ -456,20 +437,19 @@ 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;
-
-		tpm_buf_append_u8(&buf, full);
-		rc = tpm_transmit_cmd(chip, &buf, 0,
-				      "attempting the self test");
-		tpm_buf_destroy(&buf);
+	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_SELF_TEST);
+	for (full = 0; full < 2; full++) {
+		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, "TPM2_SelfTest");
 		if (rc == TPM2_RC_TESTING)
 			rc = TPM2_RC_SUCCESS;
 		if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
@@ -494,23 +474,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);
@@ -550,7 +531,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;
@@ -562,39 +542,37 @@ 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, "TPM2_GetCapability(PCRS)");
 	if (rc)
-		goto out;
+		return rc;
 
-	nr_possible_banks = be32_to_cpup(
-		(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
+	nr_possible_banks = be32_to_cpup((__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);
-		rc = -ENOMEM;
-		goto out;
+		return -ENOMEM;
 	}
 
-	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 +
 			offsetof(struct tpm2_pcr_selection, size_of_select);
-		if (pcr_select_offset >= end) {
-			rc = -EFAULT;
-			break;
-		}
+		if (pcr_select_offset >= end)
+			return -EFAULT;
 
 		memcpy(&pcr_selection, marker, sizeof(pcr_selection));
 		hash_alg = be16_to_cpu(pcr_selection.hash_alg);
@@ -606,7 +584,7 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 
 			rc = tpm2_init_bank_info(chip, nr_alloc_banks);
 			if (rc < 0)
-				break;
+				return rc;
 
 			nr_alloc_banks++;
 		}
@@ -618,21 +596,21 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 	}
 
 	chip->nr_allocated_banks = nr_alloc_banks;
-out:
-	tpm_buf_destroy(&buf);
-
-	return rc;
+	return 0;
 }
 
 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) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
 	if (rc)
 		goto out;
@@ -649,30 +627,24 @@ 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)
-		goto out;
+	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);
 
-	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])) {
+	if (nr_commands != 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;
@@ -684,8 +656,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 		}
 	}
 
-	tpm_buf_destroy(&buf);
-
 out:
 	if (rc > 0)
 		rc = -ENODEV;
@@ -706,20 +676,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
 
 static int tpm2_startup(struct tpm_chip *chip)
 {
-	struct tpm_buf buf;
-	int rc;
-
-	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);
-
-	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, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
+	tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
+	return tpm_transmit_cmd(chip, buf, 0, "TPM2_Startup");
 }
 
 /**
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 92d116d73c6f..79f27a46bd7f 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -168,7 +168,6 @@ 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;
 	int rc, name_size_alg;
-	struct tpm_buf buf;
 
 	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
 	    mso != TPM2_MSO_NVRAM) {
@@ -176,47 +175,41 @@ 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;
+	struct tpm_buf *buf __free(kfree) = 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]);
+	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);
+	memcpy(name, &buf->data[offset], rc);
 	return name_size_alg;
 }
 #endif /* CONFIG_TCG_TPM2_HMAC */
@@ -982,7 +975,6 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
 int tpm2_start_auth_session(struct tpm_chip *chip)
 {
 	struct tpm2_auth *auth;
-	struct tpm_buf buf;
 	u32 null_key;
 	int rc;
 
@@ -991,6 +983,10 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
 		return 0;
 	}
 
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	auth = kzalloc(sizeof(*auth), GFP_KERNEL);
 	if (!auth)
 		return -ENOMEM;
@@ -1001,41 +997,37 @@ 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)
-		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, "TPM2_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;
@@ -1257,18 +1249,18 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
 			       u32 *handle, u8 *name)
 {
 	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;
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
 
-	rc = tpm_buf_init_sized(&template);
-	if (rc) {
-		tpm_buf_destroy(&buf);
-		return rc;
-	}
+	struct tpm_buf *template __free(kfree) = 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
@@ -1280,75 +1272,71 @@ 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,
-			      "attempting to create NULL primary");
+	rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_CreatePrimary");
 
 	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 0818bb517805..682dfc93845d 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 1e0585e45371..42e2a091f43d 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -25,7 +25,8 @@
 #include <crypto/hash_info.h>
 #include <crypto/aes.h>
 
-#define TPM_DIGEST_SIZE 20	/* Max TPM v1.2 PCR size */
+#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
@@ -376,13 +377,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;
 	u8 handles;
+	u16 length;
+	u16 capacity;
+	u8 data[];
 };
 
 enum tpm2_object_attributes {
@@ -413,12 +416,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 636acb66a4f6..759c1ecb0435 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -310,9 +310,10 @@ 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(void *cmd, size_t cmd_len)
 {
-	struct tpm_buf buf;
+	u8 buf_data[512];
+	struct tpm_buf *buf = (struct tpm_buf *)buf_data;
 	int rc;
 
 	if (!chip)
@@ -322,11 +323,10 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
 	if (rc)
 		return rc;
 
-	buf.flags = 0;
-	buf.length = buflen;
-	buf.data = cmd;
+	tpm_buf_init(buf, sizeof(buf_data));
+	tpm_buf_append(buf, cmd, cmd_len);
 	dump_tpm_buf(cmd);
-	rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
+	rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
 	dump_tpm_buf(cmd);
 
 	if (rc > 0)
@@ -624,23 +624,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;
 }
 
@@ -650,14 +650,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);
@@ -665,7 +666,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 fdba641fefdb..fb76c4ea496f 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -234,7 +234,6 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_options *options)
 {
 	off_t offset = TPM_HEADER_SIZE;
-	struct tpm_buf buf, sized;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -255,97 +254,99 @@ 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) {
+	struct tpm_buf *buf __free(kfree) = 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);
-		tpm2_end_auth_session(chip);
-		goto out_put;
-	}
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
 	if (rc)
-		goto out;
+		goto out_put;
 
-	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);
 
+	struct tpm_buf *sized __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!sized) {
+		rc = -ENOMEM;
+		tpm2_end_auth_session(chip);
+		goto out_put;
+	}
+
 	/* sensitive */
-	tpm_buf_append_u16(&sized, options->blobauth_len);
+	tpm_buf_init_sized(sized, TPM_BUFSIZE);
+	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_init_sized(sized, TPM_BUFSIZE);
+	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_INVALID) {
+	if (buf->flags & TPM_BUF_INVALID) {
 		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_INVALID) {
+	blob_len = tpm_buf_read_u32(buf, &offset);
+	if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_INVALID) {
 		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 +374,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
-	struct tpm_buf buf;
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
@@ -427,39 +427,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) {
+	struct tpm_buf *buf __free(kfree) = 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_INVALID) {
-		rc = -E2BIG;
+	if (buf->flags & TPM_BUF_INVALID) {
 		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,28 +481,28 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
-	struct tpm_buf buf;
 	u16 data_len;
 	int offset;
 	u8 *data;
 	int rc;
 
+	struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
-	if (rc) {
-		tpm2_end_auth_session(chip);
-		return rc;
-	}
+	tpm_buf_init(buf, TPM_BUFSIZE);
+	tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, buf, options->keyhandle, 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 +517,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 +563,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		}
 	}
 
-out:
-	tpm_buf_destroy(&buf);
 	return tpm_ret_to_err(rc);
 }
 
-- 
2.39.5


^ permalink raw reply related

* [PATCH 2/3] tpm-buf: Remove chip parameter from tpm_buf_append_handle
From: Jarkko Sakkinen @ 2025-12-06 11:31 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Jonathan McDowell, Peter Huewe, Jarkko Sakkinen,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251206113110.1793407-1-jarkko@kernel.org>

From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>

Remove @chip from tpm_buf_append_handle() in order to decouple it from
driver. This allows tpm-buf.c to be compiled as part of early boot code,
thus providing a mechanism to build and parse TPM data.

E.g., Trenchboot can use this to manage TPM2_PcrExtend protocol data as
part of its early boot blob simply by compiling and linking the object
file.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
 drivers/char/tpm/tpm-buf.c       | 5 ++---
 drivers/char/tpm/tpm2-cmd.c      | 2 +-
 drivers/char/tpm/tpm2-sessions.c | 2 +-
 include/linux/tpm.h              | 2 +-
 4 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 69ee77400539..1b9dee0d0681 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -147,20 +147,19 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
 
 /**
  * tpm_buf_append_handle() - Add a handle
- * @chip:	&tpm_chip instance
  * @buf:	&tpm_buf instance
  * @handle:	a TPM object handle
  *
  * Add a handle to the buffer, and increase the count tracking the number of
  * handles in the command buffer. Works only for command buffers.
  */
-void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
+void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle)
 {
 	if (buf->flags & TPM_BUF_INVALID)
 		return;
 
 	if (buf->flags & TPM_BUF_TPM2B) {
-		dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+		WARN(1, "tpm-buf: invalid type: TPM2B\n");
 		buf->flags |= TPM_BUF_INVALID;
 		return;
 	}
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..82076b6cfcf7 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -209,7 +209,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 		}
 		tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
 	} else {
-		tpm_buf_append_handle(chip, &buf, pcr_idx);
+		tpm_buf_append_handle(&buf, pcr_idx);
 		tpm_buf_append_auth(chip, &buf, NULL, 0);
 	}
 
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..92d116d73c6f 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -257,7 +257,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 #endif
 
 	if (!tpm2_chip_auth(chip)) {
-		tpm_buf_append_handle(chip, buf, handle);
+		tpm_buf_append_handle(buf, handle);
 		return 0;
 	}
 
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index aed34f119e8c..1e0585e45371 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -426,7 +426,7 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value);
 u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset);
 u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset);
 u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset);
-void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle);
+void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle);
 
 /*
  * Check if TPM device is in the firmware upgrade mode.
-- 
2.39.5


^ permalink raw reply related

* [PATCH 1/3] tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
From: Jarkko Sakkinen @ 2025-12-06 11:31 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Jonathan McDowell, Peter Huewe, Jarkko Sakkinen,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251206113110.1793407-1-jarkko@kernel.org>

From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>

Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW into TPM_BUF_INVALID,
given that they are identical. The only difference are the log messages.

In addition, add a missing TPM_BUF_INVALID check to tpm_buf_append_handle()
following the pattern from other functions in tpm-buf.c.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
 drivers/char/tpm/tpm-buf.c                | 14 ++++++++------
 include/linux/tpm.h                       |  8 +++-----
 security/keys/trusted-keys/trusted_tpm2.c |  6 +++---
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index dc882fc9fa9e..69ee77400539 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -104,13 +104,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
  */
 void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
 {
-	/* Return silently if overflow has already happened. */
-	if (buf->flags & TPM_BUF_OVERFLOW)
+	if (buf->flags & TPM_BUF_INVALID)
 		return;
 
 	if ((buf->length + new_length) > PAGE_SIZE) {
 		WARN(1, "tpm_buf: write overflow\n");
-		buf->flags |= TPM_BUF_OVERFLOW;
+		buf->flags |= TPM_BUF_INVALID;
 		return;
 	}
 
@@ -157,8 +156,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
  */
 void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
 {
+	if (buf->flags & TPM_BUF_INVALID)
+		return;
+
 	if (buf->flags & TPM_BUF_TPM2B) {
 		dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+		buf->flags |= TPM_BUF_INVALID;
 		return;
 	}
 
@@ -177,14 +180,13 @@ static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void
 {
 	off_t next_offset;
 
-	/* Return silently if overflow has already happened. */
-	if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+	if (buf->flags & TPM_BUF_INVALID)
 		return;
 
 	next_offset = *offset + count;
 	if (next_offset > buf->length) {
 		WARN(1, "tpm_buf: read out of boundary\n");
-		buf->flags |= TPM_BUF_BOUNDARY_ERROR;
+		buf->flags |= TPM_BUF_INVALID;
 		return;
 	}
 
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..aed34f119e8c 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -369,12 +369,10 @@ struct tpm_header {
 } __packed;
 
 enum tpm_buf_flags {
-	/* the capacity exceeded: */
-	TPM_BUF_OVERFLOW	= BIT(0),
 	/* TPM2B format: */
-	TPM_BUF_TPM2B		= BIT(1),
-	/* read out of boundary: */
-	TPM_BUF_BOUNDARY_ERROR	= BIT(2),
+	TPM_BUF_TPM2B		= BIT(0),
+	/* The buffer is in invalid and unusable state: */
+	TPM_BUF_INVALID		= BIT(1),
 };
 
 /*
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..fdba641fefdb 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -313,7 +313,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	/* creation PCR */
 	tpm_buf_append_u32(&buf, 0);
 
-	if (buf.flags & TPM_BUF_OVERFLOW) {
+	if (buf.flags & TPM_BUF_INVALID) {
 		rc = -E2BIG;
 		tpm2_end_auth_session(chip);
 		goto out;
@@ -329,7 +329,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out;
 
 	blob_len = tpm_buf_read_u32(&buf, &offset);
-	if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+	if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_INVALID) {
 		rc = -E2BIG;
 		goto out;
 	}
@@ -442,7 +442,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 
 	tpm_buf_append(&buf, blob, blob_len);
 
-	if (buf.flags & TPM_BUF_OVERFLOW) {
+	if (buf.flags & TPM_BUF_INVALID) {
 		rc = -E2BIG;
 		tpm2_end_auth_session(chip);
 		goto out;
-- 
2.39.5


^ permalink raw reply related

* [PATCH 0/3] Implement managed allocations
From: Jarkko Sakkinen @ 2025-12-06 11:31 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM, open list

Jarkko Sakkinen (3):
  tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
  tpm-buf: Remove chip parameter from tpm_buf_append_handle
  tpm-buf: Implement managed allocations

 drivers/char/tpm/tpm-buf.c                | 143 ++++++----
 drivers/char/tpm/tpm-sysfs.c              |  21 +-
 drivers/char/tpm/tpm.h                    |   1 -
 drivers/char/tpm/tpm1-cmd.c               | 166 +++++------
 drivers/char/tpm/tpm2-cmd.c               | 320 ++++++++++------------
 drivers/char/tpm/tpm2-sessions.c          | 144 +++++-----
 drivers/char/tpm/tpm2-space.c             |  44 ++-
 drivers/char/tpm/tpm_vtpm_proxy.c         |  30 +-
 include/linux/tpm.h                       |  30 +-
 security/keys/trusted-keys/trusted_tpm1.c |  36 +--
 security/keys/trusted-keys/trusted_tpm2.c | 170 ++++++------
 11 files changed, 523 insertions(+), 582 deletions(-)

-- 
2.39.5


^ permalink raw reply

* Re: [GIT PULL] TPM DEVICE DRIVER: tpmdd-sessions-next-6.19-rc1
From: pr-tracker-bot @ 2025-12-06  5:42 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Linus Torvalds, Peter Huewe, Jason Gunthorpe, David Howells,
	keyrings, linux-integrity, linux-kernel
In-Reply-To: <aTJy3xx3DEFS5Gr-@kernel.org>

The pull request you sent on Fri, 5 Dec 2025 07:51:27 +0200:

> git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git tags/tpmdd-sessions-next-6.19-rc1

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/001eefb503901603de48b8dcaf06155036ed7452

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html

^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: steven chen @ 2025-12-06  1:28 UTC (permalink / raw)
  To: Roberto Sassu, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring, steven chen
In-Reply-To: <099492ee58996b6f18d73232677757ecadb14cb7.camel@huaweicloud.com>

On 12/5/2025 1:30 AM, Roberto Sassu wrote:
> On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
>> This patch is for trimming N entries of the IMA event logs as well as
>> cleaning the hash table.
>>
>> It provides a userspace interface ima_trim_log that can be used to input
>> number N to let kernel to trim N entries of IMA event logs. When read
>> this interface, it returns number of entries trimmed last tim.
> High-level comments:
> - It does not offer the possibility to keep the hash table

Hi Roberto,

Should the hash table persist after log removal? Log trimming is 
infrequent, right?

Thanks,

Steven

> - There is no coordination between taking a snapshot and the readers of
>    the measurements list (I think it is necessary, since reading is
>    based on *pos, which contains the entries read until a given point;
>    if there is a truncate in the middle of the read, *pos would still
>    refer to the non-truncated list and the next read will skip some
>    measurement entries)

Hi Roberto,

What’s your suggestion for this? Leave user space handle this (check log 
trimming
happened)?

Thanks!

Steven.

> - While trimming per se is ok, I like more the idea of staging changes
>    and letting the user delete the staged measurements list later

Hi Roberto,

This approach simplifies both the user space agent and the kernel. The 
main difference
lies in whether log purging requires one step or two; I think a single 
step is preferable
since logs must be removed from the kernel regardless.

Thanks,

Steven

>> A mutex ima_trim_list_mutex is provided to allow one trimming request
>> at a time.
>>
>> Signed-off-by: steven chen <chenste@linux.microsoft.com>
>> ---
>>   security/integrity/ima/ima.h       |  2 +
>>   security/integrity/ima/ima_fs.c    | 78 ++++++++++++++++++++++++++++++
>>   security/integrity/ima/ima_queue.c | 42 ++++++++++++++++
>>   3 files changed, 122 insertions(+)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index e3d71d8d56e3..ab0e30ee25ea 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>>   
>>   #ifdef CONFIG_IMA_KEXEC
>>   void ima_measure_kexec_event(const char *event_name);
>> +long ima_purge_event_log(long number_logs);
>>   #else
>>   static inline void ima_measure_kexec_event(const char *event_name) {}
>> +static inline long ima_purge_event_log(long number_logs) { return 0; }
>>   #endif
>>   
>>   /*
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 87045b09f120..ea93448feedd 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -38,6 +38,11 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>>   
>>   static int valid_policy = 1;
>>   
>> +#define IMA_LOG_TRIM_REQ_LENGTH 11
>> +static long trimcount;
>> +/* mutex protects atomicity of trimming measurement list requests */
>> +static DEFINE_MUTEX(ima_trim_list_mutex);
>> +
>>   static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>>   				     loff_t *ppos, atomic_long_t *val)
>>   {
>> @@ -289,6 +294,69 @@ static const struct file_operations ima_ascii_measurements_ops = {
>>   	.release = seq_release,
>>   };
>>   
>> +static int ima_log_trim_open(struct inode *inode, struct file *filp)
>> +{
>> +	if (!capable(CAP_SYS_ADMIN))
>> +		return -EPERM;
>> +	return 0;
>> +}
>> +
>> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
>> +{
>> +	char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH];	/* greater than largest 'long' string value */
>> +	ssize_t len;
>> +
>> +	len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
>> +	return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
>> +}
>> +
>> +static ssize_t ima_log_trim_write(struct file *file,
>> +				  const char __user *buf, size_t datalen, loff_t *ppos)
>> +{
>> +	unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
>> +	long count, n;
>> +	int ret;
>> +
>> +	mutex_lock(&ima_trim_list_mutex);
>> +
>> +	if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	n = (int)datalen;
>> +
>> +	ret = copy_from_user(req, buf, datalen);
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +	count = 0;
>> +	for (int i = 0; i < n; ++i) {
>> +		if (req[i] < '0' || req[i] > '9') {
>> +			ret = -EINVAL;
>> +			goto out;
>> +		}
>> +		count = count * 10 + req[i] - '0';
>> +	}
>> +	ret = ima_purge_event_log(count);
>> +
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +	trimcount = ret;
>> +	ret = datalen;
>> +out:
>> +	mutex_unlock(&ima_trim_list_mutex);
>> +	return ret;
>> +}
>> +
>> +static const struct file_operations ima_log_trim_ops = {
>> +	.open = ima_log_trim_open,
>> +	.read = ima_log_trim_read,
>> +	.write = ima_log_trim_write,
>> +	.llseek = generic_file_llseek,
>> +};
>> +
>>   static ssize_t ima_read_policy(char *path)
>>   {
>>   	void *data = NULL;
>> @@ -528,6 +596,16 @@ int __init ima_fs_init(void)
>>   		goto out;
>>   	}
>>   
>> +	dentry = securityfs_create_file("ima_trim_log",
>> +					S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
>> +					ima_dir, NULL, &ima_log_trim_ops);
>> +	if (IS_ERR(dentry)) {
>> +		ret = PTR_ERR(dentry);
>> +		goto out;
>> +	}
>> +
>> +	trimcount = 0;
>> +
>>   	dentry = securityfs_create_file("runtime_measurements_count",
>>   				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>>   				   &ima_measurements_count_ops);
>> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
>> index 590637e81ad1..999cd42c517c 100644
>> --- a/security/integrity/ima/ima_queue.c
>> +++ b/security/integrity/ima/ima_queue.c
>> @@ -220,6 +220,48 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
>>   	return result;
>>   }
>>   
>> +/* Delete the IMA event logs */
>> +long ima_purge_event_log(long number_logs)
>> +{
>> +	struct ima_queue_entry *qe;
>> +	long cur = 0;
>> +
>> +	if (number_logs <= 0)
>> +		return number_logs;
>> +
>> +	mutex_lock(&ima_extend_list_mutex);
>> +	rcu_read_lock();
> Sorry, I'm missing why rcu_read_lock() is needed.

Hi Roberto,

I will add "true" parameter in list_for_each_entry_rcu api.

Thanks,

Steven

>> +
>> +	/*
>> +	 * Remove this entry from both hash table and the measurement list
>> +	 * When removing from hash table, decrease the length counter
>> +	 * so that the hash table re-sizing logic works correctly
>> +	 */
>> +	list_for_each_entry_rcu(qe, &ima_measurements, later) {
>> +		int i;
>> +
>> +		/* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
>> +		if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
>> +			hlist_del_rcu(&qe->hnext);
>> +
>> +		for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
>> +			kfree(qe->entry->template_data[i].data);
>> +			qe->entry->template_data[i].data = NULL;
>> +			qe->entry->template_data[i].len = 0;
>> +		}
>> +
>> +		atomic_long_dec(&ima_htable.len);
>> +		list_del_rcu(&qe->later);
> Missing kfree() of qe->entry and qe?
>
> Thanks
>
> Roberto

Hi Roberto,

I will update in next version.

Thanks,

Steven

>> +		++cur;
>> +		if (cur >= number_logs)
>> +			break;
>> +	}
>> +
>> +	rcu_read_unlock();
>> +	mutex_unlock(&ima_extend_list_mutex);
>> +	return cur;
>> +}
>> +
>>   int ima_restore_measurement_entry(struct ima_template_entry *entry)
>>   {
>>   	int result = 0;



^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: James Bottomley @ 2025-12-05 18:30 UTC (permalink / raw)
  To: Roberto Sassu, steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring
In-Reply-To: <099492ee58996b6f18d73232677757ecadb14cb7.camel@huaweicloud.com>

On Fri, 2025-12-05 at 10:30 +0100, Roberto Sassu wrote:
> On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
> > This patch is for trimming N entries of the IMA event logs as well
> > as cleaning the hash table.
> > 
> > It provides a userspace interface ima_trim_log that can be used to
> > input number N to let kernel to trim N entries of IMA event logs.
> > When read this interface, it returns number of entries trimmed last
> > tim.
> 
> High-level comments:
> - It does not offer the possibility to keep the hash table
> - There is no coordination between taking a snapshot and the readers
> of
>   the measurements list (I think it is necessary, since reading is
>   based on *pos, which contains the entries read until a given point;
>   if there is a truncate in the middle of the read, *pos would still
>   refer to the non-truncated list and the next read will skip some
>   measurement entries)

Rather than designing the interface absent use cases, could we give use
cases first so we know it fits?  I really only have one: the keylime
agent, but I believe it's pattern, which would be get TPM quote of
logging PCRs, certify quote and then trim the log up to the point the
quote was obtained will be a common pattern (at least I don't think
anyone would trim the log without quoting it).  If you're trimming
based on a quote, you know the end hash of all the PCRs and you want to
trim up to that.  Since the log is fixed, you can work out what the
index offset is, but it does seem a bit suboptimal to have to compute
that, which is why I think trimming by end PCR hash is useful.  The
main point in all of this is, I think, that you don't really get to
decide which point in the log the TPM will quote: it always quotes it's
current PCR values, so no-one who cares about log quotes can select the
number of entries first, they all have to see what PCR values the quote
returns.

> - While trimming per se is ok, I like more the idea of staging
> changes and letting the user delete the staged measurements list
> later

I'm not averse to this, but keylime won't care ... it gets the quote
and the log and it will verify the log before it will get the agent to
issue a trim.  Is there a use case that would actually need this
behaviour?

I also think having the base PCRs stored in the kernel after the trim
(in a way that's easily consumed, like a non measured log entry), thus
allowing verification from the log up to any current quote is useful. 
The reason I think it's useful is the attestation token one: I can see
the keylime verifier giving an I've verified the log up to here and all
the entries look good token that contains the base PCRs and if there
are quite a few of these floating around (especially if the log isn't
always trimmed) I could see the kernel base PCR storage being used to
ask which is the relevant attestation token I should use to verify the
rest of the log (or even used to detect nefarious trimming designed to
hide records).

Regards,

James


^ permalink raw reply

* [PATCH v4 4/4] tpm2-sessions: Enforce single authorized handle
From: Jarkko Sakkinen @ 2025-12-05 15:19 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list
In-Reply-To: <20251205151955.298182-1-jarkko@kernel.org>

Eliminate AUTH_MAX_NAMES and replace array of names with a buffer for
single TPM name, as this what call sites expect at worst.

Benefits are obvious i.e., removing dead code is usually a good idea
:-)

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
 drivers/char/tpm/tpm2-sessions.c | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index c8b44b1a6cb6..8bfe2875faf2 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
 #include <crypto/sha2.h>
 #include <crypto/utils.h>
 
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES	3
-
 #define AES_KEY_BYTES	AES_KEYSIZE_128
 #define AES_KEY_BITS	(AES_KEY_BYTES*8)
 
@@ -136,8 +133,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
-	u16 name_size_tbl[AUTH_MAX_NAMES];
+	u8 name[2 + SHA512_DIGEST_SIZE];
+	u16 name_size;
 };
 
 /**
@@ -182,7 +179,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 	slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
-	if (slot >= AUTH_MAX_NAMES) {
+	if (slot > 0) {
 		dev_err(&chip->dev, "too many handles\n");
 		ret = -EIO;
 		goto err;
@@ -195,8 +192,8 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-	memcpy(auth->name[slot], name, name_size);
-	auth->name_size_tbl[slot] = name_size;
+	memcpy(auth->name, name, name_size);
+	auth->name_size = name_size;
 #endif
 	return 0;
 
@@ -573,8 +570,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++)
-		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+	sha256_update(&sctx, auth->name, auth->name_size);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v4 3/4] KEYS: trusted: Open code tpm2_buf_append()
From: Jarkko Sakkinen @ 2025-12-05 15:19 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Jonathan McDowell, Peter Huewe, Jarkko Sakkinen,
	Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
	Paul Moore, James Morris, Serge E. Hallyn, open list,
	open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251205151955.298182-1-jarkko@kernel.org>

From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>

tpm2_buf_append_auth() has only single call site and most of its parameters
are redundant. Open code it to the call site. Remove illegit FIXME comment
as there is no categorized bug and replace it with more sane comment about
implementation (i.e. "non-opionated inline comment").

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@earth.li>
---
 security/keys/trusted-keys/trusted_tpm2.c | 51 ++++-------------------
 1 file changed, 9 insertions(+), 42 deletions(-)

diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 07f04cc010bc..3b5dbbc2d257 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -226,36 +226,6 @@ int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
 
 	return 0;
 }
-/**
- * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
- *
- * @buf: an allocated tpm_buf instance
- * @session_handle: session handle
- * @nonce: the session nonce, may be NULL if not used
- * @nonce_len: the session nonce length, may be 0 if not used
- * @attributes: the session attributes
- * @hmac: the session HMAC or password, may be NULL if not used
- * @hmac_len: the session HMAC or password length, maybe 0 if not used
- */
-static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
-				 const u8 *nonce, u16 nonce_len,
-				 u8 attributes,
-				 const u8 *hmac, u16 hmac_len)
-{
-	tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
-	tpm_buf_append_u32(buf, session_handle);
-	tpm_buf_append_u16(buf, nonce_len);
-
-	if (nonce && nonce_len)
-		tpm_buf_append(buf, nonce, nonce_len);
-
-	tpm_buf_append_u8(buf, attributes);
-	tpm_buf_append_u16(buf, hmac_len);
-
-	if (hmac && hmac_len)
-		tpm_buf_append(buf, hmac, hmac_len);
-}
-
 /**
  * tpm2_seal_trusted() - seal the payload of a trusted key
  *
@@ -549,19 +519,16 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 					    options->blobauth_len);
 	} else {
 		/*
-		 * FIXME: The policy session was generated outside the
-		 * kernel so we don't known the nonce and thus can't
-		 * calculate a HMAC on it.  Therefore, the user can
-		 * only really use TPM2_PolicyPassword and we must
-		 * send down the plain text password, which could be
-		 * intercepted.  We can still encrypt the returned
-		 * key, but that's small comfort since the interposer
-		 * could repeat our actions with the exfiltrated
-		 * password.
+		 * The policy session is generated outside the kernel, and thus
+		 * the password will end up being unencrypted on the bus, as
+		 * HMAC nonce cannot be calculated for it.
 		 */
-		tpm2_buf_append_auth(&buf, options->policyhandle,
-				     NULL /* nonce */, 0, 0,
-				     options->blobauth, options->blobauth_len);
+		tpm_buf_append_u32(&buf, 9 + options->blobauth_len);
+		tpm_buf_append_u32(&buf, options->policyhandle);
+		tpm_buf_append_u16(&buf, 0);
+		tpm_buf_append_u8(&buf, 0);
+		tpm_buf_append_u16(&buf, options->blobauth_len);
+		tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
 		if (tpm2_chip_auth(chip)) {
 			tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
 		} else  {
-- 
2.52.0


^ permalink raw reply related

* [PATCH v4 2/4] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05 15:19 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251205151955.298182-1-jarkko@kernel.org>

Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
attribute containing TPM name of the parent key (in other words, TPMT_HA
blob).

The life-cycle for trusted keys will now proceed as follows:

1. Encode parent's name to the 'paretName' during tpm2_key_encode().
2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
   the attribute is not available, fallback on doing tpm2_read_public().

In other words, in the common (i.e., not loading a legacy key blob),
tpm2_read_public() will now only happen at the time when a key is first
created.

In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
unconditional so that the binary format of the saved keys is not dependent
on kernel configuration.

[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Fixed three low-hanging fruit issues I found myself.
v2:
- A new patch.
---
 drivers/char/tpm/tpm2-cmd.c               | 90 ++++++++++++++++++++++
 drivers/char/tpm/tpm2-sessions.c          | 92 ----------------------
 include/linux/tpm.h                       |  9 +--
 security/keys/trusted-keys/tpm2key.asn1   | 17 ++++-
 security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++-------
 5 files changed, 173 insertions(+), 128 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 1f561ad3bdcf..360bc014d895 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -793,3 +793,93 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
 
 	return -1;
 }
+
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+	u16 hash_alg = get_unaligned_be16(name);
+
+	switch (hash_alg) {
+	case TPM_ALG_SHA1:
+		return SHA1_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA256:
+		return SHA256_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA384:
+		return SHA384_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA512:
+		return SHA512_DIGEST_SIZE + 2;
+	default:
+		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+		return -EINVAL;
+	}
+}
+
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+{
+	u32 mso = tpm2_handle_mso(handle);
+	off_t offset = TPM_HEADER_SIZE;
+	int rc, name_size_alg;
+	struct tpm_buf buf;
+
+	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+	    mso != TPM2_MSO_NVRAM) {
+		memcpy(name, &handle, sizeof(u32));
+		return sizeof(u32);
+	}
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+	if (rc)
+		return rc;
+
+	tpm_buf_append_u32(&buf, handle);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
+	if (rc) {
+		tpm_buf_destroy(&buf);
+		return tpm_ret_to_err(rc);
+	}
+
+	/* Skip TPMT_PUBLIC: */
+	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);
+		return -EIO;
+	}
+
+	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);
+		return -EIO;
+	}
+
+	if (offset + rc > tpm_buf_length(&buf)) {
+		tpm_buf_destroy(&buf);
+		return -EIO;
+	}
+
+	memcpy(name, &buf.data[offset], rc);
+	return name_size_alg;
+}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index e33be09446ff..c8b44b1a6cb6 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -140,98 +140,6 @@ struct tpm2_auth {
 	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
-#ifdef CONFIG_TCG_TPM2_HMAC
-/*
- * Name Size based on TPM algorithm (assumes no hash bigger than 255)
- */
-static int name_size(const u8 *name)
-{
-	u16 hash_alg = get_unaligned_be16(name);
-
-	switch (hash_alg) {
-	case TPM_ALG_SHA1:
-		return SHA1_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA256:
-		return SHA256_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA384:
-		return SHA384_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA512:
-		return SHA512_DIGEST_SIZE + 2;
-	default:
-		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
-		return -EINVAL;
-	}
-}
-
-/**
- * tpm2_read_public: Resolve TPM name for a handle
- * @chip:		TPM chip to use.
- * @handle:		TPM handle.
- * @name:		A buffer for returning the name blob. Must have a
- *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
- *
- * Returns size of TPM handle name of success.
- * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
- */
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
-{
-	u32 mso = tpm2_handle_mso(handle);
-	off_t offset = TPM_HEADER_SIZE;
-	int rc, name_size_alg;
-	struct tpm_buf buf;
-
-	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
-	    mso != TPM2_MSO_NVRAM) {
-		memcpy(name, &handle, sizeof(u32));
-		return sizeof(u32);
-	}
-
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
-	if (rc)
-		return rc;
-
-	tpm_buf_append_u32(&buf, handle);
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
-	if (rc) {
-		tpm_buf_destroy(&buf);
-		return tpm_ret_to_err(rc);
-	}
-
-	/* Skip TPMT_PUBLIC: */
-	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);
-		return -EIO;
-	}
-
-	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);
-		return -EIO;
-	}
-
-	if (offset + rc > tpm_buf_length(&buf)) {
-		tpm_buf_destroy(&buf);
-		return -EIO;
-	}
-
-	memcpy(name, &buf.data[offset], rc);
-	return name_size_alg;
-}
-EXPORT_SYMBOL_GPL(tpm2_read_public);
-#endif /* CONFIG_TCG_TPM2_HMAC */
-
 /**
  * tpm_buf_append_name() - Append a handle and store TPM name
  * @chip:		TPM chip to use.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 319ba75dd79a..704fceb46a40 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -478,6 +478,7 @@ extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
 extern struct tpm_chip *tpm_default_chip(void);
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
 int tpm2_find_hash_alg(unsigned int crypto_id);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 
 static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
 {
@@ -544,7 +545,6 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -568,13 +568,6 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
-
-static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
-				   void *name)
-{
-	memcpy(name, &handle, sizeof(u32));
-	return sizeof(u32);
-}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..70cca3b7c3d4 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
 ---
 --- ASN.1 for TPM 2.0 keys
 ---
+TPMPolicy ::= SEQUENCE {
+	commandCode	[0] EXPLICIT INTEGER,
+	commandPolicy	[1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+	name		[0] EXPLICIT UTF8String OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy
+}
 
 TPMKey ::= SEQUENCE {
 	type		OBJECT IDENTIFIER ({tpm2_key_type}),
 	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	authPolicy	[3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+	description	[4] EXPLICIT UTF8String OPTIONAL,
+	rsaParent	[5] EXPLICIT BOOLEAN OPTIONAL,
+	parentName	[6] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
 	parent		INTEGER ({tpm2_key_parent}),
 	pubkey		OCTET STRING ({tpm2_key_pub}),
 	privkey		OCTET STRING ({tpm2_key_priv})
-	}
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..07f04cc010bc 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
 
 static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
 
+enum tpm_key_tag {
+	TPM_KEY_TAG_EMPTY_AUTH	= 0,
+	TPM_KEY_TAG_POLICY	= 1,
+	TPM_KEY_TAG_SECRET	= 2,
+	TPM_KEY_TAG_AUTH_POLICY	= 3,
+	TPM_KEY_TAG_DESCRIPTION = 4,
+	TPM_KEY_TAG_RSA_PARENT	= 5,
+	TPM_KEY_TAG_PARENT_NAME	= 6,
+};
+
 static int tpm2_key_encode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 *src, u32 len)
+			   u8 *src, u32 len, u8 *parent_name,
+			   u16 parent_name_size)
 {
 	const int SCRATCH_SIZE = PAGE_SIZE;
-	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
-	u8 *work = scratch, *work1;
-	u8 *end_work = scratch + SCRATCH_SIZE;
-	u8 *priv, *pub;
+	u8 *end_work, *name_end;
 	u16 priv_len, pub_len;
+	u8 *work, *work1;
+	u8 *priv, *pub;
 	int ret;
 
 	priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	pub_len = get_unaligned_be16(src) + 2;
 	pub = src;
 
+	u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
 	if (!scratch)
 		return -ENOMEM;
 
+	work = scratch;
+	end_work = scratch + SCRATCH_SIZE;
+
 	work = asn1_encode_oid(work, end_work, tpm2key_oid,
 			       asn1_oid_len(tpm2key_oid));
 
 	if (options->blobauth_len == 0) {
-		unsigned char bool[3], *w = bool;
-		/* tag 0 is emptyAuth */
-		w = asn1_encode_boolean(w, w + sizeof(bool), true);
-		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
-			ret = PTR_ERR(w);
-			goto err;
+		u8 *bool_end;
+		u8 bool[3];
+
+		bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+					       true);
+		if (IS_ERR(bool_end)) {
+			pr_err("BUG: Boolean failed to encode\n");
+			return PTR_ERR(bool_end);
 		}
-		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+		work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+				       bool, bool_end - bool);
 	}
 
+	u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	if (!name_encoded)
+		return -ENOMEM;
+
+	name_end = asn1_encode_octet_string(&name_encoded[0],
+					    &name_encoded[SCRATCH_SIZE],
+					    parent_name, parent_name_size);
+	work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+			       name_encoded, name_end - name_encoded);
+
 	/*
 	 * Assume both octet strings will encode to a 2 byte definite length
 	 *
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	 */
 	if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
 		 "BUG: scratch buffer is too small")) {
-		ret = -EINVAL;
-		goto err;
+		return -EINVAL;
 	}
 
 	work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	if (IS_ERR(work1)) {
 		ret = PTR_ERR(work1);
 		pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
-		goto err;
+		return ret;
 	}
 
-	kfree(scratch);
 	return work1 - payload->blob;
-
-err:
-	kfree(scratch);
-	return ret;
 }
 
 struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
 	u32 pub_len;
 	const u8 *priv;
 	u32 priv_len;
+	const u8 *name;
+	u32 name_len;
 };
 
 static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 **buf)
+			   u8 **buf, u8 *parent_name, u16 *parent_name_size)
 {
 	int ret;
 	struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 	blob += ctx.priv_len;
 
 	memcpy(blob, ctx.pub, ctx.pub_len);
+	memcpy(parent_name, ctx.name, ctx.name_len);
+	*parent_name_size = ctx.name_len;
 
 	return 0;
 }
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
 	return 0;
 }
 
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->name = value;
+	ctx->name_len = vlen;
+
+	return 0;
+}
 /**
  * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		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, parent_name, parent_name_size);
 	if (blob_len < 0)
 		rc = blob_len;
 
@@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
 	 * blob is in the legacy format if decoding does not end successfully.
 	 */
-	rc = tpm2_key_decode(payload, options, &blob);
+	rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
 	if (rc) {
 		blob = payload->blob;
 		payload->old_format = 1;
@@ -617,11 +654,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
-	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
-	if (rc < 0)
-		goto out;
+	if (!parent_name_size) {
+		rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+		if (rc < 0)
+			goto out;
 
-	parent_name_size = rc;
+		parent_name_size = rc;
+	}
 
 	rc = tpm2_load_cmd(chip, payload, options, parent_name,
 			   parent_name_size, blob, &blob_handle);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v4 1/4] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05 15:19 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251205151955.298182-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- No changes.
v2:
- No changes.
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	}
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc) {
 			tpm_buf_destroy(&buf);
 			return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -219,14 +229,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf.data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -243,15 +255,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -276,36 +287,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -613,22 +603,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -689,23 +665,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -544,6 +544,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -567,6 +568,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
 	struct tpm_buf buf, sized;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -251,6 +253,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out_put;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -355,21 +364,25 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -465,20 +465,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -573,30 +577,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.52.0


^ permalink raw reply related

* [PATCH v4 0/4] Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05 15:19 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list

The main goal is fairly straight-forwrd here.

The aim of these patches is optimize the number of tpm2_read_public()
calls to the bare minimum. The last patch is a bit out of scope but
I included because it builds on top of this work.

Jarkko Sakkinen (4):
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  KEYS: trusted: Store parent's name to the encoded keys
  KEYS: trusted: Open code tpm2_buf_append()
  tpm2-sessions: Enforce single authorized handle

 drivers/char/tpm/tpm2-cmd.c               |  93 +++++++-
 drivers/char/tpm/tpm2-sessions.c          | 169 ++-------------
 include/linux/tpm.h                       |   3 +-
 security/keys/trusted-keys/tpm2key.asn1   |  17 +-
 security/keys/trusted-keys/trusted_tpm2.c | 250 +++++++++++++---------
 5 files changed, 272 insertions(+), 260 deletions(-)

-- 
2.52.0


^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-05 14:21 UTC (permalink / raw)
  To: steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring
In-Reply-To: <099492ee58996b6f18d73232677757ecadb14cb7.camel@huaweicloud.com>

On Fri, 2025-12-05 at 10:30 +0100, Roberto Sassu wrote:
> On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
> > This patch is for trimming N entries of the IMA event logs as well as
> > cleaning the hash table.
> > 
> > It provides a userspace interface ima_trim_log that can be used to input
> > number N to let kernel to trim N entries of IMA event logs. When read
> > this interface, it returns number of entries trimmed last tim.
> 
> High-level comments:
> - It does not offer the possibility to keep the hash table
> - There is no coordination between taking a snapshot and the readers of
>   the measurements list (I think it is necessary, since reading is
>   based on *pos, which contains the entries read until a given point;
>   if there is a truncate in the middle of the read, *pos would still
>   refer to the non-truncated list and the next read will skip some
>   measurement entries)
> - While trimming per se is ok, I like more the idea of staging changes
>   and letting the user delete the staged measurements list later
> 
> > A mutex ima_trim_list_mutex is provided to allow one trimming request
> > at a time.
> > 
> > Signed-off-by: steven chen <chenste@linux.microsoft.com>
> > ---
> >  security/integrity/ima/ima.h       |  2 +
> >  security/integrity/ima/ima_fs.c    | 78 ++++++++++++++++++++++++++++++
> >  security/integrity/ima/ima_queue.c | 42 ++++++++++++++++
> >  3 files changed, 122 insertions(+)
> > 
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index e3d71d8d56e3..ab0e30ee25ea 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
> >  
> >  #ifdef CONFIG_IMA_KEXEC
> >  void ima_measure_kexec_event(const char *event_name);
> > +long ima_purge_event_log(long number_logs);
> >  #else
> >  static inline void ima_measure_kexec_event(const char *event_name) {}
> > +static inline long ima_purge_event_log(long number_logs) { return 0; }
> >  #endif
> >  
> >  /*
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 87045b09f120..ea93448feedd 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -38,6 +38,11 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
> >  
> >  static int valid_policy = 1;
> >  
> > +#define IMA_LOG_TRIM_REQ_LENGTH 11
> > +static long trimcount;
> > +/* mutex protects atomicity of trimming measurement list requests */
> > +static DEFINE_MUTEX(ima_trim_list_mutex);
> > +
> >  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> >  				     loff_t *ppos, atomic_long_t *val)
> >  {
> > @@ -289,6 +294,69 @@ static const struct file_operations ima_ascii_measurements_ops = {
> >  	.release = seq_release,
> >  };
> >  
> > +static int ima_log_trim_open(struct inode *inode, struct file *filp)
> > +{
> > +	if (!capable(CAP_SYS_ADMIN))
> > +		return -EPERM;
> > +	return 0;
> > +}
> > +
> > +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> > +{
> > +	char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH];	/* greater than largest 'long' string value */
> > +	ssize_t len;
> > +
> > +	len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> > +	return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> > +}
> > +
> > +static ssize_t ima_log_trim_write(struct file *file,
> > +				  const char __user *buf, size_t datalen, loff_t *ppos)
> > +{
> > +	unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
> > +	long count, n;
> > +	int ret;
> > +
> > +	mutex_lock(&ima_trim_list_mutex);
> > +
> > +	if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
> > +		ret = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	n = (int)datalen;
> > +
> > +	ret = copy_from_user(req, buf, datalen);
> > +	if (ret < 0)
> > +		goto out;
> > +
> > +	count = 0;
> > +	for (int i = 0; i < n; ++i) {
> > +		if (req[i] < '0' || req[i] > '9') {
> > +			ret = -EINVAL;
> > +			goto out;
> > +		}
> > +		count = count * 10 + req[i] - '0';
> > +	}
> > +	ret = ima_purge_event_log(count);
> > +
> > +	if (ret < 0)
> > +		goto out;
> > +
> > +	trimcount = ret;
> > +	ret = datalen;
> > +out:
> > +	mutex_unlock(&ima_trim_list_mutex);
> > +	return ret;
> > +}
> > +
> > +static const struct file_operations ima_log_trim_ops = {
> > +	.open = ima_log_trim_open,
> > +	.read = ima_log_trim_read,
> > +	.write = ima_log_trim_write,
> > +	.llseek = generic_file_llseek,
> > +};
> > +
> >  static ssize_t ima_read_policy(char *path)
> >  {
> >  	void *data = NULL;
> > @@ -528,6 +596,16 @@ int __init ima_fs_init(void)
> >  		goto out;
> >  	}
> >  
> > +	dentry = securityfs_create_file("ima_trim_log",
> > +					S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> > +					ima_dir, NULL, &ima_log_trim_ops);
> > +	if (IS_ERR(dentry)) {
> > +		ret = PTR_ERR(dentry);
> > +		goto out;
> > +	}
> > +
> > +	trimcount = 0;
> > +
> >  	dentry = securityfs_create_file("runtime_measurements_count",
> >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> >  				   &ima_measurements_count_ops);
> > diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> > index 590637e81ad1..999cd42c517c 100644
> > --- a/security/integrity/ima/ima_queue.c
> > +++ b/security/integrity/ima/ima_queue.c
> > @@ -220,6 +220,48 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> >  	return result;
> >  }
> >  
> > +/* Delete the IMA event logs */
> > +long ima_purge_event_log(long number_logs)
> > +{
> > +	struct ima_queue_entry *qe;
> > +	long cur = 0;
> > +
> > +	if (number_logs <= 0)
> > +		return number_logs;
> > +
> > +	mutex_lock(&ima_extend_list_mutex);
> > +	rcu_read_lock();
> 
> Sorry, I'm missing why rcu_read_lock() is needed.

Ah, I guess it is because of list_for_each_entry_rcu(). In that case, I
think you can add 'true' as 4th argument, since you are holding
ima_extend_list_mutex.

Roberto

> > +
> > +	/*
> > +	 * Remove this entry from both hash table and the measurement list
> > +	 * When removing from hash table, decrease the length counter
> > +	 * so that the hash table re-sizing logic works correctly
> > +	 */
> > +	list_for_each_entry_rcu(qe, &ima_measurements, later) {
> > +		int i;
> > +
> > +		/* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
> > +		if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
> > +			hlist_del_rcu(&qe->hnext);
> > +
> > +		for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
> > +			kfree(qe->entry->template_data[i].data);
> > +			qe->entry->template_data[i].data = NULL;
> > +			qe->entry->template_data[i].len = 0;
> > +		}
> > +
> > +		atomic_long_dec(&ima_htable.len);
> > +		list_del_rcu(&qe->later);
> 
> Missing kfree() of qe->entry and qe?
> 
> Thanks
> 
> Roberto
> 
> > +		++cur;
> > +		if (cur >= number_logs)
> > +			break;
> > +	}
> > +
> > +	rcu_read_unlock();
> > +	mutex_unlock(&ima_extend_list_mutex);
> > +	return cur;
> > +}
> > +
> >  int ima_restore_measurement_entry(struct ima_template_entry *entry)
> >  {
> >  	int result = 0;


^ permalink raw reply

* Re: [PATCH 1/1] IMA event log trimming
From: Roberto Sassu @ 2025-12-05  9:30 UTC (permalink / raw)
  To: steven chen, linux-integrity
  Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge, linux-security-module, anirudhve, gregorylumen,
	nramas, sushring
In-Reply-To: <20251202232857.8211-2-chenste@linux.microsoft.com>

On Tue, 2025-12-02 at 15:28 -0800, steven chen wrote:
> This patch is for trimming N entries of the IMA event logs as well as
> cleaning the hash table.
> 
> It provides a userspace interface ima_trim_log that can be used to input
> number N to let kernel to trim N entries of IMA event logs. When read
> this interface, it returns number of entries trimmed last tim.

High-level comments:
- It does not offer the possibility to keep the hash table
- There is no coordination between taking a snapshot and the readers of
  the measurements list (I think it is necessary, since reading is
  based on *pos, which contains the entries read until a given point;
  if there is a truncate in the middle of the read, *pos would still
  refer to the non-truncated list and the next read will skip some
  measurement entries)
- While trimming per se is ok, I like more the idea of staging changes
  and letting the user delete the staged measurements list later

> A mutex ima_trim_list_mutex is provided to allow one trimming request
> at a time.
> 
> Signed-off-by: steven chen <chenste@linux.microsoft.com>
> ---
>  security/integrity/ima/ima.h       |  2 +
>  security/integrity/ima/ima_fs.c    | 78 ++++++++++++++++++++++++++++++
>  security/integrity/ima/ima_queue.c | 42 ++++++++++++++++
>  3 files changed, 122 insertions(+)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index e3d71d8d56e3..ab0e30ee25ea 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>  
>  #ifdef CONFIG_IMA_KEXEC
>  void ima_measure_kexec_event(const char *event_name);
> +long ima_purge_event_log(long number_logs);
>  #else
>  static inline void ima_measure_kexec_event(const char *event_name) {}
> +static inline long ima_purge_event_log(long number_logs) { return 0; }
>  #endif
>  
>  /*
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 87045b09f120..ea93448feedd 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -38,6 +38,11 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>  
>  static int valid_policy = 1;
>  
> +#define IMA_LOG_TRIM_REQ_LENGTH 11
> +static long trimcount;
> +/* mutex protects atomicity of trimming measurement list requests */
> +static DEFINE_MUTEX(ima_trim_list_mutex);
> +
>  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>  				     loff_t *ppos, atomic_long_t *val)
>  {
> @@ -289,6 +294,69 @@ static const struct file_operations ima_ascii_measurements_ops = {
>  	.release = seq_release,
>  };
>  
> +static int ima_log_trim_open(struct inode *inode, struct file *filp)
> +{
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +	return 0;
> +}
> +
> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> +{
> +	char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH];	/* greater than largest 'long' string value */
> +	ssize_t len;
> +
> +	len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> +	return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_log_trim_write(struct file *file,
> +				  const char __user *buf, size_t datalen, loff_t *ppos)
> +{
> +	unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
> +	long count, n;
> +	int ret;
> +
> +	mutex_lock(&ima_trim_list_mutex);
> +
> +	if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	n = (int)datalen;
> +
> +	ret = copy_from_user(req, buf, datalen);
> +	if (ret < 0)
> +		goto out;
> +
> +	count = 0;
> +	for (int i = 0; i < n; ++i) {
> +		if (req[i] < '0' || req[i] > '9') {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +		count = count * 10 + req[i] - '0';
> +	}
> +	ret = ima_purge_event_log(count);
> +
> +	if (ret < 0)
> +		goto out;
> +
> +	trimcount = ret;
> +	ret = datalen;
> +out:
> +	mutex_unlock(&ima_trim_list_mutex);
> +	return ret;
> +}
> +
> +static const struct file_operations ima_log_trim_ops = {
> +	.open = ima_log_trim_open,
> +	.read = ima_log_trim_read,
> +	.write = ima_log_trim_write,
> +	.llseek = generic_file_llseek,
> +};
> +
>  static ssize_t ima_read_policy(char *path)
>  {
>  	void *data = NULL;
> @@ -528,6 +596,16 @@ int __init ima_fs_init(void)
>  		goto out;
>  	}
>  
> +	dentry = securityfs_create_file("ima_trim_log",
> +					S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> +					ima_dir, NULL, &ima_log_trim_ops);
> +	if (IS_ERR(dentry)) {
> +		ret = PTR_ERR(dentry);
> +		goto out;
> +	}
> +
> +	trimcount = 0;
> +
>  	dentry = securityfs_create_file("runtime_measurements_count",
>  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>  				   &ima_measurements_count_ops);
> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> index 590637e81ad1..999cd42c517c 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -220,6 +220,48 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
>  	return result;
>  }
>  
> +/* Delete the IMA event logs */
> +long ima_purge_event_log(long number_logs)
> +{
> +	struct ima_queue_entry *qe;
> +	long cur = 0;
> +
> +	if (number_logs <= 0)
> +		return number_logs;
> +
> +	mutex_lock(&ima_extend_list_mutex);
> +	rcu_read_lock();

Sorry, I'm missing why rcu_read_lock() is needed.

> +
> +	/*
> +	 * Remove this entry from both hash table and the measurement list
> +	 * When removing from hash table, decrease the length counter
> +	 * so that the hash table re-sizing logic works correctly
> +	 */
> +	list_for_each_entry_rcu(qe, &ima_measurements, later) {
> +		int i;
> +
> +		/* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
> +		if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
> +			hlist_del_rcu(&qe->hnext);
> +
> +		for (i = 0; i < qe->entry->template_desc->num_fields; i++) {
> +			kfree(qe->entry->template_data[i].data);
> +			qe->entry->template_data[i].data = NULL;
> +			qe->entry->template_data[i].len = 0;
> +		}
> +
> +		atomic_long_dec(&ima_htable.len);
> +		list_del_rcu(&qe->later);

Missing kfree() of qe->entry and qe?

Thanks

Roberto

> +		++cur;
> +		if (cur >= number_logs)
> +			break;
> +	}
> +
> +	rcu_read_unlock();
> +	mutex_unlock(&ima_extend_list_mutex);
> +	return cur;
> +}
> +
>  int ima_restore_measurement_entry(struct ima_template_entry *entry)
>  {
>  	int result = 0;


^ permalink raw reply

* [GIT PULL] TPM DEVICE DRIVER: tpmdd-sessions-next-6.19-rc1
From: Jarkko Sakkinen @ 2025-12-05  5:51 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Peter Huewe, Jason Gunthorpe, David Howells, keyrings,
	linux-integrity, linux-kernel

The following changes since commit 2061f18ad76ecaddf8ed17df81b8611ea88dbddd:

  Merge tag 'caps-pr-20251204' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux (2025-12-04 20:10:28 -0800)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git tags/tpmdd-sessions-next-6.19-rc1

for you to fetch changes up to b7960b90486139022d2d39caad90db252c469bab:

  tpm2-sessions: Open code tpm_buf_append_hmac_session() (2025-12-05 06:42:51 +0200)

----------------------------------------------------------------
Hi,

This second pull request for 6.19 is targeted for tpm2-sessions updates.

There's two bug fixes and two more cosmetic tweaks for HMAC protected
sessions. They provide a baseine for further improvements to be
implemented during the the course of the release cycle.

BR, Jarkko

----------------------------------------------------------------
Jarkko Sakkinen (4):
      tpm2-sessions: Fix out of range indexing in name_size
      tpm2-sessions: Fix tpm2_read_public range checks
      tpm2-sessions: Remove 'attributes' parameter from tpm_buf_append_auth
      tpm2-sessions: Open code tpm_buf_append_hmac_session()

 drivers/char/tpm/tpm2-cmd.c               |  42 +++++--
 drivers/char/tpm/tpm2-sessions.c          | 199 +++++++++++++++++++-----------
 include/linux/tpm.h                       |  38 ++----
 security/keys/trusted-keys/trusted_tpm2.c |  41 ++++--
 4 files changed, 204 insertions(+), 116 deletions(-)

^ permalink raw reply

* [PATCH v3 2/2] KEYS: trusted: Store parent's name to the encoded keys
From: Jarkko Sakkinen @ 2025-12-05  4:00 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251205040021.207369-1-jarkko@kernel.org>

Extend TPMKey ASN.1 speciication [1] with an optional 'parentName'
attribute containing TPM name of the parent key (in other words, TPMT_HA
blob).

The life-cycle for trusted keys will now proceed as follows:

1. Encode parent's name to the 'paretName' during tpm2_key_encode().
2. During tpm2_unseal_trusted, read parent's name from 'parentName'. When
   the attribute is not available, fallback on doing tpm2_read_public().

In other words, in the common (i.e., not loading a legacy key blob),
tpm2_read_public() will now only happen at the time when a key is first
created.

In addition, move tpm2_read_public() to 'tpm2-cmd.c' and make its body
unconditional so that the binary format of the saved keys is not dependent
on kernel configuration.

[1] https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.txt

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Fixed three low-hanging fruit issues I found myself.
v2:
- A new patch.
---
 drivers/char/tpm/tpm2-cmd.c               | 90 ++++++++++++++++++++++
 drivers/char/tpm/tpm2-sessions.c          | 92 ----------------------
 include/linux/tpm.h                       |  9 +--
 security/keys/trusted-keys/tpm2key.asn1   | 17 ++++-
 security/keys/trusted-keys/trusted_tpm2.c | 93 ++++++++++++++++-------
 5 files changed, 173 insertions(+), 128 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 1f561ad3bdcf..360bc014d895 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -793,3 +793,93 @@ int tpm2_find_cc(struct tpm_chip *chip, u32 cc)
 
 	return -1;
 }
+
+/*
+ * Name Size based on TPM algorithm (assumes no hash bigger than 255)
+ */
+static int name_size(const u8 *name)
+{
+	u16 hash_alg = get_unaligned_be16(name);
+
+	switch (hash_alg) {
+	case TPM_ALG_SHA1:
+		return SHA1_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA256:
+		return SHA256_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA384:
+		return SHA384_DIGEST_SIZE + 2;
+	case TPM_ALG_SHA512:
+		return SHA512_DIGEST_SIZE + 2;
+	default:
+		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
+		return -EINVAL;
+	}
+}
+
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+{
+	u32 mso = tpm2_handle_mso(handle);
+	off_t offset = TPM_HEADER_SIZE;
+	int rc, name_size_alg;
+	struct tpm_buf buf;
+
+	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
+	    mso != TPM2_MSO_NVRAM) {
+		memcpy(name, &handle, sizeof(u32));
+		return sizeof(u32);
+	}
+
+	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+	if (rc)
+		return rc;
+
+	tpm_buf_append_u32(&buf, handle);
+
+	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
+	if (rc) {
+		tpm_buf_destroy(&buf);
+		return tpm_ret_to_err(rc);
+	}
+
+	/* Skip TPMT_PUBLIC: */
+	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);
+		return -EIO;
+	}
+
+	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);
+		return -EIO;
+	}
+
+	if (offset + rc > tpm_buf_length(&buf)) {
+		tpm_buf_destroy(&buf);
+		return -EIO;
+	}
+
+	memcpy(name, &buf.data[offset], rc);
+	return name_size_alg;
+}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index e33be09446ff..c8b44b1a6cb6 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -140,98 +140,6 @@ struct tpm2_auth {
 	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
-#ifdef CONFIG_TCG_TPM2_HMAC
-/*
- * Name Size based on TPM algorithm (assumes no hash bigger than 255)
- */
-static int name_size(const u8 *name)
-{
-	u16 hash_alg = get_unaligned_be16(name);
-
-	switch (hash_alg) {
-	case TPM_ALG_SHA1:
-		return SHA1_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA256:
-		return SHA256_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA384:
-		return SHA384_DIGEST_SIZE + 2;
-	case TPM_ALG_SHA512:
-		return SHA512_DIGEST_SIZE + 2;
-	default:
-		pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
-		return -EINVAL;
-	}
-}
-
-/**
- * tpm2_read_public: Resolve TPM name for a handle
- * @chip:		TPM chip to use.
- * @handle:		TPM handle.
- * @name:		A buffer for returning the name blob. Must have a
- *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
- *
- * Returns size of TPM handle name of success.
- * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
- */
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
-{
-	u32 mso = tpm2_handle_mso(handle);
-	off_t offset = TPM_HEADER_SIZE;
-	int rc, name_size_alg;
-	struct tpm_buf buf;
-
-	if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
-	    mso != TPM2_MSO_NVRAM) {
-		memcpy(name, &handle, sizeof(u32));
-		return sizeof(u32);
-	}
-
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
-	if (rc)
-		return rc;
-
-	tpm_buf_append_u32(&buf, handle);
-
-	rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
-	if (rc) {
-		tpm_buf_destroy(&buf);
-		return tpm_ret_to_err(rc);
-	}
-
-	/* Skip TPMT_PUBLIC: */
-	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);
-		return -EIO;
-	}
-
-	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);
-		return -EIO;
-	}
-
-	if (offset + rc > tpm_buf_length(&buf)) {
-		tpm_buf_destroy(&buf);
-		return -EIO;
-	}
-
-	memcpy(name, &buf.data[offset], rc);
-	return name_size_alg;
-}
-EXPORT_SYMBOL_GPL(tpm2_read_public);
-#endif /* CONFIG_TCG_TPM2_HMAC */
-
 /**
  * tpm_buf_append_name() - Append a handle and store TPM name
  * @chip:		TPM chip to use.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 319ba75dd79a..704fceb46a40 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -478,6 +478,7 @@ extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
 extern struct tpm_chip *tpm_default_chip(void);
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
 int tpm2_find_hash_alg(unsigned int crypto_id);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 
 static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
 {
@@ -544,7 +545,6 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
-int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -568,13 +568,6 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
-
-static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
-				   void *name)
-{
-	memcpy(name, &handle, sizeof(u32));
-	return sizeof(u32);
-}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1
index f57f869ad600..70cca3b7c3d4 100644
--- a/security/keys/trusted-keys/tpm2key.asn1
+++ b/security/keys/trusted-keys/tpm2key.asn1
@@ -1,11 +1,26 @@
 ---
 --- ASN.1 for TPM 2.0 keys
 ---
+TPMPolicy ::= SEQUENCE {
+	commandCode	[0] EXPLICIT INTEGER,
+	commandPolicy	[1] EXPLICIT OCTET STRING
+}
+
+TPMAuthPolicy ::= SEQUENCE {
+	name		[0] EXPLICIT UTF8String OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy
+}
 
 TPMKey ::= SEQUENCE {
 	type		OBJECT IDENTIFIER ({tpm2_key_type}),
 	emptyAuth	[0] EXPLICIT BOOLEAN OPTIONAL,
+	policy		[1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL,
+	secret		[2] EXPLICIT OCTET STRING OPTIONAL,
+	authPolicy	[3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL,
+	description	[4] EXPLICIT UTF8String OPTIONAL,
+	rsaParent	[5] EXPLICIT BOOLEAN OPTIONAL,
+	parentName	[6] EXPLICIT OCTET STRING OPTIONAL ({tpm2_key_parent_name}),
 	parent		INTEGER ({tpm2_key_parent}),
 	pubkey		OCTET STRING ({tpm2_key_pub}),
 	privkey		OCTET STRING ({tpm2_key_priv})
-	}
+}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 88bafbcc011a..07f04cc010bc 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -20,16 +20,26 @@
 
 static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
 
+enum tpm_key_tag {
+	TPM_KEY_TAG_EMPTY_AUTH	= 0,
+	TPM_KEY_TAG_POLICY	= 1,
+	TPM_KEY_TAG_SECRET	= 2,
+	TPM_KEY_TAG_AUTH_POLICY	= 3,
+	TPM_KEY_TAG_DESCRIPTION = 4,
+	TPM_KEY_TAG_RSA_PARENT	= 5,
+	TPM_KEY_TAG_PARENT_NAME	= 6,
+};
+
 static int tpm2_key_encode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 *src, u32 len)
+			   u8 *src, u32 len, u8 *parent_name,
+			   u16 parent_name_size)
 {
 	const int SCRATCH_SIZE = PAGE_SIZE;
-	u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
-	u8 *work = scratch, *work1;
-	u8 *end_work = scratch + SCRATCH_SIZE;
-	u8 *priv, *pub;
+	u8 *end_work, *name_end;
 	u16 priv_len, pub_len;
+	u8 *work, *work1;
+	u8 *priv, *pub;
 	int ret;
 
 	priv_len = get_unaligned_be16(src) + 2;
@@ -40,23 +50,41 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	pub_len = get_unaligned_be16(src) + 2;
 	pub = src;
 
+	u8 *scratch __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
 	if (!scratch)
 		return -ENOMEM;
 
+	work = scratch;
+	end_work = scratch + SCRATCH_SIZE;
+
 	work = asn1_encode_oid(work, end_work, tpm2key_oid,
 			       asn1_oid_len(tpm2key_oid));
 
 	if (options->blobauth_len == 0) {
-		unsigned char bool[3], *w = bool;
-		/* tag 0 is emptyAuth */
-		w = asn1_encode_boolean(w, w + sizeof(bool), true);
-		if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
-			ret = PTR_ERR(w);
-			goto err;
+		u8 *bool_end;
+		u8 bool[3];
+
+		bool_end = asn1_encode_boolean(&bool[0], &bool[sizeof(bool)],
+					       true);
+		if (IS_ERR(bool_end)) {
+			pr_err("BUG: Boolean failed to encode\n");
+			return PTR_ERR(bool_end);
 		}
-		work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
+
+		work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_EMPTY_AUTH,
+				       bool, bool_end - bool);
 	}
 
+	u8 *name_encoded __free(kfree) = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
+	if (!name_encoded)
+		return -ENOMEM;
+
+	name_end = asn1_encode_octet_string(&name_encoded[0],
+					    &name_encoded[SCRATCH_SIZE],
+					    parent_name, parent_name_size);
+	work = asn1_encode_tag(work, end_work, TPM_KEY_TAG_PARENT_NAME,
+			       name_encoded, name_end - name_encoded);
+
 	/*
 	 * Assume both octet strings will encode to a 2 byte definite length
 	 *
@@ -65,8 +93,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	 */
 	if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
 		 "BUG: scratch buffer is too small")) {
-		ret = -EINVAL;
-		goto err;
+		return -EINVAL;
 	}
 
 	work = asn1_encode_integer(work, end_work, options->keyhandle);
@@ -79,15 +106,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
 	if (IS_ERR(work1)) {
 		ret = PTR_ERR(work1);
 		pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
-		goto err;
+		return ret;
 	}
 
-	kfree(scratch);
 	return work1 - payload->blob;
-
-err:
-	kfree(scratch);
-	return ret;
 }
 
 struct tpm2_key_context {
@@ -96,11 +118,13 @@ struct tpm2_key_context {
 	u32 pub_len;
 	const u8 *priv;
 	u32 priv_len;
+	const u8 *name;
+	u32 name_len;
 };
 
 static int tpm2_key_decode(struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
-			   u8 **buf)
+			   u8 **buf, u8 *parent_name, u16 *parent_name_size)
 {
 	int ret;
 	struct tpm2_key_context ctx;
@@ -127,6 +151,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
 	blob += ctx.priv_len;
 
 	memcpy(blob, ctx.pub, ctx.pub_len);
+	memcpy(parent_name, ctx.name, ctx.name_len);
+	*parent_name_size = ctx.name_len;
 
 	return 0;
 }
@@ -190,6 +216,16 @@ int tpm2_key_priv(void *context, size_t hdrlen,
 	return 0;
 }
 
+int tpm2_key_parent_name(void *context, size_t hdrlen, unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct tpm2_key_context *ctx = context;
+
+	ctx->name = value;
+	ctx->name_len = vlen;
+
+	return 0;
+}
 /**
  * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
  *
@@ -347,7 +383,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		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, parent_name, parent_name_size);
 	if (blob_len < 0)
 		rc = blob_len;
 
@@ -602,7 +639,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
 	 * blob is in the legacy format if decoding does not end successfully.
 	 */
-	rc = tpm2_key_decode(payload, options, &blob);
+	rc = tpm2_key_decode(payload, options, &blob, &parent_name[0], &parent_name_size);
 	if (rc) {
 		blob = payload->blob;
 		payload->old_format = 1;
@@ -617,11 +654,13 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
-	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
-	if (rc < 0)
-		goto out;
+	if (!parent_name_size) {
+		rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+		if (rc < 0)
+			goto out;
 
-	parent_name_size = rc;
+		parent_name_size = rc;
+	}
 
 	rc = tpm2_load_cmd(chip, payload, options, parent_name,
 			   parent_name_size, blob, &blob_handle);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v3 1/2] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05  4:00 UTC (permalink / raw)
  To: linux-integrity
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, James Bottomley,
	Mimi Zohar, David Howells, Paul Moore, James Morris,
	Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
	open list:SECURITY SUBSYSTEM
In-Reply-To: <20251205040021.207369-1-jarkko@kernel.org>

tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.

1. Export tpm2_read_public in order to make it callable from
   'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
   halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- No changes.
v2:
- No changes.
---
 drivers/char/tpm/tpm2-cmd.c               |   3 +-
 drivers/char/tpm/tpm2-sessions.c          |  95 +++++------------
 include/linux/tpm.h                       |  10 +-
 security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
 4 files changed, 118 insertions(+), 114 deletions(-)

diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 	}
 
 	if (!disable_pcr_integrity) {
-		rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+		rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+					 sizeof(u32));
 		if (rc) {
 			tpm_buf_destroy(&buf);
 			return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..e33be09446ff 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
 	 * handle, but they are part of the session by name, which
 	 * we must compute and remember
 	 */
-	u32 name_h[AUTH_MAX_NAMES];
 	u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+	u16 name_size_tbl[AUTH_MAX_NAMES];
 };
 
 #ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
 	}
 }
 
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip:		TPM chip to use.
+ * @handle:		TPM handle.
+ * @name:		A buffer for returning the name blob. Must have a
+ *			capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 {
 	u32 mso = tpm2_handle_mso(handle);
 	off_t offset = TPM_HEADER_SIZE;
@@ -219,14 +229,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
 	memcpy(name, &buf.data[offset], rc);
 	return name_size_alg;
 }
+EXPORT_SYMBOL_GPL(tpm2_read_public);
 #endif /* CONFIG_TCG_TPM2_HMAC */
 
 /**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip:		TPM chip to use.
+ * @buf:		TPM buffer containing the TPM command in-transit.
+ * @handle:		TPM handle to be appended.
+ * @name:		TPM name of the handle
+ * @name_size:		Size of the TPM name.
  *
  * In order to compute session HMACs, we need to know the names of the
  * objects pointed to by the handles.  For most objects, this is simply
@@ -243,15 +255,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
  * will be caused by an incorrect programming model and indicated by a
  * kernel message.
  *
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
  */
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name)
+			u32 handle, u8 *name, u16 name_size)
 {
 #ifdef CONFIG_TCG_TPM2_HMAC
-	enum tpm2_mso_type mso = tpm2_handle_mso(handle);
 	struct tpm2_auth *auth;
-	u16 name_size_alg;
 	int slot;
 	int ret;
 #endif
@@ -276,36 +287,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
 	}
 	tpm_buf_append_u32(buf, handle);
 	auth->session += 4;
-
-	if (mso == TPM2_MSO_PERSISTENT ||
-	    mso == TPM2_MSO_VOLATILE ||
-	    mso == TPM2_MSO_NVRAM) {
-		if (!name) {
-			ret = tpm2_read_public(chip, handle, auth->name[slot]);
-			if (ret < 0)
-				goto err;
-
-			name_size_alg = ret;
-		}
-	} else {
-		if (name) {
-			dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
-				handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-
-	auth->name_h[slot] = handle;
-	if (name)
-		memcpy(auth->name[slot], name, name_size_alg);
+	memcpy(auth->name[slot], name, name_size);
+	auth->name_size_tbl[slot] = name_size;
 #endif
 	return 0;
 
 #ifdef CONFIG_TCG_TPM2_HMAC
 err:
 	tpm2_end_auth_session(chip);
-	return tpm_ret_to_err(ret);
+	return ret;
 #endif
 }
 EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -613,22 +603,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	attrs = chip->cc_attrs_tbl[i];
 
 	handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+	offset_s += handles * sizeof(u32);
 
-	/*
-	 * just check the names, it's easy to make mistakes.  This
-	 * would happen if someone added a handle via
-	 * tpm_buf_append_u32() instead of tpm_buf_append_name()
-	 */
-	for (i = 0; i < handles; i++) {
-		u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
-		if (auth->name_h[i] != handle) {
-			dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
-			ret = -EIO;
-			goto err;
-		}
-	}
-	/* point offset_s to the start of the sessions */
 	val = tpm_buf_read_u32(buf, &offset_s);
 	/* point offset_p to the start of the parameters */
 	offset_p = offset_s + val;
@@ -689,23 +665,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
 	/* ordinal is already BE */
 	sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
 	/* add the handle names */
-	for (i = 0; i < handles; i++) {
-		enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
-		if (mso == TPM2_MSO_PERSISTENT ||
-		    mso == TPM2_MSO_VOLATILE ||
-		    mso == TPM2_MSO_NVRAM) {
-			ret = name_size(auth->name[i]);
-			if (ret < 0)
-				goto err;
-
-			sha256_update(&sctx, auth->name[i], ret);
-		} else {
-			__be32 h = cpu_to_be32(auth->name_h[i]);
-
-			sha256_update(&sctx, (u8 *)&h, 4);
-		}
-	}
+	for (i = 0; i < handles; i++)
+		sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
 	if (offset_s != tpm_buf_length(buf))
 		sha256_update(&sctx, &buf->data[offset_s],
 			      tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..319ba75dd79a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -530,7 +530,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
 }
 
 int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
-			u32 handle, u8 *name);
+			u32 handle, u8 *name, u16 name_size);
 void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
 				 u8 attributes, u8 *passphrase,
 				 int passphraselen);
@@ -544,6 +544,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
 int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
 				int rc);
 void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
 #else
 #include <linux/unaligned.h>
 
@@ -567,6 +568,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
 {
 	return rc;
 }
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+				   void *name)
+{
+	memcpy(name, &handle, sizeof(u32));
+	return sizeof(u32);
+}
 #endif	/* CONFIG_TCG_TPM2_HMAC */
 
 #endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..88bafbcc011a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -233,8 +233,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
 	off_t offset = TPM_HEADER_SIZE;
 	struct tpm_buf buf, sized;
+	u16 parent_name_size;
 	int blob_len = 0;
 	int hash;
 	u32 flags;
@@ -251,6 +253,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (rc)
 		return rc;
 
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out_put;
+
+	parent_name_size = rc;
+
 	rc = tpm2_start_auth_session(chip);
 	if (rc)
 		goto out_put;
@@ -268,7 +276,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 		goto out_put;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -355,21 +364,25 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip:		TPM chip to use.
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob:		The decoded payload for the key.
+ * @blob_handle:	On success, will contain handle to the loaded keyedhash
+ *			blob.
  *
- * Return: 0 on success.
- *        -E2BIG on wrong payload size.
- *        -EPERM on tpm error status.
- *        < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_payload *payload,
 			 struct trusted_key_options *options,
+			 u8 *parent_name,
+			 u16 parent_name_size,
+			 const u8 *blob,
 			 u32 *blob_handle)
 {
 	u8 *blob_ref __free(kfree) = NULL;
@@ -377,27 +390,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 	unsigned int private_len;
 	unsigned int public_len;
 	unsigned int blob_len;
-	u8 *blob, *pub;
+	const u8 *pub;
 	int rc;
 	u32 attrs;
 
-	rc = tpm2_key_decode(payload, options, &blob);
-	if (rc) {
-		/* old form */
-		blob = payload->blob;
-		payload->old_format = 1;
-	} else {
-		/* Bind for cleanup: */
-		blob_ref = blob;
-	}
-
-	/* new format carries keyhandle but old format doesn't */
-	if (!options->keyhandle)
-		return -EINVAL;
-
 	/* must be big enough for at least the two be16 size counts */
 	if (payload->blob_len < 4)
-		return -EINVAL;
+		return -E2BIG;
 
 	private_len = get_unaligned_be16(blob);
 
@@ -433,7 +432,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -465,20 +465,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip:		TPM chip to use
+ * @payload:		Key data in clear text.
+ * @options:		Trusted key options.
+ * @parent_name:	A cryptographic name, i.e. a TPMT_HA blob, of the
+ *			parent key.
+ * @blob_handle:	Handle to the loaded keyedhash blob.
  *
- * Return: 0 on success
- *         -EPERM on tpm error status
- *         < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
  */
 static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_payload *payload,
 			   struct trusted_key_options *options,
+			   u8 *parent_name,
+			   u16 parent_name_size,
 			   u32 blob_handle)
 {
 	struct tpm_header *head;
@@ -498,7 +501,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 		return rc;
 	}
 
-	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+	rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+				 parent_name_size);
 	if (rc)
 		goto out;
 
@@ -573,30 +577,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
 }
 
 /**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip:	TPM chip to use.
+ * @payload:	Key data in clear text.
+ * @options:	Trusted key options.
  *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
  */
 int tpm2_unseal_trusted(struct tpm_chip *chip,
 			struct trusted_key_payload *payload,
 			struct trusted_key_options *options)
 {
+	u8 *blob_ref __free(kfree) = NULL;
+	u8 parent_name[2 + SHA512_DIGEST_SIZE];
+	u16 parent_name_size;
 	u32 blob_handle;
+	u8 *blob;
 	int rc;
 
+	/*
+	 * Try to decode the provided blob as an ASN.1 blob. Assume that the
+	 * blob is in the legacy format if decoding does not end successfully.
+	 */
+	rc = tpm2_key_decode(payload, options, &blob);
+	if (rc) {
+		blob = payload->blob;
+		payload->old_format = 1;
+	} else {
+		blob_ref = blob;
+	}
+
+	if (!options->keyhandle)
+		return -EINVAL;
+
 	rc = tpm_try_get_ops(chip);
 	if (rc)
 		return rc;
 
-	rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+	rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+	if (rc < 0)
+		goto out;
+
+	parent_name_size = rc;
+
+	rc = tpm2_load_cmd(chip, payload, options, parent_name,
+			   parent_name_size, blob, &blob_handle);
 	if (rc)
 		goto out;
 
-	rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+	rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+			     parent_name_size, blob_handle);
+
 	tpm2_flush_context(chip, blob_handle);
 
 out:
-- 
2.52.0


^ permalink raw reply related

* [PATCH v3 0/2] KEYS: trusted: Optimize tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-05  4:00 UTC (permalink / raw)
  To: linux-integrity; +Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, open list

The main goal is fairly straight-forwrd here.

The aim of these patches is optimize the number of tpm2_read_public()
calls to the bare minimum.

Jarkko Sakkinen (2):
  KEYS: trusted: Re-orchestrate tpm2_read_public() calls
  KEYS: trusted: Store parent's name to the encoded keys

 drivers/char/tpm/tpm2-cmd.c               |  93 +++++++++-
 drivers/char/tpm/tpm2-sessions.c          | 163 ++---------------
 include/linux/tpm.h                       |   3 +-
 security/keys/trusted-keys/tpm2key.asn1   |  17 +-
 security/keys/trusted-keys/trusted_tpm2.c | 207 +++++++++++++++-------
 5 files changed, 266 insertions(+), 217 deletions(-)

-- 
2.52.0


^ permalink raw reply


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