The length of a TPM reply comes from the device and cannot be trusted to
be well-formed. Add tests for two malformed replies: one too short to hold
the data it advertises, and one advertising more data than the request can
hold. The sandbox TPM emulator gains two test-only properties that produce
these replies, and two DM tests ask tpm2_get_capability() for them and
check the call is rejected instead of parsing past the end of the reply or
past the caller's buffer.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
Acked-by: Miquel Raynal <[email protected]>
---
 drivers/tpm/tpm2_tis_sandbox.c | 27 +++++++++++++++++++++++++++
 include/tpm-v2.h               | 10 ++++++++++
 test/dm/tpm.c                  | 41 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+)

diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c
index 50e308e7116..9205781fafc 100644
--- a/drivers/tpm/tpm2_tis_sandbox.c
+++ b/drivers/tpm/tpm2_tis_sandbox.c
@@ -542,6 +542,33 @@ 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.
+                */
+               if (capability == TPM2_CAP_TPM_PROPERTIES &&
+                   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/include/tpm-v2.h b/include/tpm-v2.h
index a776d24d71f..8bcc4caeb06 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..a8645cd1601 100644
--- a/test/dm/tpm.c
+++ b/test/dm/tpm.c
@@ -6,6 +6,7 @@
 
 #include <dm.h>
 #include <tpm_api.h>
+#include <tpm-v2.h>
 #include <dm/test.h>
 #include <test/test.h>
 #include <test/ut.h>
@@ -197,3 +198,43 @@ 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 GetCapability reply too short to 
hold
+ * the data it advertises is rejected, 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_TPM_PROPERTIES,
+                                     TPM2_PT_SANDBOX_SHORT_RESPONSE, buf, 1));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_short, UTF_SCAN_FDT);
+
+/*
+ * Check that a GetCapability reply advertising more data than was requested is
+ * rejected, rather than copied past the end of the caller's buffer.
+ */
+static int dm_test_tpm2_get_capability_long(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_TPM_PROPERTIES,
+                                     TPM2_PT_SANDBOX_LONG_RESPONSE, buf, 1));
+
+       return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_long, UTF_SCAN_FDT);

-- 
2.43.0

Reply via email to