The length of a TPM reply comes from the device and cannot be trusted to
be well-formed. Add DM tests for the malformed replies that the length
checks reject.

For TPM2 GetCapability: one reply too short to hold its header, and one
advertising more data than the caller's buffer holds. The sandbox emulator
gains two test-only capability properties that produce them; the short
reply is caught by the length check in tpm_sendrecv_command(), the
over-long one by the caller's buffer bound.

For the TPM1 OIAP helpers tpm1_load_key2_oiap() and
tpm1_get_pub_key_oiap(): a reply too short to hold the auth trailer that
their length arithmetic subtracts. The emulator answers OIAP with a zeroed
session and both commands with a truncated reply, so the length check
rejects them before the subtraction underflows. These paths need
CONFIG_TPM_AUTH_SESSIONS, enabled here in the sandbox config.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
---
 configs/sandbox_defconfig      |  1 +
 drivers/tpm/tpm2_tis_sandbox.c | 28 ++++++++++++++
 drivers/tpm/tpm_tis_sandbox.c  |  9 +++++
 include/tpm-v2.h               | 10 +++++
 test/dm/tpm.c                  | 88 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+)

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 79f46317e45..db26b6813de 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -394,6 +394,7 @@ CONFIG_ECDSA=y
 CONFIG_ECDSA_VERIFY=y
 CONFIG_RSASSA_PSS=y
 CONFIG_TPM=y
+CONFIG_TPM_AUTH_SESSIONS=y
 CONFIG_ERRNO_STR=y
 CONFIG_GETOPT=y
 CONFIG_TEST_FDTDEC=y
diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c
index 50e308e7116..1fae0fc6ce1 100644
--- a/drivers/tpm/tpm2_tis_sandbox.c
+++ b/drivers/tpm/tpm2_tis_sandbox.c
@@ -542,6 +542,34 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 
*sendbuf,
                property_count = get_unaligned_be32(sent);
                sent += sizeof(property_count);
 
+               /*
+                * Test hook: reply with a truncated (header-only) success
+                * response so the response parser can be exercised against a
+                * reply too short to hold the data it promises. Honoured for 
any
+                * capability so the check can be tested on a path without a
+                * capability-specific length bound.
+                */
+               if (property == TPM2_PT_SANDBOX_SHORT_RESPONSE)
+                       return sandbox_tpm2_fill_buf(recv, recv_len, tag,
+                                                    TPM2_RC_SUCCESS);
+
+               /*
+                * Test hook: reply with a success response advertising more
+                * property data than a single-property request can hold, to
+                * exercise the caller-buffer bound in the parser.
+                */
+               if (capability == TPM2_CAP_TPM_PROPERTIES &&
+                   property == TPM2_PT_SANDBOX_LONG_RESPONSE) {
+                       *recv_len = TPM2_HDR_LEN + sizeof(u8) + sizeof(u32) +
+                                   sizeof(u32) + TPM2_PROPERTY_NB *
+                                   sizeof(struct tpms_tagged_property);
+                       put_unaligned_be16(tag, recv);
+                       put_unaligned_be32(*recv_len, recv + sizeof(tag));
+                       put_unaligned_be32(TPM2_RC_SUCCESS,
+                                          recv + sizeof(tag) + sizeof(u32));
+                       return 0;
+               }
+
                switch (capability) {
                case TPM2_CAP_PCRS:
                        break;
diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c
index d7341062b31..1f63e60cbaa 100644
--- a/drivers/tpm/tpm_tis_sandbox.c
+++ b/drivers/tpm/tpm_tis_sandbox.c
@@ -226,6 +226,15 @@ static int sandbox_tpm_xfer(struct udevice *dev, const 
uint8_t *sendbuf,
                *recv_len = 12;
                memset(recvbuf, '\0', *recv_len);
                break;
+       case 0x0a: /* TPM_ORD_OIAP: hand out a zeroed auth session for tests */
+               *recv_len = TPM_HDR_LEN + sizeof(uint32_t) + 20;
+               memset(recvbuf, '\0', *recv_len);
+               break;
+       case 0x21: /* TPM_ORD_GetPubKey: truncated reply, for the length check 
*/
+       case 0x41: /* TPM_ORD_LoadKey2: truncated reply, for the length check */
+               *recv_len = TPM_HDR_LEN;
+               memset(recvbuf, '\0', *recv_len);
+               break;
        default:
                printf("Unknown tpm command %02x\n", code);
                return -ENOSYS;
diff --git a/include/tpm-v2.h b/include/tpm-v2.h
index 4f5082f2234..648dfa2aec8 100644
--- a/include/tpm-v2.h
+++ b/include/tpm-v2.h
@@ -43,6 +43,16 @@ struct udevice;
 #define TPM2_CAP_PCRS 0x00000005U
 #define TPM2_CAP_TPM_PROPERTIES 0x00000006U
 
+/*
+ * Sandbox emulator test hooks: a TPM2_GetCapability for these properties makes
+ * the emulated TPM reply with, respectively, a header-only response and one
+ * advertising more property data than a single-property request can hold. They
+ * let tests drive the response parser with a reply shorter or longer than the
+ * data it should carry, as a tampered or faulty TPM on the bus could.
+ */
+#define TPM2_PT_SANDBOX_SHORT_RESPONSE 0x00ffffff
+#define TPM2_PT_SANDBOX_LONG_RESPONSE  0x00fffffe
+
 /* Definition of (UINT32) TPM2_PT Constants */
 #define TPM2_PT_GROUP                  (u32)(0x00000100)
 #define TPM2_PT_FIXED                  (u32)(TPM2_PT_GROUP * 1)
diff --git a/test/dm/tpm.c b/test/dm/tpm.c
index 87c5c416daa..eb6e55c3ab0 100644
--- a/test/dm/tpm.c
+++ b/test/dm/tpm.c
@@ -6,6 +6,8 @@
 
 #include <dm.h>
 #include <tpm_api.h>
+#include <tpm-v1.h>
+#include <tpm-v2.h>
 #include <dm/test.h>
 #include <test/test.h>
 #include <test/ut.h>
@@ -197,3 +199,89 @@ static int dm_test_tpm_autostart_reinit(struct 
unit_test_state *uts)
        return 0;
 }
 DM_TEST(dm_test_tpm_autostart_reinit, UTF_SCAN_FDT);
+
+/*
+ * A TPM sits on a bus a physical attacker can reach, so its responses cannot 
be
+ * trusted to be well-formed. Check that a reply too short to hold its header 
is
+ * rejected by tpm_sendrecv_command(), rather than parsed with an underflowed
+ * length.
+ */
+static int dm_test_tpm2_get_capability_short(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       u8 buf[64];
+
+       ut_assertok(get_tpm_version(TPM_V2, &dev));
+       ut_assertok(tpm_auto_start(dev));
+
+       ut_assert(tpm2_get_capability(dev, TPM2_CAP_PCRS,
+                                     TPM2_PT_SANDBOX_SHORT_RESPONSE, buf,
+                                     sizeof(buf), 1));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_short, UTF_SCAN_FDT);
+
+/*
+ * Check that a GetCapability reply larger than the caller's buffer is 
rejected,
+ * rather than copied past the end of it.
+ */
+static int dm_test_tpm2_get_capability_long(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       u8 buf[16];
+
+       ut_assertok(get_tpm_version(TPM_V2, &dev));
+       ut_assertok(tpm_auto_start(dev));
+
+       ut_assert(tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+                                     TPM2_PT_SANDBOX_LONG_RESPONSE, buf,
+                                     sizeof(buf), 1));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_long, UTF_SCAN_FDT);
+
+#if CONFIG_IS_ENABLED(TPM_AUTH_SESSIONS)
+/*
+ * A TPM1 OIAP reply carries an auth trailer that is subtracted from a
+ * device-controlled length. Check that a LoadKey2 reply too short to hold that
+ * trailer is rejected, rather than parsed with an underflowed length.
+ */
+static int dm_test_tpm1_load_key2_oiap_short(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       u8 key[8] = {0};
+       u8 auth[20] = {0};
+       u32 handle;
+
+       ut_assertok(get_tpm_version(TPM_V1, &dev));
+       ut_assertok(tpm_init(dev));
+
+       ut_assert(tpm1_load_key2_oiap(dev, 0, key, sizeof(key), auth, &handle));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm1_load_key2_oiap_short, UTF_SCAN_FDT);
+
+/*
+ * The GetPubKey OIAP helper subtracts the same auth trailer from the same
+ * device-controlled length. Check that a reply too short to hold the trailer
+ * is rejected, rather than parsed with an underflowed length.
+ */
+static int dm_test_tpm1_get_pub_key_oiap_short(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       u8 auth[20] = {0};
+       u8 pubkey[8] = {0};
+       size_t pubkey_len = sizeof(pubkey);
+
+       ut_assertok(get_tpm_version(TPM_V1, &dev));
+       ut_assertok(tpm_init(dev));
+
+       ut_assert(tpm1_get_pub_key_oiap(dev, 0, auth, pubkey, &pubkey_len));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm1_get_pub_key_oiap_short, UTF_SCAN_FDT);
+#endif

-- 
2.43.0

Reply via email to