diff options
-rw-r--r-- | firmware/lib/include/stateful_util.h | 14 | ||||
-rw-r--r-- | firmware/lib/stateful_util.c | 6 | ||||
-rw-r--r-- | tests/Makefile | 2 | ||||
-rw-r--r-- | tests/stateful_util_tests.c | 302 | ||||
-rw-r--r-- | tests/test_common.c | 19 | ||||
-rw-r--r-- | tests/test_common.h | 8 | ||||
-rw-r--r-- | tests/utility_tests.c | 2 |
7 files changed, 345 insertions, 8 deletions
diff --git a/firmware/lib/include/stateful_util.h b/firmware/lib/include/stateful_util.h index 5630534f..980b12b0 100644 --- a/firmware/lib/include/stateful_util.h +++ b/firmware/lib/include/stateful_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +/* 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. */ @@ -19,10 +19,14 @@ typedef struct MemcpyState { uint8_t overrun; /* Flag set to 1 when an overrun occurs. */ } MemcpyState; +/* Initialize a stateful buffer struct to point to the buffer, with + * the specified remaining length in bytes. */ +void StatefulInit(MemcpyState* state, void* buf, uint64_t len); + /* Skip [len] bytes only if there's enough data to skip according * to [state]. * On success, return a meaningless but non-NULL pointer and updates [state]. - * On failure, return NULL, set remaining_len in state to -1. + * On failure, return NULL, set state->overrun to 1. * * Useful for iterating through a binary blob to populate a struct. After the * first failure (buffer overrun), successive calls will always fail. @@ -32,7 +36,7 @@ void* StatefulSkip(MemcpyState* state, uint64_t len); /* Copy [len] bytes into [dst] only if there's enough data to read according * to [state]. * On success, return [dst] and update [state]. - * On failure, return NULL, set remaining len in state to -1. + * On failure, return NULL, set state->overrun to 1. * * Useful for iterating through a binary blob to populate a struct. After the * first failure (buffer overrun), successive calls will always fail. @@ -42,7 +46,7 @@ void* StatefulMemcpy(MemcpyState* state, void* dst, uint64_t len); /* Like StatefulMemcpy() but copies in the opposite direction, populating * data from [src] into the buffer encapsulated in state [state]. * On success, return [src] and update [state]. - * On failure, return NULL, set remaining_len in state to -1. + * On failure, return NULL, set state->overrun to 1. * * Useful for iterating through a structure to populate a binary blob. After the * first failure (buffer overrun), successive calls will always fail. @@ -52,7 +56,7 @@ const void* StatefulMemcpy_r(MemcpyState* state, const void* src, uint64_t len); /* Like StatefulMemcpy_r() but fills a portion of the encapsulated buffer with * a constant value. * On success, return a meaningless but non-NULL pointer and updates [state]. - * On failure, return NULL, set remaining_len in state to -1. + * On failure, return NULL, set state->overrun to 1. * * After the first failure (buffer overrun), successive calls will always fail. */ diff --git a/firmware/lib/stateful_util.c b/firmware/lib/stateful_util.c index 1d824fc2..01a7d641 100644 --- a/firmware/lib/stateful_util.c +++ b/firmware/lib/stateful_util.c @@ -9,6 +9,12 @@ #include "utility.h" +void StatefulInit(MemcpyState* state, void* buf, uint64_t len) { + state->remaining_buf = buf; + state->remaining_len = len; + state->overrun = 0; +} + void* StatefulSkip(MemcpyState* state, uint64_t len) { if (state->overrun) return NULL; diff --git a/tests/Makefile b/tests/Makefile index 01729598..235ba957 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -15,6 +15,7 @@ TEST_NAMES = cgptlib_test \ rsa_verify_benchmark \ sha_benchmark \ sha_tests \ + stateful_util_tests \ utility_string_tests \ utility_tests \ vboot_common_tests \ @@ -92,6 +93,7 @@ runcryptotests: # Run other misc tests runmisctests: ./run_vbutil_tests.sh + ${BUILD_ROOT}/stateful_util_tests ${BUILD_ROOT}/utility_string_tests ${BUILD_ROOT}/utility_tests diff --git a/tests/stateful_util_tests.c b/tests/stateful_util_tests.c new file mode 100644 index 00000000..077a2ae9 --- /dev/null +++ b/tests/stateful_util_tests.c @@ -0,0 +1,302 @@ +/* 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 statful_util functions. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define _STUB_IMPLEMENTATION_ /* So we can use memset() ourselves */ + +#include "stateful_util.h" +#include "test_common.h" +#include "utility.h" +#include "vboot_common.h" + + +/* Test StatefulInit */ +static void StatefulInitTest(void) { + MemcpyState s; + char buf[128]; + + memset(&s, 0, sizeof(s)); + s.overrun = 1; + StatefulInit(&s, buf, 128); + TEST_EQ(0, s.overrun, "StatefulInit() overrun"); + TEST_EQ(128, s.remaining_len, "StatefulInit() len"); + TEST_PTR_EQ(buf, s.remaining_buf, "StatefulInit() buf"); +} + + +/* Test StatefulSkip */ +static void StatefulSkipTest(void) { + MemcpyState s; + char buf[128]; + + /* Small skip */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(&s, StatefulSkip(&s, 5), "StatefulSkip(5) retval"); + TEST_EQ(128 - 5, s.remaining_len, "StatefulSkip(5) len"); + TEST_PTR_EQ(buf + 5, s.remaining_buf, "StatefulSkip(5) buf"); + TEST_EQ(0, s.overrun, "StatefulSkip(5) overrun"); + + /* Use entire buffer */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(&s, StatefulSkip(&s, 128), "StatefulSkip(all) retval"); + TEST_EQ(0, s.remaining_len, "StatefulSkip(all) len"); + TEST_PTR_EQ(buf + 128, s.remaining_buf, "StatefulSkip(all) buf"); + TEST_EQ(0, s.overrun, "StatefulSkip(all) overrun"); + + /* Zero-length skip is ok (but meaningless) */ + TEST_PTR_EQ(&s, StatefulSkip(&s, 0), "StatefulSkip(0) retval"); + TEST_EQ(0, s.remaining_len, "StatefulSkip(0) len"); + TEST_PTR_EQ(buf + 128, s.remaining_buf, "StatefulSkip(0) buf"); + TEST_EQ(0, s.overrun, "StatefulSkip(0) overrun"); + + /* Can't use even one byte past that */ + TEST_PTR_EQ(NULL, StatefulSkip(&s, 1), "StatefulSkip(+1) retval"); + TEST_EQ(0, s.remaining_len, "StatefulSkip(+1) len"); + TEST_EQ(1, s.overrun, "StatefulSkip(+1) overrun"); + + /* Overrun */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(NULL, StatefulSkip(&s, 256), "StatefulSkip(256) retval"); + TEST_EQ(1, s.overrun, "StatefulSkip(256) overrun"); + /* Once overrun, always overrun, even if we now ask for a small skip */ + TEST_PTR_EQ(NULL, StatefulSkip(&s, 1), "StatefulSkip(256+1) retval"); + TEST_EQ(1, s.overrun, "StatefulSkip(256+1) overrun"); + + /* Overrun with potential wraparound */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(NULL, StatefulSkip(&s, -1), "StatefulSkip(-1) retval"); + TEST_EQ(1, s.overrun, "StatefulSkip(-1) overrun"); +} + + +/* Test StatefulMemset_r */ +static void StatefulMemset_rTest(void) { + MemcpyState s; + char buf[129]; + char want[129]; + + memset(want, 0, sizeof(want)); + memset(buf, 0, sizeof(buf)); + + /* Small sets */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(&s, StatefulMemset_r(&s, 'A', 5), "StatefulMemset_r(5) retval"); + TEST_EQ(128 - 5, s.remaining_len, "StatefulMemset_r(5) len"); + TEST_PTR_EQ(buf + 5, s.remaining_buf, "StatefulMemset_r(5) buf"); + /* Using strcmp() is a convenient way to check that we didn't + * overwrite the 0-byte following what we expected to set. */ + TEST_EQ(0, strcmp("AAAAA", buf), "StatefulMemset_r(5) contents"); + TEST_EQ(0, s.overrun, "StatefulMemset_r(5) overrun"); + TEST_PTR_EQ(&s, StatefulMemset_r(&s, 'B', 3), "StatefulMemset_r(3) retval"); + TEST_EQ(0, strcmp("AAAAABBB", buf), "StatefulMemset_r(3) contents"); + + /* Use entire buffer */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(&s, StatefulMemset_r(&s, 'C', 128), + "StatefulMemset_r(all) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemset_r(all) len"); + TEST_PTR_EQ(buf + 128, s.remaining_buf, "StatefulMemset_r(all) buf"); + TEST_EQ(0, s.overrun, "StatefulMemset_r(all) overrun"); + memset(want, 'C', 128); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemset_r(all) contents"); + + /* Zero-length set is ok (but meaningless) */ + TEST_PTR_EQ(&s, StatefulMemset_r(&s, 'D', 0), "StatefulMemset_r(0) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemset_r(0) len"); + TEST_PTR_EQ(buf + 128, s.remaining_buf, "StatefulMemset_r(0) buf"); + TEST_EQ(0, s.overrun, "StatefulMemset_r(0) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemset_r(0) contents"); + + /* Can't use even one byte past that */ + TEST_PTR_EQ(NULL, StatefulMemset_r(&s, 'E', 1), + "StatefulMemset_r(+1) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemset_r(+1) len"); + TEST_EQ(1, s.overrun, "StatefulMemset_r(+1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemset_r(+1) contents"); + + /* Overrun */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(NULL, StatefulMemset_r(&s, 'F', 256), + "StatefulMemset_r(256) retval"); + TEST_EQ(1, s.overrun, "StatefulMemset_r(256) overrun"); + /* Once overrun, always overrun, even if we now ask for a small skip */ + TEST_PTR_EQ(NULL, StatefulMemset_r(&s, 'G', 1), + "StatefulMemset_r(256+1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemset_r(256+1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemset_r(+1) contents"); + + /* Overrun with potential wraparound */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(NULL, StatefulMemset_r(&s, 'H', -1), + "StatefulMemset_r(-1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemset_r(-1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemset_r(+1) contents"); +} + + +/* Test StatefulMemcpy_r */ +static void StatefulMemcpy_rTest(void) { + MemcpyState s; + char buf[129]; + char want[129]; + char* src1 = "Doogie"; + char* src2 = "Howserrr"; + char* src3 = "WholeBuffr"; + + memset(want, 0, sizeof(want)); + memset(buf, 0, sizeof(buf)); + + /* Small copies */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(src1, StatefulMemcpy_r(&s, src1, 6), + "StatefulMemcpy_r(6) retval"); + TEST_EQ(128 - 6, s.remaining_len, "StatefulMemcpy_r(6) len"); + TEST_PTR_EQ(buf + 6, s.remaining_buf, "StatefulMemcpy_r(6) buf"); + /* Using strcmp() is a convenient way to check that we didn't + * overwrite the 0-byte following what we expected to copy. */ + TEST_EQ(0, strcmp("Doogie", buf), "StatefulMemcpy_r(6) contents"); + TEST_EQ(0, s.overrun, "StatefulMemcpy_r(6) overrun"); + TEST_PTR_EQ(src2, StatefulMemcpy_r(&s, src2, 8), + "StatefulMemcpy_r(8) retval"); + TEST_EQ(0, strcmp("DoogieHowserrr", buf), "StatefulMemcpy_r(8) contents"); + + /* Use entire buffer */ + memset(buf, 42, sizeof(buf)); + StatefulInit(&s, buf, 10); + TEST_PTR_EQ(src3, StatefulMemcpy_r(&s, src3, 10), + "StatefulMemcpy_r(all) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy_r(all) len"); + TEST_PTR_EQ(buf + 10, s.remaining_buf, "StatefulMemcpy_r(all) buf"); + TEST_EQ(0, s.overrun, "StatefulMemcpy_r(all) overrun"); + TEST_EQ(0, memcmp(src3, buf, 10), "StatefulMemcpy_r(all) contents"); + TEST_EQ(42, buf[10], "StatefulMemcpy_r(all) contents+1"); + + /* Zero-length copy is ok (but meaningless) */ + TEST_PTR_EQ(src1, StatefulMemcpy_r(&s, src1, 0), + "StatefulMemcpy_r(0) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy_r(0) len"); + TEST_PTR_EQ(buf + 10, s.remaining_buf, "StatefulMemcpy_r(0) buf"); + TEST_EQ(0, s.overrun, "StatefulMemcpy_r(0) overrun"); + TEST_EQ(42, buf[10], "StatefulMemcpy_r(0) contents+1"); + + /* Can't use even one byte past that */ + TEST_PTR_EQ(NULL, StatefulMemcpy_r(&s, src1, 1), + "StatefulMemcpy_r(+1) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy_r(+1) len"); + TEST_EQ(1, s.overrun, "StatefulMemcpy_r(+1) overrun"); + TEST_EQ(42, buf[10], "StatefulMemcpy_r(+1) contents"); + + /* Overrun */ + memset(buf, 0, sizeof(buf)); + StatefulInit(&s, buf, 8); + TEST_PTR_EQ(NULL, StatefulMemcpy_r(&s, "MoreThan8", 9), + "StatefulMemcpy_r(9) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy_r(9) overrun"); + /* Once overrun, always overrun, even if we now ask for a small skip */ + TEST_PTR_EQ(NULL, StatefulMemcpy_r(&s, "Less", 4), + "StatefulMemcpy_r(9+1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy_r(9+1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemcpy_r(+1) contents"); + + /* Overrun with potential wraparound */ + StatefulInit(&s, buf, 128); + TEST_PTR_EQ(NULL, StatefulMemcpy_r(&s, "FOO", -1), + "StatefulMemcpy_r(-1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy_r(-1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemcpy_r(+1) contents"); +} + + +/* Test StatefulMemcpy */ +static void StatefulMemcpyTest(void) { + MemcpyState s; + char buf[129]; + char want[129]; + char* src1 = "ThisIsATest"; + char* src2 = "ThisIsOnlyATest"; + + memset(want, 0, sizeof(want)); + memset(buf, 0, sizeof(buf)); + + /* Small copies */ + StatefulInit(&s, src1, 12); + TEST_PTR_EQ(buf, StatefulMemcpy(&s, buf, 6), "StatefulMemcpy(6) retval"); + TEST_EQ(6, s.remaining_len, "StatefulMemcpy(6) len"); + TEST_PTR_EQ(src1 + 6, s.remaining_buf, "StatefulMemcpy(6) buf"); + /* Using strcmp() is a convenient way to check that we didn't + * overwrite the 0-byte following what we expected to copy. */ + TEST_EQ(0, strcmp("ThisIs", buf), "StatefulMemcpy(6) contents"); + TEST_EQ(0, s.overrun, "StatefulMemcpy(6) overrun"); + TEST_PTR_EQ(buf, StatefulMemcpy(&s, buf, 5), "StatefulMemcpy(5) retval"); + /* Note that we shouldn't have copied the last byte out of the + * stateful buffer, so we don't overwrite the last character of the + * string that was in buf. */ + TEST_EQ(0, strcmp("ATests", buf), "StatefulMemcpy(5) contents"); + + /* Use entire buffer */ + memset(buf, 1, sizeof(buf)); + StatefulInit(&s, src2, 16); + TEST_PTR_EQ(buf, StatefulMemcpy(&s, buf, 16), "StatefulMemcpy(all) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy(all) len"); + TEST_PTR_EQ(src2 + 16, s.remaining_buf, "StatefulMemcpy(all) buf"); + TEST_EQ(0, s.overrun, "StatefulMemcpy(all) overrun"); + TEST_EQ(0, strcmp(src2, buf), "StatefulMemcpy(all) contents"); + + /* Zero-length copy is ok (but meaningless) */ + TEST_PTR_EQ(buf, StatefulMemcpy(&s, buf, 0), + "StatefulMemcpy(0) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy(0) len"); + TEST_PTR_EQ(src2 + 16, s.remaining_buf, "StatefulMemcpy(0) buf"); + TEST_EQ(0, s.overrun, "StatefulMemcpy(0) overrun"); + TEST_EQ(0, strcmp(src2, buf), "StatefulMemcpy(0) contents"); + + /* Can't use even one byte past that */ + TEST_PTR_EQ(NULL, StatefulMemcpy(&s, buf, 1), + "StatefulMemcpy(+1) retval"); + TEST_EQ(0, s.remaining_len, "StatefulMemcpy(+1) len"); + TEST_EQ(1, s.overrun, "StatefulMemcpy(+1) overrun"); + TEST_EQ(0, strcmp(src2, buf), "StatefulMemcpy(+1) contents"); + + /* Overrun */ + memset(buf, 0, sizeof(buf)); + StatefulInit(&s, "Small", 5); + TEST_PTR_EQ(NULL, StatefulMemcpy(&s, buf, 9), "StatefulMemcpy(9) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy(9) overrun"); + /* Once overrun, always overrun, even if we now ask for a small skip */ + TEST_PTR_EQ(NULL, StatefulMemcpy(&s, buf, 4), + "StatefulMemcpy(9+1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy(9+1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemcpy(+1) contents"); + + /* Overrun with potential wraparound */ + StatefulInit(&s, "Larger", 6); + TEST_PTR_EQ(NULL, StatefulMemcpy(&s, buf, -1), "StatefulMemcpy(-1) retval"); + TEST_EQ(1, s.overrun, "StatefulMemcpy(-1) overrun"); + TEST_EQ(0, memcmp(want, buf, sizeof(want)), "StatefulMemcpy(+1) contents"); +} + + +/* disable MSVC warnings on unused arguments */ +__pragma(warning (disable: 4100)) + +int main(int argc, char* argv[]) { + int error_code = 0; + + StatefulInitTest(); + StatefulSkipTest(); + StatefulMemset_rTest(); + StatefulMemcpy_rTest(); + StatefulMemcpyTest(); + + if (!gTestSuccess) + error_code = 255; + + return error_code; +} diff --git a/tests/test_common.c b/tests/test_common.c index e7e54933..6521fa0f 100644 --- a/tests/test_common.c +++ b/tests/test_common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +/* 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. * @@ -23,6 +23,7 @@ int TEST_EQ(int result, int expected_result, char* testname) { } else { fprintf(stderr, "%s Test " COL_RED "FAILED\n" COL_STOP, testname); + fprintf(stderr, " Expected: %d, got: %d\n", expected_result, result); gTestSuccess = 0; return 0; } @@ -35,6 +36,22 @@ int TEST_NEQ(int result, int not_expected_result, char* testname) { } 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; + return 0; + } +} + +int TEST_PTR_EQ(const void* result, const void* expected_result, + char* testname) { + if (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: 0x%lx, got: 0x%lx\n", (long)expected_result, + (long)result); gTestSuccess = 0; return 0; } diff --git a/tests/test_common.h b/tests/test_common.h index ed3e6039..532e7b2f 100644 --- a/tests/test_common.h +++ b/tests/test_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +/* 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. * @@ -12,10 +12,16 @@ extern int gTestSuccess; /* Return 1 if result is equal to expected_result, else return 0. * Also update the global gTestSuccess flag if test fails. */ int TEST_EQ(int result, int expected_result, char* testname); + /* Return 0 if result is equal to not_expected_result, else return 1. * 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. */ +int TEST_PTR_EQ(const void* result, const void* expected_result, + char* testname); + /* ANSI Color coding sequences. * * Don't use \e as MSC does not recognize it as a valid escape sequence. diff --git a/tests/utility_tests.c b/tests/utility_tests.c index b8a1da93..9fc1dab5 100644 --- a/tests/utility_tests.c +++ b/tests/utility_tests.c @@ -26,7 +26,7 @@ static void MemsetTest(void) { /* Simple fill */ memset(want, 123, 5); - TEST_EQ(0, dest - (char*)Memset(dest, 123, 5), "Memset() returns dest"); + TEST_PTR_EQ(dest, Memset(dest, 123, 5), "Memset() returns dest"); TEST_EQ(0, memcmp(dest, want, 128), "Memset()"); /* Filling length 0 does nothing */ |