Skip to contentSkip to conversation

tpm: Actually fail on TPM errors during "get random"

PATCH Superseded Kees Cook 3 participants 5 messages Apr 1, 2019

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH 1/1 tpm: Actually fail on TPM errors during "get random"

A "get random" may fail with a TPM error, but those codes were returned as-is to the caller, which assumed the result was the number of bytes that had been written to the target buffer, which could lead to an kernel heap memory exposure and over-read.

This fixes tpm1_get_random() to mask positive TPM errors into -EIO, as before.

[   18.092103] tpm tpm0: A TPM error (379) occurred attempting get random
[   18.092106] usercopy: Kernel memory exposure attempt detected from SLUB object 'kmalloc-64' (offset 0, size 379)!
Fixes 7aee9c52d7ac ("tpm: tpm1: rewrite tpm1_get_random() using tpm_buf structure")
Cc Laura Abbott <[email protected]>
Cc Tomas Winkler <[email protected]>
Cc Jarkko Sakkinen <[email protected]>
---
 drivers/char/tpm/tpm1-cmd.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/char/tpm/tpm1-cmd.c +6 -3
index 85dcf2654d11..faeb78ecf960 100644
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -524,7 +524,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
 	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
 	if (rc)
-		return rc;
+		goto fail;
 
 	do {
 		tpm_buf_append_u32(&buf, num_bytes);
@@ -559,7 +559,10 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 	rc = total ? (int)total : -EIO;
 out:
 	tpm_buf_destroy(&buf);
-	return rc;
+fail:
+	if (rc < 0)
+		return rc;
+	return -EIO;
 }
 
 #define TPM_ORD_PCRREAD 21
Signature · 5 lines
2.17.1


-- 
Kees Cook
#1 lore raw
Quoted 36 lines · Kees Cook

A "get random" may fail with a TPM error, but those codes were returned as-is to the caller, which assumed the result was the number of bytes that had been written to the target buffer, which could lead to an kernel heap memory exposure and over-read.

This fixes tpm1_get_random() to mask positive TPM errors into -EIO, as before.

[   18.092103] tpm tpm0: A TPM error (379) occurred attempting get random
[   18.092106] usercopy: Kernel memory exposure attempt detected from SLUB object 'kmalloc-64' (offset 0, size 379)!
Fixes 7aee9c52d7ac ("tpm: tpm1: rewrite tpm1_get_random() using tpm_buf structure")
Cc Laura Abbott <[email protected]>
Cc Tomas Winkler <[email protected]>
Cc Jarkko Sakkinen <[email protected]>
 drivers/char/tpm/tpm1-cmd.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/char/tpm/tpm1-cmd.c +2 -1
index 85dcf2654d11..faeb78ecf960 100644
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {

drivers/char/tpm/tpm1-cmd.c int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) · line 527

Quoted 5 lines
drivers/char/tpm/tpm1-cmd.c +1 -1
@@ -524,7 +524,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
 	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
 	if (rc)
-		return rc;
+		goto fail;

?? Does tpm_buf_init() return positive?? Shouldn't..

I think you actually want this:

drivers/char/tpm/tpm1-cmd.c +4 -2
index 85dcf2654d1102..a01e6fba1aacb6 100644
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -531,8 +531,10 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
                rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
                                      "attempting get random");
-               if (rc)
+               if (rc) {
+                       rc = -EIO
                        goto out;
+               }
 
                out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
 
drivers/char/tpm/tpm2-cmd.c +3 -1
index e74c5b7b64bfbd..9fa498b4cf8816 100644
@@ -328,8 +328,10 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
                                       offsetof(struct tpm2_get_random_out,
                                                buffer),
                                       "attempting get random");
-               if (err)
+               if (err) {
+                       rc = -EIO;
                        goto out;
+               }
 
                out = (struct tpm2_get_random_out *)
                        &buf.data[TPM_HEADER_SIZE];
author
#2 lore raw
Quoted 39 lines · Jason Gunthorpe
Quoted 37 lines · Kees Cook

A "get random" may fail with a TPM error, but those codes were returned as-is to the caller, which assumed the result was the number of bytes that had been written to the target buffer, which could lead to an kernel heap memory exposure and over-read.

This fixes tpm1_get_random() to mask positive TPM errors into -EIO, as before.

[   18.092103] tpm tpm0: A TPM error (379) occurred attempting get random
[   18.092106] usercopy: Kernel memory exposure attempt detected from SLUB object 'kmalloc-64' (offset 0, size 379)!
Fixes 7aee9c52d7ac ("tpm: tpm1: rewrite tpm1_get_random() using tpm_buf structure")
Cc Laura Abbott <[email protected]>
Cc Tomas Winkler <[email protected]>
Cc Jarkko Sakkinen <[email protected]>
 drivers/char/tpm/tpm1-cmd.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/char/tpm/tpm1-cmd.c +2 -1
index 85dcf2654d11..faeb78ecf960 100644
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -524,7 +524,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
Quoted 8 lines

drivers/char/tpm/tpm1-cmd.c int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max) · line 527

Quoted 4 lines
drivers/char/tpm/tpm1-cmd.c +1 -1
      rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
      if (rc)
-             return rc;
+             goto fail;

?? Does tpm_buf_init() return positive?? Shouldn't..

I think you actually want this:

I didn't check that, but it was tpm_transmit_cmd() that was failing (and would be covered by the error path change I sent).

Quoted 32 lines
drivers/char/tpm/tpm1-cmd.c +4 -2
index 85dcf2654d1102..a01e6fba1aacb6 100644
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -531,8 +531,10 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
                rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
                                      "attempting get random");
-               if (rc)
+               if (rc) {
+                       rc = -EIO
                        goto out;
+               }
 
                out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
 
drivers/char/tpm/tpm2-cmd.c +0 -0
index e74c5b7b64bfbd..9fa498b4cf8816 100644
@@ -328,8 +328,10 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
                                       offsetof(struct tpm2_get_random_out,

drivers/char/tpm/tpm2-cmd.c int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) · line 337

Quoted 10 lines
drivers/char/tpm/tpm2-cmd.c +3 -1
                                                buffer),
                                       "attempting get random");
-               if (err)
+               if (err) {
+                       rc = -EIO;
                        goto out;
+               }
 
                out = (struct tpm2_get_random_out *)
                        &buf.data[TPM_HEADER_SIZE];

Eeks, I missed tpm2. In both cases, though a negative error would be masked. I was trying to leave those as-is and only mask positive errors.

Signature · 1 line
Kees Cook
maintainer
#3 lore raw
In reply to Kees Cook

On Mon, 2019-04-01 at 11:32 -0700, Kees Cook wrote:
[...]

Quoted 23 lines
drivers/char/tpm/tpm1-cmd.c +2 -2
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -524,7 +524,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8
 *dest, size_t max)
 
 	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
 TPM_ORD_GET_RANDOM);
 	if (rc)
-		return rc;
+		goto fail;
 
drivers/char/tpm/tpm1-cmd.c +0 -0
 	do {
 		tpm_buf_append_u32(&buf, num_bytes);

drivers/char/tpm/tpm1-cmd.c int tpm1_get_random(struct tpm_chip *chip, u8 · line 563

Quoted 5 lines
drivers/char/tpm/tpm1-cmd.c +0 -1
@@ -559,7 +559,10 @@ int tpm1_get_random(struct tpm_chip *chip, u8
 *dest, size_t max)
 	rc = total ? (int)total : -EIO;
 out:
 	tpm_buf_destroy(&buf);
-	return rc;

You can't remove this otherwise the only return will ever be a failure.

I think what you're trying to catch is tpm_transmit_cmd returning a positive failure, So you need to check the output of tpm_transmit_cmd as well and goto failure leaving the above return in place.

James

Quoted 11 lines
drivers/char/tpm/tpm1-cmd.c +4 -0
+fail:
+	if (rc < 0)
+		return rc;
+	return -EIO;
 }
 

#define TPM_ORD_PCRREAD 21

Signature · 1 line
2.17.1
author
#4 lore raw
Quoted 34 lines · James Bottomley

On Mon, 2019-04-01 at 11:32 -0700, Kees Cook wrote:
[...]

Quoted 23 lines
drivers/char/tpm/tpm1-cmd.c +2 -2
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -524,7 +524,7 @@ int tpm1_get_random(struct tpm_chip *chip, u8
 *dest, size_t max)
 
      rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
 TPM_ORD_GET_RANDOM);
      if (rc)
-             return rc;
+             goto fail;
 
      do {
              tpm_buf_append_u32(&buf, num_bytes);

drivers/char/tpm/tpm1-cmd.c int tpm1_get_random(struct tpm_chip *chip, u8 · line 563

Quoted 5 lines
drivers/char/tpm/tpm1-cmd.c +0 -1
@@ -559,7 +559,10 @@ int tpm1_get_random(struct tpm_chip *chip, u8
 *dest, size_t max)
      rc = total ? (int)total : -EIO;
 out:
      tpm_buf_destroy(&buf);
-     return rc;

You can't remove this otherwise the only return will ever be a failure.

Quoted 3 lines

I think what you're trying to catch is tpm_transmit_cmd returning a positive failure, So you need to check the output of tpm_transmit_cmd as well and goto failure leaving the above return in place.

eek, yes. I double-checked this in tpm2, but tpm1 is different...

Signature · 1 line
Kees Cook