* [PATCH v3] tpm2-sessions: address out-of-range indexing
@ 2025-12-01 19:39 Jarkko Sakkinen
2025-12-02 16:26 ` Jonathan McDowell
0 siblings, 1 reply; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-01 19:39 UTC (permalink / raw)
To: linux-integrity
Cc: Jonathan McDowell, Stefano Garzarella, Jarkko Sakkinen, stable,
Peter Huewe, Jason Gunthorpe, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
Ard Biesheuvel, linux-kernel, keyrings, linux-security-module
'name_size' does not have any range checks, and it just directly indexes
with TPM_ALG_ID, which could lead into memory corruption at worst.
Address the issue by only processing known values and returning -EINVAL for
unrecognized values.
Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
that errors are detected before causing any spurious TPM traffic.
End also the authorization session on failure in both of the functions, as
the session state would be then by definition corrupted.
Cc: stable@vger.kernel.org # v6.10+
Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Add two missing 'tpm2_end_auth_session' calls to the fallback paths of
'tpm_buf_fill_hmac_session'.
- Rewrote the commit message.
- End authorization session on failure in 'tpm2_buf_append_name' and
'tpm_buf_fill_hmac_session'.
v2:
There was spurious extra field added to tpm2_hash by mistake.
---
drivers/char/tpm/tpm2-cmd.c | 23 +++-
drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
include/linux/tpm.h | 6 +-
security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
4 files changed, 136 insertions(+), 53 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5b6ccf901623..4473b81122e8 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
@@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
- if (!disable_pcr_integrity)
- tpm_buf_fill_hmac_session(chip, &buf);
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ 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);
@@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
tpm_buf_append_u16(&buf, num_bytes);
- tpm_buf_fill_hmac_session(chip, &buf);
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
+
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 6d03c224e6b2..33ad0d668e1a 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -144,16 +144,24 @@ struct tpm2_auth {
/*
* Name Size based on TPM algorithm (assumes no hash bigger than 255)
*/
-static u8 name_size(const u8 *name)
+static int name_size(const u8 *name)
{
- static u8 size_map[] = {
- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
- };
- u16 alg = get_unaligned_be16(name);
- return size_map[alg] + 2;
+ 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;
+ case TPM_ALG_SM3_256:
+ return SM3256_DIGEST_SIZE + 2;
+ }
+
+ return -EINVAL;
}
static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
@@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset = TPM_HEADER_SIZE;
u32 tot_len = be32_to_cpu(head->length);
+ int ret;
u32 val;
/* we're starting after the header so adjust the length */
@@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
return -EINVAL;
offset += val;
/* name */
+
val = tpm_buf_read_u16(buf, &offset);
- if (val != name_size(&buf->data[offset]))
+ ret = name_size(&buf->data[offset]);
+ if (ret < 0)
+ return ret;
+
+ if (val != ret)
return -EINVAL;
+
memcpy(name, &buf->data[offset], val);
/* forget the rest */
return 0;
@@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
+ *
+ * Ends the authorization session on failure.
*/
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
int slot;
+ int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
- return;
+ return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "TPM: too many handles\n");
- return;
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
}
auth = chip->auth;
- WARN(auth->session != tpm_buf_length(buf),
- "name added in wrong place\n");
+ if (auth->session != tpm_buf_length(buf)) {
+ dev_err(&chip->dev, "session state malformed");
+ ret = -EIO;
+ goto err;
+ }
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- if (!name)
- tpm2_read_public(chip, handle, auth->name[slot]);
+ if (!name) {
+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
+ if (ret)
+ goto err;
+ }
} else {
- if (name)
- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
+ if (name) {
+ ret = -EIO;
+ goto err;
+ }
}
auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size(name));
+ if (name) {
+ ret = name_size(name);
+ if (ret < 0)
+ goto err;
+
+ memcpy(auth->name[slot], name, ret);
+ }
+#endif
+ return 0;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+err:
+ tpm2_end_auth_session(chip);
+ return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
* encryption key and encrypts the first parameter of the command
* buffer with it.
*
- * As with most tpm_buf operations, success is assumed because failure
- * will be caused by an incorrect programming model and indicated by a
- * kernel message.
+ * Ends the authorization session on failure.
*/
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
@@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
+ int ret;
- if (!auth)
- return;
+ if (!auth) {
+ ret = -EINVAL;
+ goto err;
+ }
/* save the command code in BE format */
auth->ordinal = head->ordinal;
@@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
i = tpm2_find_cc(chip, cc);
if (i < 0) {
- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
- return;
+ ret = -EINVAL;
+ goto err;
}
+
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
@@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
- i);
- return;
+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
+ ret = -EINVAL;
+ goto err;
}
}
/* point offset_s to the start of the sessions */
@@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
offset_s += len;
}
if (offset_s != offset_p) {
- dev_err(&chip->dev, "TPM session length is incorrect\n");
- return;
+ dev_err(&chip->dev, "session length is incorrect\n");
+ ret = -EINVAL;
+ goto err;
}
if (!hmac) {
- dev_err(&chip->dev, "TPM could not find HMAC session\n");
- return;
+ dev_err(&chip->dev, "could not find HMAC session\n");
+ ret = -EINVAL;
+ goto err;
}
/* encrypt before HMAC */
@@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- sha256_update(&sctx, auth->name[i],
- name_size(auth->name[i]));
+ 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]);
@@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
+ return 0;
+
+err:
+ tpm2_end_auth_session(chip);
+ return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 0e9e043f728c..1a59f0190eb3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
#endif
}
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
+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);
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e165b117bbca..7672a4376dad 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
@@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- 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);
if (rc)
@@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
@@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
- 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, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
@@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
@@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
NULL, 0);
}
- 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, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 1/4] tpm2-sessions: address out-of-range indexing
[not found] <20251201224554.1717104-1-jarkko@kernel.org>
@ 2025-12-01 22:45 ` Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH 4/4] tpm2-sessions: Open code tpm_buf_append_hmac_session() Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH v3] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
2 siblings, 0 replies; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-01 22:45 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, stable, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, Ard Biesheuvel, open list,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
'name_size' does not have any range checks, and it just directly indexes
with TPM_ALG_ID, which could lead into memory corruption at worst.
Address the issue by only processing known values and returning -EINVAL for
unrecognized values.
Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
that errors are detected before causing any spurious TPM traffic.
End also the authorization session on failure in both of the functions, as
the session state would be then by definition corrupted.
Cc: stable@vger.kernel.org # v6.10+
Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-cmd.c | 23 +++-
drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
include/linux/tpm.h | 6 +-
security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
4 files changed, 136 insertions(+), 53 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5b6ccf901623..4473b81122e8 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
@@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
- if (!disable_pcr_integrity)
- tpm_buf_fill_hmac_session(chip, &buf);
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ 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);
@@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
tpm_buf_append_u16(&buf, num_bytes);
- tpm_buf_fill_hmac_session(chip, &buf);
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
+
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 6d03c224e6b2..33ad0d668e1a 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -144,16 +144,24 @@ struct tpm2_auth {
/*
* Name Size based on TPM algorithm (assumes no hash bigger than 255)
*/
-static u8 name_size(const u8 *name)
+static int name_size(const u8 *name)
{
- static u8 size_map[] = {
- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
- };
- u16 alg = get_unaligned_be16(name);
- return size_map[alg] + 2;
+ 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;
+ case TPM_ALG_SM3_256:
+ return SM3256_DIGEST_SIZE + 2;
+ }
+
+ return -EINVAL;
}
static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
@@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset = TPM_HEADER_SIZE;
u32 tot_len = be32_to_cpu(head->length);
+ int ret;
u32 val;
/* we're starting after the header so adjust the length */
@@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
return -EINVAL;
offset += val;
/* name */
+
val = tpm_buf_read_u16(buf, &offset);
- if (val != name_size(&buf->data[offset]))
+ ret = name_size(&buf->data[offset]);
+ if (ret < 0)
+ return ret;
+
+ if (val != ret)
return -EINVAL;
+
memcpy(name, &buf->data[offset], val);
/* forget the rest */
return 0;
@@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
+ *
+ * Ends the authorization session on failure.
*/
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
int slot;
+ int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
- return;
+ return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "TPM: too many handles\n");
- return;
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
}
auth = chip->auth;
- WARN(auth->session != tpm_buf_length(buf),
- "name added in wrong place\n");
+ if (auth->session != tpm_buf_length(buf)) {
+ dev_err(&chip->dev, "session state malformed");
+ ret = -EIO;
+ goto err;
+ }
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- if (!name)
- tpm2_read_public(chip, handle, auth->name[slot]);
+ if (!name) {
+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
+ if (ret)
+ goto err;
+ }
} else {
- if (name)
- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
+ if (name) {
+ ret = -EIO;
+ goto err;
+ }
}
auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size(name));
+ if (name) {
+ ret = name_size(name);
+ if (ret < 0)
+ goto err;
+
+ memcpy(auth->name[slot], name, ret);
+ }
+#endif
+ return 0;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+err:
+ tpm2_end_auth_session(chip);
+ return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
* encryption key and encrypts the first parameter of the command
* buffer with it.
*
- * As with most tpm_buf operations, success is assumed because failure
- * will be caused by an incorrect programming model and indicated by a
- * kernel message.
+ * Ends the authorization session on failure.
*/
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
@@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
+ int ret;
- if (!auth)
- return;
+ if (!auth) {
+ ret = -EINVAL;
+ goto err;
+ }
/* save the command code in BE format */
auth->ordinal = head->ordinal;
@@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
i = tpm2_find_cc(chip, cc);
if (i < 0) {
- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
- return;
+ ret = -EINVAL;
+ goto err;
}
+
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
@@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
- i);
- return;
+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
+ ret = -EINVAL;
+ goto err;
}
}
/* point offset_s to the start of the sessions */
@@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
offset_s += len;
}
if (offset_s != offset_p) {
- dev_err(&chip->dev, "TPM session length is incorrect\n");
- return;
+ dev_err(&chip->dev, "session length is incorrect\n");
+ ret = -EINVAL;
+ goto err;
}
if (!hmac) {
- dev_err(&chip->dev, "TPM could not find HMAC session\n");
- return;
+ dev_err(&chip->dev, "could not find HMAC session\n");
+ ret = -EINVAL;
+ goto err;
}
/* encrypt before HMAC */
@@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- sha256_update(&sctx, auth->name[i],
- name_size(auth->name[i]));
+ 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]);
@@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
+ return 0;
+
+err:
+ tpm2_end_auth_session(chip);
+ return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 0e9e043f728c..1a59f0190eb3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
#endif
}
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
+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);
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e165b117bbca..7672a4376dad 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
@@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- 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);
if (rc)
@@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
@@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
- 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, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
@@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
@@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
NULL, 0);
}
- 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, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/4] tpm2-sessions: Open code tpm_buf_append_hmac_session()
[not found] <20251201224554.1717104-1-jarkko@kernel.org>
2025-12-01 22:45 ` [PATCH 1/4] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
@ 2025-12-01 22:45 ` Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH v3] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
2 siblings, 0 replies; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-01 22:45 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
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Open code 'tpm_buf_append_hmac_session_opt' to the call site, as it only
masks a call sequence and does otherwise nothing particularly useful.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
drivers/char/tpm/tpm2-cmd.c | 14 +++++++++++---
include/linux/tpm.h | 23 -----------------------
security/keys/trusted-keys/trusted_tpm2.c | 12 ++++++++++--
3 files changed, 21 insertions(+), 28 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index f1e9c35f13a2..94cb887cb4a9 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -270,9 +270,17 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
do {
tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
- tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT
- | TPM2_SA_CONTINUE_SESSION,
- NULL, 0);
+ if (tpm2_chip_auth(chip)) {
+ 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)
+ 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) {
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e3a6e2fb41a7..a93df0efebe4 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -535,29 +535,6 @@ void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
int passphraselen);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 *passphrase, int passphraselen);
-static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
- struct tpm_buf *buf,
- u8 attributes,
- u8 *passphrase,
- int passphraselen)
-{
- struct tpm_header *head;
- int offset;
-
- if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, buf, attributes, passphrase, passphraselen);
- } else {
- offset = buf->handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf->data;
-
- /*
- * If the only sessions are optional, the command tag must change to
- * TPM2_ST_NO_SESSIONS.
- */
- if (tpm_buf_length(buf) == offset)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
-}
#ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 7672a4376dad..e6b95111ac7d 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -494,8 +494,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
+ struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
+ int offset;
u8 *data;
int rc;
@@ -532,8 +534,14 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm2_buf_append_auth(&buf, options->policyhandle,
NULL /* nonce */, 0, 0,
options->blobauth, options->blobauth_len);
- tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT,
- NULL, 0);
+ if (tpm2_chip_auth(chip)) {
+ 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)
+ head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ }
}
rc = tpm_buf_fill_hmac_session(chip, &buf);
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3] tpm2-sessions: address out-of-range indexing
[not found] <20251201224554.1717104-1-jarkko@kernel.org>
2025-12-01 22:45 ` [PATCH 1/4] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH 4/4] tpm2-sessions: Open code tpm_buf_append_hmac_session() Jarkko Sakkinen
@ 2025-12-01 22:45 ` Jarkko Sakkinen
2 siblings, 0 replies; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-01 22:45 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, stable, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, Ard Biesheuvel, open list,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
'name_size' does not have any range checks, and it just directly indexes
with TPM_ALG_ID, which could lead into memory corruption at worst.
Address the issue by only processing known values and returning -EINVAL for
unrecognized values.
Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
that errors are detected before causing any spurious TPM traffic.
End also the authorization session on failure in both of the functions, as
the session state would be then by definition corrupted.
Cc: stable@vger.kernel.org # v6.10+
Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Add two missing 'tpm2_end_auth_session' calls to the fallback paths of
'tpm_buf_fill_hmac_session'.
- Rewrote the commit message.
- End authorization session on failure in 'tpm2_buf_append_name' and
'tpm_buf_fill_hmac_session'.
v2:
There was spurious extra field added to tpm2_hash by mistake.
---
drivers/char/tpm/tpm2-cmd.c | 23 +++-
drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
include/linux/tpm.h | 6 +-
security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
4 files changed, 136 insertions(+), 53 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5b6ccf901623..4473b81122e8 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
tpm_buf_append_handle(chip, &buf, pcr_idx);
@@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
- if (!disable_pcr_integrity)
- tpm_buf_fill_hmac_session(chip, &buf);
+ if (!disable_pcr_integrity) {
+ rc = tpm_buf_fill_hmac_session(chip, &buf);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ 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);
@@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
tpm_buf_append_u16(&buf, num_bytes);
- tpm_buf_fill_hmac_session(chip, &buf);
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
+
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 6d03c224e6b2..33ad0d668e1a 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -144,16 +144,24 @@ struct tpm2_auth {
/*
* Name Size based on TPM algorithm (assumes no hash bigger than 255)
*/
-static u8 name_size(const u8 *name)
+static int name_size(const u8 *name)
{
- static u8 size_map[] = {
- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
- };
- u16 alg = get_unaligned_be16(name);
- return size_map[alg] + 2;
+ 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;
+ case TPM_ALG_SM3_256:
+ return SM3256_DIGEST_SIZE + 2;
+ }
+
+ return -EINVAL;
}
static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
@@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset = TPM_HEADER_SIZE;
u32 tot_len = be32_to_cpu(head->length);
+ int ret;
u32 val;
/* we're starting after the header so adjust the length */
@@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
return -EINVAL;
offset += val;
/* name */
+
val = tpm_buf_read_u16(buf, &offset);
- if (val != name_size(&buf->data[offset]))
+ ret = name_size(&buf->data[offset]);
+ if (ret < 0)
+ return ret;
+
+ if (val != ret)
return -EINVAL;
+
memcpy(name, &buf->data[offset], val);
/* forget the rest */
return 0;
@@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
* As with most tpm_buf operations, success is assumed because failure
* will be caused by an incorrect programming model and indicated by a
* kernel message.
+ *
+ * Ends the authorization session on failure.
*/
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
int slot;
+ int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
- return;
+ return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "TPM: too many handles\n");
- return;
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
}
auth = chip->auth;
- WARN(auth->session != tpm_buf_length(buf),
- "name added in wrong place\n");
+ if (auth->session != tpm_buf_length(buf)) {
+ dev_err(&chip->dev, "session state malformed");
+ ret = -EIO;
+ goto err;
+ }
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- if (!name)
- tpm2_read_public(chip, handle, auth->name[slot]);
+ if (!name) {
+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
+ if (ret)
+ goto err;
+ }
} else {
- if (name)
- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
+ if (name) {
+ ret = -EIO;
+ goto err;
+ }
}
auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size(name));
+ if (name) {
+ ret = name_size(name);
+ if (ret < 0)
+ goto err;
+
+ memcpy(auth->name[slot], name, ret);
+ }
+#endif
+ return 0;
+
+#ifdef CONFIG_TCG_TPM2_HMAC
+err:
+ tpm2_end_auth_session(chip);
+ return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
* encryption key and encrypts the first parameter of the command
* buffer with it.
*
- * As with most tpm_buf operations, success is assumed because failure
- * will be caused by an incorrect programming model and indicated by a
- * kernel message.
+ * Ends the authorization session on failure.
*/
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
@@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
+ int ret;
- if (!auth)
- return;
+ if (!auth) {
+ ret = -EINVAL;
+ goto err;
+ }
/* save the command code in BE format */
auth->ordinal = head->ordinal;
@@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
i = tpm2_find_cc(chip, cc);
if (i < 0) {
- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
- return;
+ ret = -EINVAL;
+ goto err;
}
+
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
@@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
- i);
- return;
+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
+ ret = -EINVAL;
+ goto err;
}
}
/* point offset_s to the start of the sessions */
@@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
offset_s += len;
}
if (offset_s != offset_p) {
- dev_err(&chip->dev, "TPM session length is incorrect\n");
- return;
+ dev_err(&chip->dev, "session length is incorrect\n");
+ ret = -EINVAL;
+ goto err;
}
if (!hmac) {
- dev_err(&chip->dev, "TPM could not find HMAC session\n");
- return;
+ dev_err(&chip->dev, "could not find HMAC session\n");
+ ret = -EINVAL;
+ goto err;
}
/* encrypt before HMAC */
@@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
- sha256_update(&sctx, auth->name[i],
- name_size(auth->name[i]));
+ 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]);
@@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
+ return 0;
+
+err:
+ tpm2_end_auth_session(chip);
+ return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 0e9e043f728c..1a59f0190eb3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
#endif
}
-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
+ u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
+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);
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index e165b117bbca..7672a4376dad 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
@@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- 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);
if (rc)
@@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
+
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
@@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
goto out;
}
- 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, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
@@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ if (rc)
+ goto out;
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
@@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
NULL, 0);
}
- 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, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
--
2.52.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3] tpm2-sessions: address out-of-range indexing
2025-12-01 19:39 Jarkko Sakkinen
@ 2025-12-02 16:26 ` Jonathan McDowell
2025-12-02 19:53 ` Jarkko Sakkinen
0 siblings, 1 reply; 7+ messages in thread
From: Jonathan McDowell @ 2025-12-02 16:26 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, Stefano Garzarella, stable, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, Ard Biesheuvel,
linux-kernel, keyrings, linux-security-module
On Mon, Dec 01, 2025 at 09:39:58PM +0200, Jarkko Sakkinen wrote:
>'name_size' does not have any range checks, and it just directly indexes
>with TPM_ALG_ID, which could lead into memory corruption at worst.
>
>Address the issue by only processing known values and returning -EINVAL for
>unrecognized values.
>
>Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
>that errors are detected before causing any spurious TPM traffic.
>
>End also the authorization session on failure in both of the functions, as
>the session state would be then by definition corrupted.
>
>Cc: stable@vger.kernel.org # v6.10+
>Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
>Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>---
>v3:
>- Add two missing 'tpm2_end_auth_session' calls to the fallback paths of
> 'tpm_buf_fill_hmac_session'.
>- Rewrote the commit message.
>- End authorization session on failure in 'tpm2_buf_append_name' and
> 'tpm_buf_fill_hmac_session'.
>v2:
>There was spurious extra field added to tpm2_hash by mistake.
>---
> drivers/char/tpm/tpm2-cmd.c | 23 +++-
> drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
> include/linux/tpm.h | 6 +-
> security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
> 4 files changed, 136 insertions(+), 53 deletions(-)
>
>diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
>index 5b6ccf901623..4473b81122e8 100644
>--- a/drivers/char/tpm/tpm2-cmd.c
>+++ b/drivers/char/tpm/tpm2-cmd.c
>@@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> }
>
> if (!disable_pcr_integrity) {
>- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
>+ if (rc) {
>+ tpm_buf_destroy(&buf);
>+ return rc;
>+ }
> tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> } else {
> tpm_buf_append_handle(chip, &buf, pcr_idx);
>@@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> chip->allocated_banks[i].digest_size);
> }
>
>- if (!disable_pcr_integrity)
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ if (!disable_pcr_integrity) {
>+ rc = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (rc) {
>+ tpm_buf_destroy(&buf);
>+ 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);
>@@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> | TPM2_SA_CONTINUE_SESSION,
> NULL, 0);
> tpm_buf_append_u16(&buf, num_bytes);
>- tpm_buf_fill_hmac_session(chip, &buf);
>+ err = tpm_buf_fill_hmac_session(chip, &buf);
>+ if (err) {
>+ tpm_buf_destroy(&buf);
>+ return err;
>+ }
>+
> err = tpm_transmit_cmd(chip, &buf,
> offsetof(struct tpm2_get_random_out,
> buffer),
>diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
>index 6d03c224e6b2..33ad0d668e1a 100644
>--- a/drivers/char/tpm/tpm2-sessions.c
>+++ b/drivers/char/tpm/tpm2-sessions.c
>@@ -144,16 +144,24 @@ struct tpm2_auth {
> /*
> * Name Size based on TPM algorithm (assumes no hash bigger than 255)
> */
>-static u8 name_size(const u8 *name)
>+static int name_size(const u8 *name)
> {
>- static u8 size_map[] = {
>- [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
>- [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
>- [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
>- [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
>- };
>- u16 alg = get_unaligned_be16(name);
>- return size_map[alg] + 2;
>+ 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;
>+ case TPM_ALG_SM3_256:
>+ return SM3256_DIGEST_SIZE + 2;
>+ }
>+
Can we/should we perhaps print a warning here if we don't know the
algorithm?
>+ return -EINVAL;
> }
>
> static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
>@@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> struct tpm_header *head = (struct tpm_header *)buf->data;
> off_t offset = TPM_HEADER_SIZE;
> u32 tot_len = be32_to_cpu(head->length);
>+ int ret;
> u32 val;
>
> /* we're starting after the header so adjust the length */
>@@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> return -EINVAL;
> offset += val;
> /* name */
>+
> val = tpm_buf_read_u16(buf, &offset);
>- if (val != name_size(&buf->data[offset]))
>+ ret = name_size(&buf->data[offset]);
>+ if (ret < 0)
>+ return ret;
>+
>+ if (val != ret)
> return -EINVAL;
>+
> memcpy(name, &buf->data[offset], val);
> /* forget the rest */
> return 0;
>@@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
> * As with most tpm_buf operations, success is assumed because failure
> * will be caused by an incorrect programming model and indicated by a
> * kernel message.
>+ *
>+ * Ends the authorization session on failure.
> */
>-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>- u32 handle, u8 *name)
>+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>+ u32 handle, u8 *name)
> {
> #ifdef CONFIG_TCG_TPM2_HMAC
> enum tpm2_mso_type mso = tpm2_handle_mso(handle);
> struct tpm2_auth *auth;
> int slot;
>+ int ret;
> #endif
>
> if (!tpm2_chip_auth(chip)) {
> tpm_buf_append_handle(chip, buf, handle);
>- return;
>+ return 0;
> }
>
> #ifdef CONFIG_TCG_TPM2_HMAC
> slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
> if (slot >= AUTH_MAX_NAMES) {
>- dev_err(&chip->dev, "TPM: too many handles\n");
>- return;
>+ dev_err(&chip->dev, "too many handles\n");
>+ ret = -EIO;
>+ goto err;
> }
> auth = chip->auth;
>- WARN(auth->session != tpm_buf_length(buf),
>- "name added in wrong place\n");
>+ if (auth->session != tpm_buf_length(buf)) {
>+ dev_err(&chip->dev, "session state malformed");
>+ ret = -EIO;
>+ goto err;
>+ }
> tpm_buf_append_u32(buf, handle);
> auth->session += 4;
>
> if (mso == TPM2_MSO_PERSISTENT ||
> mso == TPM2_MSO_VOLATILE ||
> mso == TPM2_MSO_NVRAM) {
>- if (!name)
>- tpm2_read_public(chip, handle, auth->name[slot]);
>+ if (!name) {
>+ ret = tpm2_read_public(chip, handle, auth->name[slot]);
>+ if (ret)
>+ goto err;
>+ }
> } else {
>- if (name)
>- dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
We're dropping the error message here; is there a reason for that?
>+ if (name) {
>+ ret = -EIO;
>+ goto err;
>+ }
> }
>
> auth->name_h[slot] = handle;
>- if (name)
>- memcpy(auth->name[slot], name, name_size(name));
>+ if (name) {
>+ ret = name_size(name);
>+ if (ret < 0)
>+ goto err;
>+
>+ memcpy(auth->name[slot], name, ret);
>+ }
>+#endif
>+ return 0;
>+
>+#ifdef CONFIG_TCG_TPM2_HMAC
>+err:
>+ tpm2_end_auth_session(chip);
>+ return tpm_ret_to_err(ret);
> #endif
> }
> EXPORT_SYMBOL_GPL(tpm_buf_append_name);
>@@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
> * encryption key and encrypts the first parameter of the command
> * buffer with it.
> *
>- * As with most tpm_buf operations, success is assumed because failure
>- * will be caused by an incorrect programming model and indicated by a
>- * kernel message.
>+ * Ends the authorization session on failure.
> */
>-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
>+int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> {
> u32 cc, handles, val;
> struct tpm2_auth *auth = chip->auth;
>@@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> u8 cphash[SHA256_DIGEST_SIZE];
> struct sha256_ctx sctx;
> struct hmac_sha256_ctx hctx;
>+ int ret;
>
>- if (!auth)
>- return;
>+ if (!auth) {
>+ ret = -EINVAL;
>+ goto err;
>+ }
>
> /* save the command code in BE format */
> auth->ordinal = head->ordinal;
>@@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
>
> i = tpm2_find_cc(chip, cc);
> if (i < 0) {
>- dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
Again, I think it's generally helpful to have the error message given
that the return (EINVAL) does not help narrow down which value is bad.
>- return;
>+ ret = -EINVAL;
>+ goto err;
> }
>+
> attrs = chip->cc_attrs_tbl[i];
>
> handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
>@@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> u32 handle = tpm_buf_read_u32(buf, &offset_s);
>
> if (auth->name_h[i] != handle) {
>- dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
>- i);
>- return;
>+ dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
>+ ret = -EINVAL;
>+ goto err;
> }
> }
> /* point offset_s to the start of the sessions */
>@@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> offset_s += len;
> }
> if (offset_s != offset_p) {
>- dev_err(&chip->dev, "TPM session length is incorrect\n");
>- return;
>+ dev_err(&chip->dev, "session length is incorrect\n");
>+ ret = -EINVAL;
>+ goto err;
> }
> if (!hmac) {
>- dev_err(&chip->dev, "TPM could not find HMAC session\n");
>- return;
>+ dev_err(&chip->dev, "could not find HMAC session\n");
>+ ret = -EINVAL;
>+ goto err;
> }
>
> /* encrypt before HMAC */
>@@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> if (mso == TPM2_MSO_PERSISTENT ||
> mso == TPM2_MSO_VOLATILE ||
> mso == TPM2_MSO_NVRAM) {
>- sha256_update(&sctx, auth->name[i],
>- name_size(auth->name[i]));
>+ 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]);
>
>@@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
> hmac_sha256_update(&hctx, &auth->attrs, 1);
> hmac_sha256_final(&hctx, hmac);
>+ return 0;
>+
>+err:
>+ tpm2_end_auth_session(chip);
>+ return ret;
> }
> EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
>
>diff --git a/include/linux/tpm.h b/include/linux/tpm.h
>index 0e9e043f728c..1a59f0190eb3 100644
>--- a/include/linux/tpm.h
>+++ b/include/linux/tpm.h
>@@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
> #endif
> }
>
>-void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>- u32 handle, u8 *name);
>+int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
>+ u32 handle, u8 *name);
> void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> u8 attributes, u8 *passphrase,
> int passphraselen);
>@@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
> #ifdef CONFIG_TCG_TPM2_HMAC
>
> int tpm2_start_auth_session(struct tpm_chip *chip);
>-void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
>+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);
>diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
>index e165b117bbca..7672a4376dad 100644
>--- a/security/keys/trusted-keys/trusted_tpm2.c
>+++ b/security/keys/trusted-keys/trusted_tpm2.c
>@@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out_put;
> }
>
>- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>+
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> options->keyauth, TPM_DIGEST_SIZE);
>
>@@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out;
> }
>
>- 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);
> if (rc)
>@@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> return rc;
> }
>
>- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>+
> tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> TPM_DIGEST_SIZE);
>
>@@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> goto out;
> }
>
>- 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, "loading blob");
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> if (!rc)
>@@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> return rc;
> }
>
>- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
>+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
>+ if (rc)
>+ goto out;
>
> if (!options->policyhandle) {
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
>@@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> NULL, 0);
> }
>
>- 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, 6, "unsealing");
> rc = tpm_buf_check_hmac_response(chip, &buf, rc);
>
>--
>2.52.0
>
J.
--
Be Ye Not Lost Among Precepts of Order
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3] tpm2-sessions: address out-of-range indexing
2025-12-02 16:26 ` Jonathan McDowell
@ 2025-12-02 19:53 ` Jarkko Sakkinen
2025-12-02 22:07 ` Jarkko Sakkinen
0 siblings, 1 reply; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-02 19:53 UTC (permalink / raw)
To: Jonathan McDowell
Cc: linux-integrity, Stefano Garzarella, stable, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, Ard Biesheuvel,
linux-kernel, keyrings, linux-security-module
On Tue, Dec 02, 2025 at 04:26:09PM +0000, Jonathan McDowell wrote:
> On Mon, Dec 01, 2025 at 09:39:58PM +0200, Jarkko Sakkinen wrote:
> > 'name_size' does not have any range checks, and it just directly indexes
> > with TPM_ALG_ID, which could lead into memory corruption at worst.
> >
> > Address the issue by only processing known values and returning -EINVAL for
> > unrecognized values.
> >
> > Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
> > that errors are detected before causing any spurious TPM traffic.
> >
> > End also the authorization session on failure in both of the functions, as
> > the session state would be then by definition corrupted.
> >
> > Cc: stable@vger.kernel.org # v6.10+
> > Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > ---
> > v3:
> > - Add two missing 'tpm2_end_auth_session' calls to the fallback paths of
> > 'tpm_buf_fill_hmac_session'.
> > - Rewrote the commit message.
> > - End authorization session on failure in 'tpm2_buf_append_name' and
> > 'tpm_buf_fill_hmac_session'.
> > v2:
> > There was spurious extra field added to tpm2_hash by mistake.
> > ---
> > drivers/char/tpm/tpm2-cmd.c | 23 +++-
> > drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
> > include/linux/tpm.h | 6 +-
> > security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
> > 4 files changed, 136 insertions(+), 53 deletions(-)
> >
> > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> > index 5b6ccf901623..4473b81122e8 100644
> > --- a/drivers/char/tpm/tpm2-cmd.c
> > +++ b/drivers/char/tpm/tpm2-cmd.c
> > @@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > }
> >
> > if (!disable_pcr_integrity) {
> > - tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> > + rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> > + if (rc) {
> > + tpm_buf_destroy(&buf);
> > + return rc;
> > + }
> > tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> > } else {
> > tpm_buf_append_handle(chip, &buf, pcr_idx);
> > @@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > chip->allocated_banks[i].digest_size);
> > }
> >
> > - if (!disable_pcr_integrity)
> > - tpm_buf_fill_hmac_session(chip, &buf);
> > + if (!disable_pcr_integrity) {
> > + rc = tpm_buf_fill_hmac_session(chip, &buf);
> > + if (rc) {
> > + tpm_buf_destroy(&buf);
> > + 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);
> > @@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > | TPM2_SA_CONTINUE_SESSION,
> > NULL, 0);
> > tpm_buf_append_u16(&buf, num_bytes);
> > - tpm_buf_fill_hmac_session(chip, &buf);
> > + err = tpm_buf_fill_hmac_session(chip, &buf);
> > + if (err) {
> > + tpm_buf_destroy(&buf);
> > + return err;
> > + }
> > +
> > err = tpm_transmit_cmd(chip, &buf,
> > offsetof(struct tpm2_get_random_out,
> > buffer),
> > diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> > index 6d03c224e6b2..33ad0d668e1a 100644
> > --- a/drivers/char/tpm/tpm2-sessions.c
> > +++ b/drivers/char/tpm/tpm2-sessions.c
> > @@ -144,16 +144,24 @@ struct tpm2_auth {
> > /*
> > * Name Size based on TPM algorithm (assumes no hash bigger than 255)
> > */
> > -static u8 name_size(const u8 *name)
> > +static int name_size(const u8 *name)
> > {
> > - static u8 size_map[] = {
> > - [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
> > - [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
> > - [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
> > - [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
> > - };
> > - u16 alg = get_unaligned_be16(name);
> > - return size_map[alg] + 2;
> > + 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;
> > + case TPM_ALG_SM3_256:
> > + return SM3256_DIGEST_SIZE + 2;
> > + }
> > +
>
> Can we/should we perhaps print a warning here if we don't know the
> algorithm?
I think it is a good idea to do that right now.
Also, it'd be better to not have SM3 label as SM2/SM3 is not supported
at this point. I'm working simulatenously on an improved feature and
that slipped from that work:
My big picture roadmap for this feature, and how to make it useful is:
1. tpm.integrity_mode=disabled/permissive/enforced. Permissive means
here that the feature is conditionally enabled if algorithms that are
required to enable the HMAC pipe are available.
2. tpm.integrity_handle=0x00000000/0x81??????.
>
> > + return -EINVAL;
> > }
> >
> > static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > @@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > struct tpm_header *head = (struct tpm_header *)buf->data;
> > off_t offset = TPM_HEADER_SIZE;
> > u32 tot_len = be32_to_cpu(head->length);
> > + int ret;
> > u32 val;
> >
> > /* we're starting after the header so adjust the length */
> > @@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > return -EINVAL;
> > offset += val;
> > /* name */
> > +
> > val = tpm_buf_read_u16(buf, &offset);
> > - if (val != name_size(&buf->data[offset]))
> > + ret = name_size(&buf->data[offset]);
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (val != ret)
> > return -EINVAL;
> > +
> > memcpy(name, &buf->data[offset], val);
> > /* forget the rest */
> > return 0;
> > @@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
> > * As with most tpm_buf operations, success is assumed because failure
> > * will be caused by an incorrect programming model and indicated by a
> > * kernel message.
> > + *
> > + * Ends the authorization session on failure.
> > */
> > -void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > - u32 handle, u8 *name)
> > +int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > + u32 handle, u8 *name)
> > {
> > #ifdef CONFIG_TCG_TPM2_HMAC
> > enum tpm2_mso_type mso = tpm2_handle_mso(handle);
> > struct tpm2_auth *auth;
> > int slot;
> > + int ret;
> > #endif
> >
> > if (!tpm2_chip_auth(chip)) {
> > tpm_buf_append_handle(chip, buf, handle);
> > - return;
> > + return 0;
> > }
> >
> > #ifdef CONFIG_TCG_TPM2_HMAC
> > slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
> > if (slot >= AUTH_MAX_NAMES) {
> > - dev_err(&chip->dev, "TPM: too many handles\n");
> > - return;
> > + dev_err(&chip->dev, "too many handles\n");
> > + ret = -EIO;
> > + goto err;
> > }
> > auth = chip->auth;
> > - WARN(auth->session != tpm_buf_length(buf),
> > - "name added in wrong place\n");
> > + if (auth->session != tpm_buf_length(buf)) {
> > + dev_err(&chip->dev, "session state malformed");
> > + ret = -EIO;
> > + goto err;
> > + }
> > tpm_buf_append_u32(buf, handle);
> > auth->session += 4;
> >
> > if (mso == TPM2_MSO_PERSISTENT ||
> > mso == TPM2_MSO_VOLATILE ||
> > mso == TPM2_MSO_NVRAM) {
> > - if (!name)
> > - tpm2_read_public(chip, handle, auth->name[slot]);
> > + if (!name) {
> > + ret = tpm2_read_public(chip, handle, auth->name[slot]);
> > + if (ret)
> > + goto err;
> > + }
> > } else {
> > - if (name)
> > - dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
>
> We're dropping the error message here; is there a reason for that?
Thanks, I'll add it back.
>
> > + if (name) {
> > + ret = -EIO;
> > + goto err;
> > + }
> > }
> >
> > auth->name_h[slot] = handle;
> > - if (name)
> > - memcpy(auth->name[slot], name, name_size(name));
> > + if (name) {
> > + ret = name_size(name);
> > + if (ret < 0)
> > + goto err;
> > +
> > + memcpy(auth->name[slot], name, ret);
> > + }
> > +#endif
> > + return 0;
> > +
> > +#ifdef CONFIG_TCG_TPM2_HMAC
> > +err:
> > + tpm2_end_auth_session(chip);
> > + return tpm_ret_to_err(ret);
> > #endif
> > }
> > EXPORT_SYMBOL_GPL(tpm_buf_append_name);
> > @@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
> > * encryption key and encrypts the first parameter of the command
> > * buffer with it.
> > *
> > - * As with most tpm_buf operations, success is assumed because failure
> > - * will be caused by an incorrect programming model and indicated by a
> > - * kernel message.
> > + * Ends the authorization session on failure.
> > */
> > -void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > +int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > {
> > u32 cc, handles, val;
> > struct tpm2_auth *auth = chip->auth;
> > @@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > u8 cphash[SHA256_DIGEST_SIZE];
> > struct sha256_ctx sctx;
> > struct hmac_sha256_ctx hctx;
> > + int ret;
> >
> > - if (!auth)
> > - return;
> > + if (!auth) {
> > + ret = -EINVAL;
> > + goto err;
> > + }
> >
> > /* save the command code in BE format */
> > auth->ordinal = head->ordinal;
> > @@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> >
> > i = tpm2_find_cc(chip, cc);
> > if (i < 0) {
> > - dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
>
> Again, I think it's generally helpful to have the error message given that
> the return (EINVAL) does not help narrow down which value is bad.
Agreed.
>
> > - return;
> > + ret = -EINVAL;
> > + goto err;
> > }
> > +
> > attrs = chip->cc_attrs_tbl[i];
> >
> > handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
> > @@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > u32 handle = tpm_buf_read_u32(buf, &offset_s);
> >
> > if (auth->name_h[i] != handle) {
> > - dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
> > - i);
> > - return;
> > + dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
> > + ret = -EINVAL;
> > + goto err;
> > }
> > }
> > /* point offset_s to the start of the sessions */
> > @@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > offset_s += len;
> > }
> > if (offset_s != offset_p) {
> > - dev_err(&chip->dev, "TPM session length is incorrect\n");
> > - return;
> > + dev_err(&chip->dev, "session length is incorrect\n");
> > + ret = -EINVAL;
> > + goto err;
> > }
> > if (!hmac) {
> > - dev_err(&chip->dev, "TPM could not find HMAC session\n");
> > - return;
> > + dev_err(&chip->dev, "could not find HMAC session\n");
> > + ret = -EINVAL;
> > + goto err;
> > }
> >
> > /* encrypt before HMAC */
> > @@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > if (mso == TPM2_MSO_PERSISTENT ||
> > mso == TPM2_MSO_VOLATILE ||
> > mso == TPM2_MSO_NVRAM) {
> > - sha256_update(&sctx, auth->name[i],
> > - name_size(auth->name[i]));
> > + 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]);
> >
> > @@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
> > hmac_sha256_update(&hctx, &auth->attrs, 1);
> > hmac_sha256_final(&hctx, hmac);
> > + return 0;
> > +
> > +err:
> > + tpm2_end_auth_session(chip);
> > + return ret;
> > }
> > EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
> >
> > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > index 0e9e043f728c..1a59f0190eb3 100644
> > --- a/include/linux/tpm.h
> > +++ b/include/linux/tpm.h
> > @@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
> > #endif
> > }
> >
> > -void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > - u32 handle, u8 *name);
> > +int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > + u32 handle, u8 *name);
> > void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> > u8 attributes, u8 *passphrase,
> > int passphraselen);
> > @@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
> > #ifdef CONFIG_TCG_TPM2_HMAC
> >
> > int tpm2_start_auth_session(struct tpm_chip *chip);
> > -void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
> > +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);
> > diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> > index e165b117bbca..7672a4376dad 100644
> > --- a/security/keys/trusted-keys/trusted_tpm2.c
> > +++ b/security/keys/trusted-keys/trusted_tpm2.c
> > @@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > goto out_put;
> > }
> >
> > - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + if (rc)
> > + goto out;
> > +
> > tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> > options->keyauth, TPM_DIGEST_SIZE);
> >
> > @@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > goto out;
> > }
> >
> > - 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);
> > if (rc)
> > @@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > return rc;
> > }
> >
> > - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + if (rc)
> > + goto out;
> > +
> > tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> > TPM_DIGEST_SIZE);
> >
> > @@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > goto out;
> > }
> >
> > - 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, "loading blob");
> > rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > if (!rc)
> > @@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > return rc;
> > }
> >
> > - tpm_buf_append_name(chip, &buf, blob_handle, NULL);
> > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > + if (rc)
> > + goto out;
> >
> > if (!options->policyhandle) {
> > tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> > @@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > NULL, 0);
> > }
> >
> > - 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, 6, "unsealing");
> > rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> >
> > --
> > 2.52.0
> >
>
> J.
>
> --
> Be Ye Not Lost Among Precepts of Order
BR, Jarkko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3] tpm2-sessions: address out-of-range indexing
2025-12-02 19:53 ` Jarkko Sakkinen
@ 2025-12-02 22:07 ` Jarkko Sakkinen
0 siblings, 0 replies; 7+ messages in thread
From: Jarkko Sakkinen @ 2025-12-02 22:07 UTC (permalink / raw)
To: Jonathan McDowell
Cc: linux-integrity, Stefano Garzarella, stable, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, Ard Biesheuvel,
linux-kernel, keyrings, linux-security-module
On Tue, Dec 02, 2025 at 09:53:39PM +0200, Jarkko Sakkinen wrote:
> On Tue, Dec 02, 2025 at 04:26:09PM +0000, Jonathan McDowell wrote:
> > On Mon, Dec 01, 2025 at 09:39:58PM +0200, Jarkko Sakkinen wrote:
> > > 'name_size' does not have any range checks, and it just directly indexes
> > > with TPM_ALG_ID, which could lead into memory corruption at worst.
> > >
> > > Address the issue by only processing known values and returning -EINVAL for
> > > unrecognized values.
> > >
> > > Make also 'tpm_buf_append_name' and 'tpm_buf_fill_hmac_session' fallible so
> > > that errors are detected before causing any spurious TPM traffic.
> > >
> > > End also the authorization session on failure in both of the functions, as
> > > the session state would be then by definition corrupted.
> > >
> > > Cc: stable@vger.kernel.org # v6.10+
> > > Fixes: 1085b8276bb4 ("tpm: Add the rest of the session HMAC API")
> > > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> > > ---
> > > v3:
> > > - Add two missing 'tpm2_end_auth_session' calls to the fallback paths of
> > > 'tpm_buf_fill_hmac_session'.
> > > - Rewrote the commit message.
> > > - End authorization session on failure in 'tpm2_buf_append_name' and
> > > 'tpm_buf_fill_hmac_session'.
> > > v2:
> > > There was spurious extra field added to tpm2_hash by mistake.
> > > ---
> > > drivers/char/tpm/tpm2-cmd.c | 23 +++-
> > > drivers/char/tpm/tpm2-sessions.c | 131 +++++++++++++++-------
> > > include/linux/tpm.h | 6 +-
> > > security/keys/trusted-keys/trusted_tpm2.c | 29 ++++-
> > > 4 files changed, 136 insertions(+), 53 deletions(-)
> > >
> > > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> > > index 5b6ccf901623..4473b81122e8 100644
> > > --- a/drivers/char/tpm/tpm2-cmd.c
> > > +++ b/drivers/char/tpm/tpm2-cmd.c
> > > @@ -187,7 +187,11 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > > }
> > >
> > > if (!disable_pcr_integrity) {
> > > - tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> > > + rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
> > > + if (rc) {
> > > + tpm_buf_destroy(&buf);
> > > + return rc;
> > > + }
> > > tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
> > > } else {
> > > tpm_buf_append_handle(chip, &buf, pcr_idx);
> > > @@ -202,8 +206,14 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> > > chip->allocated_banks[i].digest_size);
> > > }
> > >
> > > - if (!disable_pcr_integrity)
> > > - tpm_buf_fill_hmac_session(chip, &buf);
> > > + if (!disable_pcr_integrity) {
> > > + rc = tpm_buf_fill_hmac_session(chip, &buf);
> > > + if (rc) {
> > > + tpm_buf_destroy(&buf);
> > > + 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);
> > > @@ -261,7 +271,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> > > | TPM2_SA_CONTINUE_SESSION,
> > > NULL, 0);
> > > tpm_buf_append_u16(&buf, num_bytes);
> > > - tpm_buf_fill_hmac_session(chip, &buf);
> > > + err = tpm_buf_fill_hmac_session(chip, &buf);
> > > + if (err) {
> > > + tpm_buf_destroy(&buf);
> > > + return err;
> > > + }
> > > +
> > > err = tpm_transmit_cmd(chip, &buf,
> > > offsetof(struct tpm2_get_random_out,
> > > buffer),
> > > diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
> > > index 6d03c224e6b2..33ad0d668e1a 100644
> > > --- a/drivers/char/tpm/tpm2-sessions.c
> > > +++ b/drivers/char/tpm/tpm2-sessions.c
> > > @@ -144,16 +144,24 @@ struct tpm2_auth {
> > > /*
> > > * Name Size based on TPM algorithm (assumes no hash bigger than 255)
> > > */
> > > -static u8 name_size(const u8 *name)
> > > +static int name_size(const u8 *name)
> > > {
> > > - static u8 size_map[] = {
> > > - [TPM_ALG_SHA1] = SHA1_DIGEST_SIZE,
> > > - [TPM_ALG_SHA256] = SHA256_DIGEST_SIZE,
> > > - [TPM_ALG_SHA384] = SHA384_DIGEST_SIZE,
> > > - [TPM_ALG_SHA512] = SHA512_DIGEST_SIZE,
> > > - };
> > > - u16 alg = get_unaligned_be16(name);
> > > - return size_map[alg] + 2;
> > > + 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;
> > > + case TPM_ALG_SM3_256:
> > > + return SM3256_DIGEST_SIZE + 2;
> > > + }
> > > +
> >
> > Can we/should we perhaps print a warning here if we don't know the
> > algorithm?
>
> I think it is a good idea to do that right now.
>
> Also, it'd be better to not have SM3 label as SM2/SM3 is not supported
> at this point. I'm working simulatenously on an improved feature and
> that slipped from that work:
>
> My big picture roadmap for this feature, and how to make it useful is:
>
> 1. tpm.integrity_mode=disabled/permissive/enforced. Permissive means
> here that the feature is conditionally enabled if algorithms that are
> required to enable the HMAC pipe are available.
> 2. tpm.integrity_handle=0x00000000/0x81??????.
>
> >
> > > + return -EINVAL;
> > > }
> > >
> > > static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > > @@ -161,6 +169,7 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > > struct tpm_header *head = (struct tpm_header *)buf->data;
> > > off_t offset = TPM_HEADER_SIZE;
> > > u32 tot_len = be32_to_cpu(head->length);
> > > + int ret;
> > > u32 val;
> > >
> > > /* we're starting after the header so adjust the length */
> > > @@ -172,9 +181,15 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
> > > return -EINVAL;
> > > offset += val;
> > > /* name */
> > > +
> > > val = tpm_buf_read_u16(buf, &offset);
> > > - if (val != name_size(&buf->data[offset]))
> > > + ret = name_size(&buf->data[offset]);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + if (val != ret)
> > > return -EINVAL;
> > > +
> > > memcpy(name, &buf->data[offset], val);
> > > /* forget the rest */
> > > return 0;
> > > @@ -221,46 +236,70 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
> > > * As with most tpm_buf operations, success is assumed because failure
> > > * will be caused by an incorrect programming model and indicated by a
> > > * kernel message.
> > > + *
> > > + * Ends the authorization session on failure.
> > > */
> > > -void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > > - u32 handle, u8 *name)
> > > +int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > > + u32 handle, u8 *name)
> > > {
> > > #ifdef CONFIG_TCG_TPM2_HMAC
> > > enum tpm2_mso_type mso = tpm2_handle_mso(handle);
> > > struct tpm2_auth *auth;
> > > int slot;
> > > + int ret;
> > > #endif
> > >
> > > if (!tpm2_chip_auth(chip)) {
> > > tpm_buf_append_handle(chip, buf, handle);
> > > - return;
> > > + return 0;
> > > }
> > >
> > > #ifdef CONFIG_TCG_TPM2_HMAC
> > > slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
> > > if (slot >= AUTH_MAX_NAMES) {
> > > - dev_err(&chip->dev, "TPM: too many handles\n");
> > > - return;
> > > + dev_err(&chip->dev, "too many handles\n");
> > > + ret = -EIO;
> > > + goto err;
> > > }
> > > auth = chip->auth;
> > > - WARN(auth->session != tpm_buf_length(buf),
> > > - "name added in wrong place\n");
> > > + if (auth->session != tpm_buf_length(buf)) {
> > > + dev_err(&chip->dev, "session state malformed");
> > > + ret = -EIO;
> > > + goto err;
> > > + }
> > > tpm_buf_append_u32(buf, handle);
> > > auth->session += 4;
> > >
> > > if (mso == TPM2_MSO_PERSISTENT ||
> > > mso == TPM2_MSO_VOLATILE ||
> > > mso == TPM2_MSO_NVRAM) {
> > > - if (!name)
> > > - tpm2_read_public(chip, handle, auth->name[slot]);
> > > + if (!name) {
> > > + ret = tpm2_read_public(chip, handle, auth->name[slot]);
> > > + if (ret)
> > > + goto err;
> > > + }
> > > } else {
> > > - if (name)
> > > - dev_err(&chip->dev, "TPM: Handle does not require name but one is specified\n");
> >
> > We're dropping the error message here; is there a reason for that?
>
> Thanks, I'll add it back.
>
> >
> > > + if (name) {
> > > + ret = -EIO;
> > > + goto err;
> > > + }
> > > }
> > >
> > > auth->name_h[slot] = handle;
> > > - if (name)
> > > - memcpy(auth->name[slot], name, name_size(name));
> > > + if (name) {
> > > + ret = name_size(name);
> > > + if (ret < 0)
> > > + goto err;
> > > +
> > > + memcpy(auth->name[slot], name, ret);
> > > + }
> > > +#endif
> > > + return 0;
> > > +
> > > +#ifdef CONFIG_TCG_TPM2_HMAC
> > > +err:
> > > + tpm2_end_auth_session(chip);
> > > + return tpm_ret_to_err(ret);
> > > #endif
> > > }
> > > EXPORT_SYMBOL_GPL(tpm_buf_append_name);
> > > @@ -533,11 +572,9 @@ static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
> > > * encryption key and encrypts the first parameter of the command
> > > * buffer with it.
> > > *
> > > - * As with most tpm_buf operations, success is assumed because failure
> > > - * will be caused by an incorrect programming model and indicated by a
> > > - * kernel message.
> > > + * Ends the authorization session on failure.
> > > */
> > > -void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > +int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > {
> > > u32 cc, handles, val;
> > > struct tpm2_auth *auth = chip->auth;
> > > @@ -549,9 +586,12 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > u8 cphash[SHA256_DIGEST_SIZE];
> > > struct sha256_ctx sctx;
> > > struct hmac_sha256_ctx hctx;
> > > + int ret;
> > >
> > > - if (!auth)
> > > - return;
> > > + if (!auth) {
> > > + ret = -EINVAL;
> > > + goto err;
> > > + }
> > >
> > > /* save the command code in BE format */
> > > auth->ordinal = head->ordinal;
> > > @@ -560,9 +600,10 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > >
> > > i = tpm2_find_cc(chip, cc);
> > > if (i < 0) {
> > > - dev_err(&chip->dev, "Command 0x%x not found in TPM\n", cc);
> >
> > Again, I think it's generally helpful to have the error message given that
> > the return (EINVAL) does not help narrow down which value is bad.
>
> Agreed.
>
> >
> > > - return;
> > > + ret = -EINVAL;
> > > + goto err;
> > > }
> > > +
> > > attrs = chip->cc_attrs_tbl[i];
> > >
> > > handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
> > > @@ -576,9 +617,9 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > u32 handle = tpm_buf_read_u32(buf, &offset_s);
> > >
> > > if (auth->name_h[i] != handle) {
> > > - dev_err(&chip->dev, "TPM: handle %d wrong for name\n",
> > > - i);
> > > - return;
> > > + dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
> > > + ret = -EINVAL;
> > > + goto err;
> > > }
> > > }
> > > /* point offset_s to the start of the sessions */
> > > @@ -609,12 +650,14 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > offset_s += len;
> > > }
> > > if (offset_s != offset_p) {
> > > - dev_err(&chip->dev, "TPM session length is incorrect\n");
> > > - return;
> > > + dev_err(&chip->dev, "session length is incorrect\n");
> > > + ret = -EINVAL;
> > > + goto err;
> > > }
> > > if (!hmac) {
> > > - dev_err(&chip->dev, "TPM could not find HMAC session\n");
> > > - return;
> > > + dev_err(&chip->dev, "could not find HMAC session\n");
> > > + ret = -EINVAL;
> > > + goto err;
> > > }
> > >
> > > /* encrypt before HMAC */
> > > @@ -646,8 +689,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > if (mso == TPM2_MSO_PERSISTENT ||
> > > mso == TPM2_MSO_VOLATILE ||
> > > mso == TPM2_MSO_NVRAM) {
> > > - sha256_update(&sctx, auth->name[i],
> > > - name_size(auth->name[i]));
> > > + 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]);
> > >
> > > @@ -668,6 +714,11 @@ void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
> > > hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
> > > hmac_sha256_update(&hctx, &auth->attrs, 1);
> > > hmac_sha256_final(&hctx, hmac);
> > > + return 0;
> > > +
> > > +err:
> > > + tpm2_end_auth_session(chip);
> > > + return ret;
> > > }
> > > EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
> > >
> > > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > > index 0e9e043f728c..1a59f0190eb3 100644
> > > --- a/include/linux/tpm.h
> > > +++ b/include/linux/tpm.h
> > > @@ -528,8 +528,8 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
> > > #endif
> > > }
> > >
> > > -void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > > - u32 handle, u8 *name);
> > > +int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
> > > + u32 handle, u8 *name);
> > > void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
> > > u8 attributes, u8 *passphrase,
> > > int passphraselen);
> > > @@ -562,7 +562,7 @@ static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
> > > #ifdef CONFIG_TCG_TPM2_HMAC
> > >
> > > int tpm2_start_auth_session(struct tpm_chip *chip);
> > > -void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
> > > +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);
> > > diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> > > index e165b117bbca..7672a4376dad 100644
> > > --- a/security/keys/trusted-keys/trusted_tpm2.c
> > > +++ b/security/keys/trusted-keys/trusted_tpm2.c
> > > @@ -283,7 +283,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > > goto out_put;
> > > }
> > >
> > > - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > > + if (rc)
> > > + goto out;
> > > +
> > > tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> > > options->keyauth, TPM_DIGEST_SIZE);
> > >
> > > @@ -331,7 +334,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> > > goto out;
> > > }
> > >
> > > - 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);
> > > if (rc)
> > > @@ -438,7 +444,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > > return rc;
> > > }
> > >
> > > - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > > + if (rc)
> > > + goto out;
> > > +
> > > tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> > > TPM_DIGEST_SIZE);
> > >
> > > @@ -450,7 +459,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> > > goto out;
> > > }
> > >
> > > - 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, "loading blob");
> > > rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > > if (!rc)
> > > @@ -497,7 +509,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > > return rc;
> > > }
> > >
> > > - tpm_buf_append_name(chip, &buf, blob_handle, NULL);
> > > + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> > > + if (rc)
> > > + goto out;
> > >
> > > if (!options->policyhandle) {
> > > tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> > > @@ -522,7 +536,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> > > NULL, 0);
> > > }
> > >
> > > - 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, 6, "unsealing");
> > > rc = tpm_buf_check_hmac_response(chip, &buf, rc);
> > >
> > > --
> > > 2.52.0
> > >
> >
> > J.
> >
> > --
> > Be Ye Not Lost Among Precepts of Order
>
> BR, Jarkko
Addressed in: https://lore.kernel.org/linux-integrity/20251202202643.107108-2-jarkko@kernel.org/
BR, Jarkko
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-12-02 22:07 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20251201224554.1717104-1-jarkko@kernel.org>
2025-12-01 22:45 ` [PATCH 1/4] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH 4/4] tpm2-sessions: Open code tpm_buf_append_hmac_session() Jarkko Sakkinen
2025-12-01 22:45 ` [PATCH v3] tpm2-sessions: address out-of-range indexing Jarkko Sakkinen
2025-12-01 19:39 Jarkko Sakkinen
2025-12-02 16:26 ` Jonathan McDowell
2025-12-02 19:53 ` Jarkko Sakkinen
2025-12-02 22:07 ` Jarkko Sakkinen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).