Linux Integrity Measurement development
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko@kernel.org>
To: Matthew Garrett <matthewg@nvidia.com>
Cc: mjg59@srcf.ucam.org, keyrings@vger.kernel.org,
	James.Bottomley@hansenpartnership.com,
	linux-integrity@vger.kernel.org, rafael@kernel.org,
	linux-pm@vger.kernel.org, linux-efi@vger.kernel.org
Subject: Re: [PATCH 01/17] tpm: Define a kernel-owned TPM NV index that can't be modified by userland
Date: Thu, 8 Oct 2026 19:23:02 +0300	[thread overview]
Message-ID: <asfDZiuFKCuuRysC@kernel.org> (raw)
In-Reply-To: <20261008132532.1155166-2-matthewg@nvidia.com>

On Thu, Oct 08, 2026 at 06:20:17AM -0700, Matthew Garrett wrote:
> TPM NV indexes can be used for various purposes, including using them as
> PCRs without depleting the limited number of hardware PCRs. It would be
> beneficial to have one that's under control of the kernel in order to be
> able to prove whether certain TPM operations occurred within the kernel
> or not.
> 
> Reserve NV index 0x014c4853 for use by the kernel, and filter commands
> submitted through /dev/tpm* and /dev/tpmrm* so that userspace cannot
> undefine or modify it. Blocking definition is more awkward so let's
> allow that for now, not being able to modify it means there's nothing
> interesting they can do there. The filter simply validates which handle
> the relevant set of commands is referring to and returns -EPERM if it's
> the kernel one.
> 
> NV indexes are from allocated ranges and this is a range allocated to
> Linux, so there should be no userland depending on the ability to access
> this - but let's gate it behind a default N config option anyway.
> 
> Signed-off-by: Matthew Garrett <matthewg@nvidia.com>
> ---
>  drivers/char/tpm/Kconfig          |  8 ++++
>  drivers/char/tpm/tpm-dev-common.c | 67 +++++++++++++++++++++++++++++++
>  include/linux/tpm.h               |  6 +++
>  include/linux/tpm_command.h       | 10 +++++
>  4 files changed, 91 insertions(+)
> 
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 5f672f2c01b0..a454b63edca5 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -253,5 +253,13 @@ config TCG_SVSM
>  	  level (usually VMPL0).  To compile this driver as a module, choose M
>  	  here; the module will be called tpm_svsm.
>  
> +config TCG_TPM_KERNEL_NVINDEX
> +	bool "Kernel-only NV index"
> +	help
> +	  Allocate a TPM NV index and block userland from modifying it. This
> +	  allows the kernel to develop a unique state that distinguishes it
> +	  from userspace, making it possible to prove that a TPM object
> +	  was created by the kernel.
> +
>  source "drivers/char/tpm/st33zp24/Kconfig"
>  endif # TCG_TPM
> diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
> index f942c0c8e402..1fd93cdaf306 100644
> --- a/drivers/char/tpm/tpm-dev-common.c
> +++ b/drivers/char/tpm/tpm-dev-common.c
> @@ -15,18 +15,85 @@
>  #include <linux/poll.h>
>  #include <linux/slab.h>
>  #include <linux/uaccess.h>
> +#include <linux/unaligned.h>
>  #include <linux/workqueue.h>
>  #include "tpm.h"
>  #include "tpm-dev.h"
>  
>  static struct workqueue_struct *tpm_dev_wq;
>  
> +#ifdef CONFIG_TCG_TPM_KERNEL_NVINDEX
> +/*
> + * Commands that undefine or modify an NV index, and the position of the
> + * NV index in the command's handle area.
> + */
> +static const struct {
> +	u32 cc;
> +	unsigned int handle;
> +} tpm2_nv_modify_cmds[] = {
> +	{ TPM2_CC_NV_UNDEFINE_SPACE_SPECIAL,	0 },
> +	{ TPM2_CC_NV_UNDEFINE_SPACE,		1 },
> +	{ TPM2_CC_NV_INCREMENT,			1 },
> +	{ TPM2_CC_NV_SET_BITS,			1 },
> +	{ TPM2_CC_NV_EXTEND,			1 },
> +	{ TPM2_CC_NV_WRITE,			1 },
> +	{ TPM2_CC_NV_WRITE_LOCK,		1 },
> +	{ TPM2_CC_NV_CHANGE_AUTH,		0 },
> +	{ TPM2_CC_NV_READ_LOCK,			1 },
> +};
> +
> +/*
> + * Returns true if a command from userspace attempts to define, undefine
> + * or modify the kernel-owned NV index.
> + */
> +static bool tpm2_dev_cmd_is_blocked(const u8 *buf, size_t bufsiz)

This function could have less generic name.

tpm2_dev_is_kernel_nv_change?

I'm not a huge fan of this name either but it does say quite clearly
what the function does at least. Just a suggestion.


> +{
> +	const struct tpm_header *header = (const void *)buf;
> +	size_t len = min_t(size_t, be32_to_cpu(header->length), bufsiz);
> +	u32 cc;
> +	int i, index;
> +
> +	if (len < TPM_HEADER_SIZE)
> +		return false;
> +
> +	cc = be32_to_cpu(header->ordinal);
> +
> +	for (i = 0; i < ARRAY_SIZE(tpm2_nv_modify_cmds); i++) {
> +		size_t offset;
> +
> +		if (tpm2_nv_modify_cmds[i].cc != cc)
> +			continue;
> +
> +		offset = TPM_HEADER_SIZE +
> +			 tpm2_nv_modify_cmds[i].handle * sizeof(u32);
> +		if (len < offset + sizeof(u32))
> +			return false;

This will result -E2BIG in tpm_try_transmit() if I recall correctly.

Probably that's how it should be so that errnos are given at a single
location.

So not asking for anything :-)

> +
> +		index = get_unaligned_be32(&buf[offset]);
> +
> +		if (index == TPM2_KERNEL_NV_INDEX)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +#else
> +static inline bool tpm2_dev_cmd_is_blocked(const u8 *buf, size_t bufsiz)
> +{
> +	return false;
> +}
> +#endif
> +
>  static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
>  				u8 *buf, size_t bufsiz)
>  {
>  	struct tpm_header *header = (void *)buf;
>  	ssize_t ret, len;
>  
> +	if ((chip->flags & TPM_CHIP_FLAG_TPM2) &&
> +	    tpm2_dev_cmd_is_blocked(buf, bufsiz))
> +		return -EPERM;
> +
>  	if (chip->flags & TPM_CHIP_FLAG_TPM2)
>  		tpm2_end_auth_session(chip);
>  
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 0db277af45c3..b6b862c3be3b 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -178,6 +178,12 @@ static inline enum tpm2_mso_type tpm2_handle_mso(u32 handle)
>  	return handle >> 24;
>  }
>  
> +/*
> + * NV index reserved for use by the kernel. Userspace is not permitted to
> + * undefine or modify it.
> + */
> +#define TPM2_KERNEL_NV_INDEX	0x014c4853
> +
>  #define TPM_VID_INTEL    0x8086
>  #define TPM_VID_WINBOND  0x1050
>  #define TPM_VID_STM      0x104A
> diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
> index fc446a1282e2..79f547ca6dbf 100644
> --- a/include/linux/tpm_command.h
> +++ b/include/linux/tpm_command.h
> @@ -214,14 +214,24 @@ enum tpm2_return_codes {
>  
>  enum tpm2_command_codes {
>  	TPM2_CC_FIRST			= 0x011F,
> +	TPM2_CC_NV_UNDEFINE_SPACE_SPECIAL = 0x011F,
>  	TPM2_CC_HIERARCHY_CONTROL	= 0x0121,
> +	TPM2_CC_NV_UNDEFINE_SPACE	= 0x0122,
>  	TPM2_CC_HIERARCHY_CHANGE_AUTH	= 0x0129,
> +	TPM2_CC_NV_DEFINE_SPACE		= 0x012A,
>  	TPM2_CC_CREATE_PRIMARY		= 0x0131,
> +	TPM2_CC_NV_INCREMENT		= 0x0134,
> +	TPM2_CC_NV_SET_BITS		= 0x0135,
> +	TPM2_CC_NV_EXTEND		= 0x0136,
> +	TPM2_CC_NV_WRITE		= 0x0137,
> +	TPM2_CC_NV_WRITE_LOCK		= 0x0138,
> +	TPM2_CC_NV_CHANGE_AUTH		= 0x013B,
>  	TPM2_CC_SEQUENCE_COMPLETE	= 0x013E,
>  	TPM2_CC_SELF_TEST		= 0x0143,
>  	TPM2_CC_STARTUP			= 0x0144,
>  	TPM2_CC_SHUTDOWN		= 0x0145,
>  	TPM2_CC_NV_READ			= 0x014E,
> +	TPM2_CC_NV_READ_LOCK		= 0x014F,
>  	TPM2_CC_CREATE			= 0x0153,
>  	TPM2_CC_LOAD			= 0x0157,
>  	TPM2_CC_SEQUENCE_UPDATE		= 0x015C,
> -- 
> 2.43.0
> 
> 

Br, Jarkko

  parent reply	other threads:[~2026-10-08 16:23 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08 13:20 [RFC] Make hibernation work with lockdown Matthew Garrett
2026-10-08 13:20 ` [PATCH 01/17] tpm: Define a kernel-owned TPM NV index that can't be modified by userland Matthew Garrett
2026-10-08 13:41   ` Matthew Garrett
2026-10-08 16:24     ` Jarkko Sakkinen
2026-10-08 16:23   ` Jarkko Sakkinen [this message]
2026-10-09  8:33     ` Matthew Garrett
2026-10-08 17:06   ` Ilias Apalodimas
2026-10-10  9:22   ` James Bottomley
2026-10-08 13:20 ` [PATCH 02/17] efi: Add a mechanism to modify TPM state depending on kernel security features Matthew Garrett
2026-10-08 16:38   ` Jarkko Sakkinen
2026-10-08 13:20 ` [PATCH 03/17] tpm: Allow tpm2_start_auth_session() to start an audit session Matthew Garrett
2026-10-08 13:20 ` [PATCH 04/17] tpm: Log commands executed in " Matthew Garrett
2026-10-08 13:20 ` [PATCH 05/17] tpm: Add a kernel attestation key and signed audit digest retrieval Matthew Garrett
2026-10-08 16:45   ` James Bottomley
2026-10-09  8:29     ` Matthew Garrett
2026-10-08 13:20 ` [PATCH 06/17] tpm: Use TPM2_NV_ReadPublic to read NV index names Matthew Garrett
2026-10-08 13:20 ` [PATCH 07/17] tpm: Add in-kernel support for reading NV indices Matthew Garrett
2026-10-08 13:20 ` [PATCH 08/17] tpm: Add NV define, undefine and write helpers Matthew Garrett
2026-10-08 13:20 ` [PATCH 09/17] tpm: Provision the kernel NV index at registration Matthew Garrett
2026-10-08 13:20 ` [PATCH 10/17] tpm: Move the bounds-checked response reader to a header Matthew Garrett
2026-10-08 13:20 ` [PATCH 11/17] tpm: Add kernel signing key creation with audited provenance Matthew Garrett
2026-10-08 17:00   ` James Bottomley
2026-10-09  8:31     ` Matthew Garrett
2026-10-08 13:20 ` [PATCH 12/17] tpm: Add signing with the kernel signing key Matthew Garrett
2026-10-08 13:20 ` [PATCH 13/17] tpm: Add verification of kernel signing key provenance Matthew Garrett
2026-10-10  9:34   ` James Bottomley
2026-10-10  9:42   ` James Bottomley
2026-10-08 13:20 ` [PATCH 14/17] PM: hibernate: Add image digest and signature page infrastructure Matthew Garrett
2026-10-08 13:20 ` [PATCH 15/17] PM: hibernate: Sign and verify images with a kernel-generated TPM key Matthew Garrett
2026-10-08 13:20 ` [PATCH 16/17] PM: hibernate: Refuse to verify images with a virtual TPM Matthew Garrett
2026-10-08 13:20 ` [PATCH 17/17] PM: hibernate: Allow hibernation under lockdown with signed images Matthew Garrett
2026-10-08 16:53   ` Jarkko Sakkinen
2026-10-09  8:31     ` Matthew Garrett
2026-10-08 15:56 ` [RFC] Make hibernation work with lockdown Jarkko Sakkinen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=asfDZiuFKCuuRysC@kernel.org \
    --to=jarkko@kernel.org \
    --cc=James.Bottomley@hansenpartnership.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=matthewg@nvidia.com \
    --cc=mjg59@srcf.ucam.org \
    --cc=rafael@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox