From cb3313e8cb6a95e5ad02860222fed18db82b37af Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Fri, 26 Aug 2011 12:53:16 -0700 Subject: Partial unit tests for rollback_index BUG=chromium-os:17564 TEST=make && make runtests Change-Id: I8ea6bcc15f277e10c5b8539f2ea19ad90be34889 Reviewed-on: http://gerrit.chromium.org/gerrit/6770 Reviewed-by: Bill Richardson Reviewed-by: Stefan Reinauer Tested-by: Randall Spangler --- firmware/lib/include/rollback_index.h | 38 ++++- firmware/lib/rollback_index.c | 23 +-- tests/Makefile | 5 +- tests/rollback_index2_tests.c | 256 ++++++++++++++++++++++++++++++++++ tests/test_common.c | 31 +++- tests/test_common.h | 10 +- 6 files changed, 330 insertions(+), 33 deletions(-) create mode 100644 tests/rollback_index2_tests.c diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h index 8a86e538..4d92bb71 100644 --- a/firmware/lib/include/rollback_index.h +++ b/firmware/lib/include/rollback_index.h @@ -55,11 +55,11 @@ __pragma(pack(pop)) /* Support packing for MSVC. */ /* All functions return TPM_SUCCESS (zero) if successful, non-zero if error */ -/* These functions are called from S3Resume(). They cannot use - * global variables. */ +/* These functions are called from VbInit(). They cannot use global + * variables. */ uint32_t RollbackS3Resume(void); -/* These functions are callable from LoadFirmware(). They cannot use +/* These functions are callable from VbSelectFirmware(). They cannot use * global variables. */ /* Setup must be called. Pass recovery_mode=nonzero if in recovery @@ -74,8 +74,8 @@ uint32_t RollbackFirmwareWrite(uint32_t version); /* Lock must be called */ uint32_t RollbackFirmwareLock(void); -/* These functions are callable from LoadKernel(). They may use global - * variables. */ +/* These functions are callable from VbSelectAndLoadKernel(). They + * may use global variables. */ /* Read and write may be called to read and write the kernel version. */ uint32_t RollbackKernelRead(uint32_t* version); @@ -84,9 +84,35 @@ uint32_t RollbackKernelWrite(uint32_t version); /* Lock must be called. Internally, it's ignored in recovery mode. */ uint32_t RollbackKernelLock(void); -/* The following functions are here for testing only. */ +/****************************************************************************/ +/* The following functions are internal apis, listed here for use by + * unit tests only. */ /* Issue a TPM_Clear and reenable/reactivate the TPM. */ uint32_t TPMClearAndReenable(void); +/* Like TlclWrite(), but checks for write errors due to hitting the 64-write + * limit and clears the TPM when that happens. This can only happen when the + * TPM is unowned, so it is OK to clear it (and we really have no choice). + * This is not expected to happen frequently, but it could happen. */ +uint32_t SafeWrite(uint32_t index, const void* data, uint32_t length); + +/* Similarly to SafeWrite(), this ensures we don't fail a DefineSpace because + * we hit the TPM write limit. This is even less likely to happen than with + * writes because we only define spaces once at initialization, but we'd rather + * be paranoid about this. */ +uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size); + +/* Performs one-time initializations. Creates the NVRAM spaces, and sets their + * initial values as needed. Sets the nvLocked bit and ensures the physical + * presence command is enabled and locked. + */ +uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf, + RollbackSpaceKernel* rsk); + +/* SetupTPM starts the TPM and establishes the root of trust for the + * anti-rollback mechanism. */ +uint32_t SetupTPM(int recovery_mode, int developer_mode, + RollbackSpaceFirmware* rsf); + #endif /* VBOOT_REFERENCE_ROLLBACK_INDEX_H_ */ diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c index e0554ddf..47579135 100644 --- a/firmware/lib/rollback_index.c +++ b/firmware/lib/rollback_index.c @@ -36,12 +36,7 @@ uint32_t TPMClearAndReenable(void) { } -/* Like TlclWrite(), but checks for write errors due to hitting the 64-write - * limit and clears the TPM when that happens. This can only happen when the - * TPM is unowned, so it is OK to clear it (and we really have no choice). - * This is not expected to happen frequently, but it could happen. - */ -static uint32_t SafeWrite(uint32_t index, const void* data, uint32_t length) { +uint32_t SafeWrite(uint32_t index, const void* data, uint32_t length) { uint32_t result = TlclWrite(index, data, length); if (result == TPM_E_MAXNVWRITES) { RETURN_ON_FAILURE(TPMClearAndReenable()); @@ -52,12 +47,7 @@ static uint32_t SafeWrite(uint32_t index, const void* data, uint32_t length) { } -/* Similarly to SafeWrite(), this ensures we don't fail a DefineSpace because - * we hit the TPM write limit. This is even less likely to happen than with - * writes because we only define spaces once at initialization, but we'd rather - * be paranoid about this. - */ -static uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { +uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { uint32_t result = TlclDefineSpace(index, perm, size); if (result == TPM_E_MAXNVWRITES) { RETURN_ON_FAILURE(TPMClearAndReenable()); @@ -87,12 +77,9 @@ static uint32_t WriteSpaceKernel(const RollbackSpaceKernel* rsk) { return SafeWrite(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); } -/* Performs one-time initializations. Creates the NVRAM spaces, and sets their - * initial values as needed. Sets the nvLocked bit and ensures the physical - * presence command is enabled and locked. - */ -static uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf, - RollbackSpaceKernel* rsk) { + +uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf, + RollbackSpaceKernel* rsk) { static const RollbackSpaceFirmware rsf_init = { ROLLBACK_SPACE_FIRMWARE_VERSION, 0, 0, 0}; static const RollbackSpaceKernel rsk_init = { diff --git a/tests/Makefile b/tests/Makefile index b81825bc..f602b1e8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -11,6 +11,7 @@ INCLUDES += -I./include \ BUILD_ROOT = ${BUILD}/tests TEST_NAMES = cgptlib_test \ + rollback_index2_tests \ rsa_padding_test \ rsa_verify_benchmark \ sha_benchmark \ @@ -30,7 +31,8 @@ TEST_LIB = ${BUILD_ROOT}/test.a TEST_LIB_SRCS = test_common.c timer_utils.c crc32_test.c TEST_LIB_OBJS = $(TEST_LIB_SRCS:%.c=${BUILD_ROOT}/%.o) ALL_DEPS = $(addsuffix .d,${TEST_BINS} ${TEST_LIB_OBJS}) -CFLAGS += -MMD -MF $@.d +# Allow multiple definitions, so tests can mock functions from other libraries +CFLAGS += -MMD -MF $@.d -Xlinker --allow-multiple-definition LIBS := ${TEST_LIB} $(HOSTLIB) @@ -94,6 +96,7 @@ runcryptotests: # Run other misc tests runmisctests: ./run_vbutil_tests.sh + ${BUILD_ROOT}/rollback_index2_tests ${BUILD_ROOT}/stateful_util_tests ${BUILD_ROOT}/tpm_bootmode_tests ${BUILD_ROOT}/utility_string_tests diff --git a/tests/rollback_index2_tests.c b/tests/rollback_index2_tests.c new file mode 100644 index 00000000..cd5fcb90 --- /dev/null +++ b/tests/rollback_index2_tests.c @@ -0,0 +1,256 @@ +/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Tests for rollback_index functions + */ + +#include +#include +#include + +#define _STUB_IMPLEMENTATION_ /* So we can use memset() ourselves */ + +#include "rollback_index.h" +#include "test_common.h" +#include "tlcl.h" +#include "utility.h" +#include "vboot_common.h" + +static char calls[16384]; +static char *cnext = calls; +static int ccount = 0; +static int cfail = 0; +static uint32_t cfail_err = TPM_SUCCESS; + +static TPM_PERMANENT_FLAGS mock_pflags; +static RollbackSpaceFirmware mock_rsf; +static RollbackSpaceKernel mock_rsk; + +static void ResetMocks(int fail_on_call, uint32_t fail_with_err) { + cnext = calls; + ccount = 0; + cfail = fail_on_call; + cfail_err = fail_with_err; + + Memset(&mock_pflags, 0, sizeof(mock_pflags)); + Memset(&mock_rsf, 0, sizeof(mock_rsf)); + Memset(&mock_rsk, 0, sizeof(mock_rsk)); +} + +/****************************************************************************/ +/* Mocks for tlcl functions which log the calls made to calls[]. */ + +uint32_t TlclForceClear(void) { + cnext += sprintf(cnext, "TlclForceClear()\n"); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclSetEnable(void) { + cnext += sprintf(cnext, "TlclSetEnable()\n"); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclSetDeactivated(uint8_t flag) { + cnext += sprintf(cnext, "TlclSetDeactivated(%d)\n", flag); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) { + cnext += sprintf(cnext, "TlclWrite(0x%x, %d)\n", index, length); + + if (FIRMWARE_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsf), "TlclWrite rsf size"); + Memcpy(&mock_rsf, data, length); + } else if (KERNEL_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsk), "TlclWrite rsk size"); + Memcpy(&mock_rsk, data, length); + } + + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { + cnext += sprintf(cnext, "TlclDefineSpace(0x%x, 0x%x, %d)\n", + index, perm, size); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclSelfTestFull(void) { + cnext += sprintf(cnext, "TlclSelfTestFull()\n"); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) { + cnext += sprintf(cnext, "TlclGetPermanentFlags()\n"); + Memcpy(pflags, &mock_pflags, sizeof(mock_pflags)); + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclFinalizePhysicalPresence(void) { + cnext += sprintf(cnext, "TlclFinalizePhysicalPresence()\n"); + mock_pflags.physicalPresenceLifetimeLock = 1; + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +uint32_t TlclSetNvLocked(void) { + cnext += sprintf(cnext, "TlclSetNvLocked()\n"); + mock_pflags.nvLocked = 1; + return (++ccount == cfail) ? cfail_err : TPM_SUCCESS; +} + +/****************************************************************************/ +/* Tests for misc helper functions */ + +static void MiscTest(void) { + uint8_t buf[8]; + + ResetMocks(0, 0); + TEST_EQ(TPMClearAndReenable(), 0, "TPMClearAndReenable()"); + TEST_STR_EQ(calls, + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + ResetMocks(0, 0); + TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite()"); + TEST_STR_EQ(calls, + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_BADINDEX); + TEST_EQ(SafeWrite(0x123, buf, 8), TPM_E_BADINDEX, "SafeWrite() bad"); + TEST_STR_EQ(calls, + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_MAXNVWRITES); + TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite() retry max writes"); + TEST_STR_EQ(calls, + "TlclWrite(0x123, 8)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(0, 0); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, "SafeDefineSpace()"); + TEST_STR_EQ(calls, + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_BADINDEX); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), TPM_E_BADINDEX, + "SafeDefineSpace() bad"); + TEST_STR_EQ(calls, + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_MAXNVWRITES); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, + "SafeDefineSpace() retry max writes"); + TEST_STR_EQ(calls, + "TlclDefineSpace(0x123, 0x6, 8)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); +} + +/****************************************************************************/ + +/* Tests for one-time initialization */ +static void OneTimeInitTest(void) { + RollbackSpaceFirmware rsf; + RollbackSpaceKernel rsk; + + /* Complete initialization */ + ResetMocks(0, 0); + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclFinalizePhysicalPresence()\n" + "TlclSetNvLocked()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(mock_rsf.struct_version, ROLLBACK_SPACE_FIRMWARE_VERSION, "rsf ver"); + TEST_EQ(mock_rsf.flags, 0, "rsf flags"); + TEST_EQ(mock_rsf.fw_versions, 0, "rsf fw_versions"); + TEST_EQ(mock_rsk.struct_version, ROLLBACK_SPACE_KERNEL_VERSION, "rsk ver"); + TEST_EQ(mock_rsk.uid, ROLLBACK_SPACE_KERNEL_UID, "rsk uid"); + TEST_EQ(mock_rsk.kernel_versions, 0, "rsk kernel_versions"); + + /* Physical presence already initialized */ + ResetMocks(0, 0); + mock_pflags.physicalPresenceLifetimeLock = 1; + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclSetNvLocked()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n", + "tlcl calls"); + + /* NV locking already initialized */ + ResetMocks(0, 0); + mock_pflags.nvLocked = 1; + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclFinalizePhysicalPresence()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n", + "tlcl calls"); + + /* Self test error */ + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), TPM_E_IOERROR, + "OneTimeInitializeTPM() selftest"); + TEST_STR_EQ(calls, + "TlclSelfTestFull()\n", + "tlcl calls"); +} + + +/* disable MSVC warnings on unused arguments */ +__pragma(warning (disable: 4100)) + +int main(int argc, char* argv[]) { + int error_code = 0; + + MiscTest(); + OneTimeInitTest(); + + if (!gTestSuccess) + error_code = 255; + + return error_code; +} diff --git a/tests/test_common.c b/tests/test_common.c index 6521fa0f..bb094e0f 100644 --- a/tests/test_common.c +++ b/tests/test_common.c @@ -8,6 +8,7 @@ #include "test_common.h" #include +#include #include "cryptolib.h" #include "file_keys.h" @@ -20,8 +21,7 @@ int TEST_EQ(int result, int expected_result, char* testname) { if (result == expected_result) { fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname); return 1; - } - else { + } else { fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); fprintf(stderr, " Expected: %d, got: %d\n", expected_result, result); gTestSuccess = 0; @@ -33,8 +33,7 @@ int TEST_NEQ(int result, int not_expected_result, char* testname) { if (result != not_expected_result) { fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname); return 1; - } - else { + } else { fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); fprintf(stderr, " Didn't expect %d, but got it.\n", not_expected_result); gTestSuccess = 0; @@ -47,8 +46,7 @@ int TEST_PTR_EQ(const void* result, const void* expected_result, if (result == expected_result) { fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname); return 1; - } - else { + } else { fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); fprintf(stderr, " Expected: 0x%lx, got: 0x%lx\n", (long)expected_result, (long)result); @@ -56,3 +54,24 @@ int TEST_PTR_EQ(const void* result, const void* expected_result, return 0; } } + +int TEST_STR_EQ(const char* result, const char* expected_result, + char* testname) { + + if (!result || !expected_result) { + fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); + fprintf(stderr, " String compare with NULL\n"); + gTestSuccess = 0; + return 0; + } else if (!strcmp(result, expected_result)) { + fprintf(stderr, "%s Test " COL_GREEN "PASSED\n" COL_STOP, testname); + return 1; + } else { + fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); + fprintf(stderr, " Expected: \"%s\", got: \"%s\"\n", expected_result, + result); + gTestSuccess = 0; + return 0; + } + +} diff --git a/tests/test_common.h b/tests/test_common.h index 532e7b2f..47ffa2a4 100644 --- a/tests/test_common.h +++ b/tests/test_common.h @@ -17,11 +17,17 @@ int TEST_EQ(int result, int expected_result, char* testname); * Also update the global gTestSuccess flag if test fails. */ int TEST_NEQ(int result, int not_expected_result, char* testname); -/* Return 1 if result is equal to expected_result, else return 0. - * Also update the global gTestSuccess flag if test fails. */ +/* Return 1 if result pointer is equal to expected_result pointer, + * else return 0. Does not check pointer contents, only the pointer + * itself. Also update the global gTestSuccess flag if test fails. */ int TEST_PTR_EQ(const void* result, const void* expected_result, char* testname); +/* Return 1 if result string is equal to expected_result string, + * else return 0. Also update the global gTestSuccess flag if test fails. */ +int TEST_STR_EQ(const char* result, const char* expected_result, + char* testname); + /* ANSI Color coding sequences. * * Don't use \e as MSC does not recognize it as a valid escape sequence. -- cgit v1.2.1