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
