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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.