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
next prev 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