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: [RFC] Make hibernation work with lockdown
Date: Sun, 15 Nov 2026 17:08:53 +0200 [thread overview]
Message-ID: <avnLBQwPuF5AFQh2@kernel.org> (raw)
In-Reply-To: <avnJh0nsORR45i4m@kernel.org>
On Sun, Nov 15, 2026 at 05:02:35PM +0200, Jarkko Sakkinen wrote:
> Here's a patch that encapsulates my thoughts.
>
> Br, Jarkko
> From c9a5b8d99278874bdd79d19bddd1d4a56ad8a38a Mon Sep 17 00:00:00 2001
> From: Jarkko Sakkinen <jarkko@kernel.org>
> Date: Sun, 15 Nov 2026 17:00:47 +0200
> Subject: [PATCH] tpm: Deny access to the hibernate NV index
>
> 1. Add the TPM2 command codes mutating the reserved NV index, and describe
> their target handles.
> 2. Replace tpm2_ordinal_duration_map with tpm2_cc_map providing handle
> indices for the commands mutating NV indices.
> 3. Add tpm2_is_user_command() for identifying commands allowed from user
> space. It will block commands trying to mutate the reserve NV index.
>
> NV index should be protected regardless whether hibernate is used or not
> because not doing that introduces unwanted asymmetry.
>
> Co-developed-by: Matthew Garrett <matthewg@nvidia.com>
> Signed-off-by: Matthew Garrett <matthewg@nvidia.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> drivers/char/tpm/tpm-dev-common.c | 4 ++
> drivers/char/tpm/tpm.h | 1 +
> drivers/char/tpm/tpm2-cmd.c | 96 +++++++++++++++++++++++--------
> include/linux/tpm.h | 6 ++
> include/linux/tpm_command.h | 9 +++
> 5 files changed, 91 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
> index ee91f456f975..4394b8f59012 100644
> --- a/drivers/char/tpm/tpm-dev-common.c
> +++ b/drivers/char/tpm/tpm-dev-common.c
> @@ -27,6 +27,10 @@ static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
> struct tpm_header *header = (void *)buf;
> ssize_t ret, len;
>
> + if ((chip->flags & TPM_CHIP_FLAG_TPM2) &&
> + !tpm2_is_user_command(buf, bufsiz))
> + return -EPERM;
> +
> if (chip->flags & TPM_CHIP_FLAG_TPM2)
> tpm2_end_auth_session(chip);
>
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index fa554c5ad80b..2b18acaae920 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -107,6 +107,7 @@ static inline void tpm_add_ppi(struct tpm_chip *chip)
> }
> #endif
>
> +bool tpm2_is_user_command(const u8 *buf, size_t bufsiz);
> int tpm2_get_timeouts(struct tpm_chip *chip);
> int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> struct tpm_digest *digest, u16 *digest_size_ptr);
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 4b2bb2ddc321..50c7ada9a4d6 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -51,29 +51,43 @@ int tpm2_get_timeouts(struct tpm_chip *chip)
> return 0;
> }
>
> -/*
> - * Contains the maximum durations in milliseconds for TPM2 commands.
> - */
> +enum tpm2_cc_nv {
> + TPM2_CC_NV_NA = -1, /* not available */
> + TPM2_CC_NV_H0 = 0,
> + TPM2_CC_NV_H1 = 1,
> +};
> +
> +/* Command durations in milliseconds and NV index handle positions. */
> static const struct {
> - unsigned long ordinal;
> - unsigned long duration;
> -} tpm2_ordinal_duration_map[] = {
> - {TPM2_CC_STARTUP, 750},
> - {TPM2_CC_SELF_TEST, 3000},
> - {TPM2_CC_GET_RANDOM, 2000},
> - {TPM2_CC_SEQUENCE_UPDATE, 750},
> - {TPM2_CC_SEQUENCE_COMPLETE, 750},
> - {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
> - {TPM2_CC_HASH_SEQUENCE_START, 750},
> - {TPM2_CC_VERIFY_SIGNATURE, 30000},
> - {TPM2_CC_PCR_EXTEND, 750},
> - {TPM2_CC_HIERARCHY_CONTROL, 2000},
> - {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
> - {TPM2_CC_GET_CAPABILITY, 750},
> - {TPM2_CC_NV_READ, 2000},
> - {TPM2_CC_CREATE_PRIMARY, 300000},
> - {TPM2_CC_CREATE, 300000},
> - {TPM2_CC_CREATE_LOADED, 300000},
> + u32 cc;
> + u32 duration;
> + enum tpm2_cc_nv nv;
> +} tpm2_cc_map[] = {
> + {TPM2_CC_STARTUP, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_SELF_TEST, 3000, TPM2_CC_NV_NA},
> + {TPM2_CC_GET_RANDOM, 2000, TPM2_CC_NV_NA},
> + {TPM2_CC_SEQUENCE_UPDATE, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_SEQUENCE_COMPLETE, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_HASH_SEQUENCE_START, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_VERIFY_SIGNATURE, 30000, TPM2_CC_NV_NA},
> + {TPM2_CC_PCR_EXTEND, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_HIERARCHY_CONTROL, 2000, TPM2_CC_NV_NA},
> + {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000, TPM2_CC_NV_NA},
> + {TPM2_CC_GET_CAPABILITY, 750, TPM2_CC_NV_NA},
> + {TPM2_CC_NV_READ, 2000, TPM2_CC_NV_NA},
> + {TPM2_CC_CREATE_PRIMARY, 300000, TPM2_CC_NV_NA},
> + {TPM2_CC_CREATE, 300000, TPM2_CC_NV_NA},
> + {TPM2_CC_CREATE_LOADED, 300000, TPM2_CC_NV_NA},
> + {TPM2_CC_NV_UNDEFINE_SPACE_SPECIAL, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H0},
> + {TPM2_CC_NV_UNDEFINE_SPACE, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_INCREMENT, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_SET_BITS, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_EXTEND, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_WRITE, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_WRITE_LOCK, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> + {TPM2_CC_NV_CHANGE_AUTH, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H0},
> + {TPM2_CC_NV_READ_LOCK, TPM2_DURATION_DEFAULT, TPM2_CC_NV_H1},
> };
>
> /**
> @@ -87,13 +101,45 @@ unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
> {
> int i;
>
> - for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
> - if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
> - return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
> + for (i = 0; i < ARRAY_SIZE(tpm2_cc_map); i++)
> + if (ordinal == tpm2_cc_map[i].cc)
> + return msecs_to_jiffies(tpm2_cc_map[i].duration);
>
> return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> }
>
> +/* Check whether a command is permitted through the userspace interface. */
> +bool tpm2_is_user_command(const u8 *buf, size_t bufsiz)
> +{
> + const struct tpm_header *header = (const void *)buf;
> + size_t len, offset;
> + u32 cc, index;
> + int i;
> +
> + if (bufsiz < TPM_HEADER_SIZE)
> + return true;
> +
> + len = min_t(size_t, be32_to_cpu(header->length), bufsiz);
> + if (len < TPM_HEADER_SIZE)
> + return true;
> +
> + cc = be32_to_cpu(header->ordinal);
> +
> + for (i = 0; i < ARRAY_SIZE(tpm2_cc_map); i++) {
> + if (tpm2_cc_map[i].cc != cc || tpm2_cc_map[i].nv == TPM2_CC_NV_NA)
> + continue;
> +
> + offset = TPM_HEADER_SIZE + tpm2_cc_map[i].nv * sizeof(u32);
> + if (len < offset + sizeof(u32))
> + return true;
> +
> + index = get_unaligned_be32(&buf[offset]);
> + return index != TPM2_KERNEL_NV_INDEX;
> + }
> +
> + return true;
> +}
> +
> /**
> * tpm2_pcr_read() - read a PCR value
> * @chip: TPM chip to use.
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 0db277af45c3..2a114c0ffcf9 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;
> }
>
> +/*
> + * Provisional kernel-owned NV index. Userspace is not permitted to undefine
> + * or modify it. The allocation has not yet been finalized.
> + */
> +#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 9ea1ee3cbef1..9224e81cb8f2 100644
> --- a/include/linux/tpm_command.h
> +++ b/include/linux/tpm_command.h
> @@ -208,14 +208,23 @@ 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_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.47.3
>
The additional benefit of this: we can create a kselftest if we want.
Br, Jarkko
prev parent reply other threads:[~2026-11-15 15:08 UTC|newest]
Thread overview: 58+ 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
2026-10-09 8:33 ` Matthew Garrett
2026-10-10 21:36 ` Jarkko Sakkinen
2026-10-08 17:06 ` Ilias Apalodimas
2026-10-10 9:22 ` James Bottomley
2026-10-10 21:26 ` Matthew Garrett
2026-10-11 6:04 ` James Bottomley
2026-10-11 10:23 ` Matthew Garrett
2026-10-11 10:27 ` James Bottomley
2026-10-11 11:28 ` Matthew Garrett
2026-11-15 15:15 ` Jarkko Sakkinen
2026-10-11 8:21 ` James Bottomley
2026-11-13 0:40 ` Jarkko Sakkinen
2026-11-13 0:45 ` Jarkko Sakkinen
2026-11-13 0:50 ` Jarkko Sakkinen
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-11-13 2:17 ` Jarkko Sakkinen
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 21:18 ` Matthew Garrett
2026-10-10 9:42 ` James Bottomley
2026-10-10 21:20 ` Matthew Garrett
2026-11-13 2:25 ` Jarkko Sakkinen
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-10 21:29 ` Jarkko Sakkinen
2026-10-08 15:56 ` [RFC] Make hibernation work with lockdown Jarkko Sakkinen
2026-10-23 15:28 ` Jarkko Sakkinen
2026-10-23 19:13 ` Jarkko Sakkinen
2026-10-31 3:01 ` Jarkko Sakkinen
2026-11-07 22:34 ` Neal Gompa
2026-11-10 1:36 ` Jarkko Sakkinen
2026-11-13 0:06 ` Jarkko Sakkinen
2026-11-15 15:02 ` Jarkko Sakkinen
2026-11-15 15:08 ` Jarkko Sakkinen [this message]
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=avnLBQwPuF5AFQh2@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