diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..c9c5f56 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,15 @@ +#include "munit.h" + +extern MunitTest bytes_tests[]; +extern MunitTest memory_tests[]; + +int main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) { + MunitSuite suites[] = { + {"/memory", memory_tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE}, + {nullptr, nullptr, nullptr, 0, MUNIT_SUITE_OPTION_NONE}, + }; + + MunitSuite master_suite = {"/binprobe", nullptr, suites, 1, MUNIT_SUITE_OPTION_NONE}; + + return munit_suite_main(&master_suite, nullptr, argc, argv); +} diff --git a/tests/memory.c b/tests/memory.c new file mode 100644 index 0000000..a30f53c --- /dev/null +++ b/tests/memory.c @@ -0,0 +1,641 @@ +#include "../src/common/memory.h" +#include "munit.h" +#include +#include + +MunitResult test_alloc_success(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Test various allocation sizes + err = mem_alloc(&ptr, 1, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + mem_free(&ptr); + munit_assert_null(ptr); + + err = mem_alloc(&ptr, 10, sizeof(int)); + munit_assert_null(err); + munit_assert_not_null(ptr); + mem_free(&ptr); + + err = mem_alloc(&ptr, 1024, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + mem_free(&ptr); + + err = mem_alloc(&ptr, 100, 100); + munit_assert_null(err); + munit_assert_not_null(ptr); + mem_free(&ptr); + + return MUNIT_OK; +} + +MunitResult test_alloc_zeroed(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err = mem_alloc(&ptr, 256, sizeof(uint8_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify all bytes are zero-initialized + uint8_t *bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 256; i++) { + munit_assert_uint8(bytes[i], ==, 0); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_alloc_invalid_null_ptr(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + error_t *err = mem_alloc(nullptr, 10, sizeof(int)); + munit_assert_ptr_equal(err, err_invalid_parameters); + + return MUNIT_OK; +} + +MunitResult test_alloc_invalid_zero_n(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err = mem_alloc(&ptr, 0, sizeof(int)); + munit_assert_ptr_equal(err, err_invalid_parameters); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_alloc_invalid_zero_size(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err = mem_alloc(&ptr, 10, 0); + munit_assert_ptr_equal(err, err_invalid_parameters); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_alloc_invalid_zero_both(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err = mem_alloc(&ptr, 0, 0); + munit_assert_ptr_equal(err, err_invalid_parameters); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_alloc_integer_overflow(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err = mem_alloc(&ptr, SIZE_MAX, SIZE_MAX); + munit_assert_ptr_equal(err, err_integer_overflow); + munit_assert_null(ptr); + + // Test case where multiplication would overflow + err = mem_alloc(&ptr, SIZE_MAX / 2 + 1, 2); + munit_assert_ptr_equal(err, err_integer_overflow); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_resize_grow(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate initial memory + err = mem_alloc(&ptr, 10, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write a pattern to the initial allocation + uint32_t *ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + ints[i] = 0xDEADBEEF + i; + } + + // Resize to larger size + err = mem_resize(&ptr, 20, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify original data is preserved + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + munit_assert_uint32(ints[i], ==, 0xDEADBEEF + i); + } + + // Verify new bytes are zero-initialized + uint8_t *bytes = (uint8_t *)ptr; + for (size_t i = 10 * sizeof(uint32_t); i < 20 * sizeof(uint32_t); i++) { + munit_assert_uint8(bytes[i], ==, 0); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_shrink(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate initial memory + err = mem_alloc(&ptr, 20, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write a pattern to the initial allocation + uint32_t *ints = (uint32_t *)ptr; + for (size_t i = 0; i < 20; i++) { + ints[i] = 0xCAFEBABE + i; + } + + // Resize to smaller size + err = mem_resize(&ptr, 10, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify preserved data is intact + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + munit_assert_uint32(ints[i], ==, 0xCAFEBABE + i); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_same_size(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate memory + err = mem_alloc(&ptr, 15, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write a pattern + uint32_t *ints = (uint32_t *)ptr; + for (size_t i = 0; i < 15; i++) { + ints[i] = 0x12345678 + i; + } + + // Resize to the same size + err = mem_resize(&ptr, 15, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify data is unchanged + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 15; i++) { + munit_assert_uint32(ints[i], ==, 0x12345678 + i); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_invalid_params(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Test null pointer parameter + err = mem_resize(nullptr, 10, sizeof(int)); + munit_assert_ptr_equal(err, err_invalid_parameters); + + // Test valid ptr but *ptr is nullptr + err = mem_resize(&ptr, 10, sizeof(int)); + munit_assert_ptr_equal(err, err_invalid_parameters); + + // Allocate memory for other tests + err = mem_alloc(&ptr, 10, sizeof(int)); + munit_assert_null(err); + + // Test zero n + err = mem_resize(&ptr, 0, sizeof(int)); + munit_assert_ptr_equal(err, err_invalid_parameters); + + // Test zero size + err = mem_resize(&ptr, 10, 0); + munit_assert_ptr_equal(err, err_invalid_parameters); + + // Test integer overflow + err = mem_resize(&ptr, SIZE_MAX, SIZE_MAX); + munit_assert_ptr_equal(err, err_integer_overflow); + + // Test case where multiplication would overflow + err = mem_resize(&ptr, SIZE_MAX / 2 + 1, 2); + munit_assert_ptr_equal(err, err_integer_overflow); + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_failure_preserves_ptr(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + void *original_ptr; + error_t *err; + + // Allocate initial memory + err = mem_alloc(&ptr, 10, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write a pattern to verify pointer validity + uint32_t *ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + ints[i] = 0xBEEFCAFE + i; + } + + // Save original pointer + original_ptr = ptr; + + // Attempt resize with overflow - should fail + err = mem_resize(&ptr, SIZE_MAX, SIZE_MAX); + munit_assert_ptr_equal(err, err_integer_overflow); + + // Verify ptr is unchanged + munit_assert_ptr_equal(ptr, original_ptr); + + // Verify data is still intact + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + munit_assert_uint32(ints[i], ==, 0xBEEFCAFE + i); + } + + // Verify we can still operate on the pointer (resize successfully) + err = mem_resize(&ptr, 20, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify original data preserved after successful resize + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 10; i++) { + munit_assert_uint32(ints[i], ==, 0xBEEFCAFE + i); + } + + // Verify we can free the pointer + mem_free(&ptr); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_resize_minimal_to_minimal(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate minimal size + err = mem_alloc(&ptr, 1, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write a byte + uint8_t *byte = (uint8_t *)ptr; + *byte = 0x42; + + // Resize to same minimal size + err = mem_resize(&ptr, 1, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify data is preserved + byte = (uint8_t *)ptr; + munit_assert_uint8(*byte, ==, 0x42); + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_shrink_then_grow_zeroinit(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate 100 bytes + err = mem_alloc(&ptr, 100, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write pattern throughout + uint8_t *bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 100; i++) { + bytes[i] = (uint8_t)(0xAA + (i % 16)); + } + + // Shrink to 50 bytes + err = mem_resize(&ptr, 50, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify first 50 bytes preserved + bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 50; i++) { + munit_assert_uint8(bytes[i], ==, (uint8_t)(0xAA + (i % 16))); + } + + // Grow to 150 bytes + err = mem_resize(&ptr, 150, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify first 50 bytes still preserved + bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 50; i++) { + munit_assert_uint8(bytes[i], ==, (uint8_t)(0xAA + (i % 16))); + } + + // Verify bytes 50-149 are zero-initialized + for (size_t i = 50; i < 150; i++) { + munit_assert_uint8(bytes[i], ==, 0); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_odd_boundaries_zeroinit(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate odd-sized 13 bytes + err = mem_alloc(&ptr, 13, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write pattern to all 13 bytes + uint8_t *bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 13; i++) { + bytes[i] = (uint8_t)(0xCC + i); + } + + // Resize to odd-sized 27 bytes + err = mem_resize(&ptr, 27, 1); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Verify original 13 bytes preserved + bytes = (uint8_t *)ptr; + for (size_t i = 0; i < 13; i++) { + munit_assert_uint8(bytes[i], ==, (uint8_t)(0xCC + i)); + } + + // Verify new 14 bytes (13-26) are zero-initialized + for (size_t i = 13; i < 27; i++) { + munit_assert_uint8(bytes[i], ==, 0); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_resize_multiple_times(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Start with small allocation + err = mem_alloc(&ptr, 5, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Write initial pattern + uint32_t *ints = (uint32_t *)ptr; + for (size_t i = 0; i < 5; i++) { + ints[i] = 0xAAAA0000 + i; + } + + // Resize 1: grow to 10 + err = mem_resize(&ptr, 10, sizeof(uint32_t)); + munit_assert_null(err); + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 5; i++) { + munit_assert_uint32(ints[i], ==, 0xAAAA0000 + i); + } + + // Resize 2: grow to 20 + err = mem_resize(&ptr, 20, sizeof(uint32_t)); + munit_assert_null(err); + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 5; i++) { + munit_assert_uint32(ints[i], ==, 0xAAAA0000 + i); + } + + // Resize 3: shrink to 15 + err = mem_resize(&ptr, 15, sizeof(uint32_t)); + munit_assert_null(err); + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 5; i++) { + munit_assert_uint32(ints[i], ==, 0xAAAA0000 + i); + } + + // Resize 4: shrink to 3 + err = mem_resize(&ptr, 3, sizeof(uint32_t)); + munit_assert_null(err); + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 3; i++) { + munit_assert_uint32(ints[i], ==, 0xAAAA0000 + i); + } + + // Resize 5: grow back to 25 + err = mem_resize(&ptr, 25, sizeof(uint32_t)); + munit_assert_null(err); + ints = (uint32_t *)ptr; + for (size_t i = 0; i < 3; i++) { + munit_assert_uint32(ints[i], ==, 0xAAAA0000 + i); + } + + mem_free(&ptr); + return MUNIT_OK; +} + +MunitResult test_free_normal(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate memory + err = mem_alloc(&ptr, 50, sizeof(uint32_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Free it + mem_free(&ptr); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_free_null_ptr_param(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + // Freeing with nullptr parameter should be a no-op (not crash) + mem_free(nullptr); + + return MUNIT_OK; +} + +MunitResult test_free_null_deref_ptr(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + + // Freeing a pointer that points to nullptr should be a no-op + mem_free(&ptr); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_free_double_free(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate memory + err = mem_alloc(&ptr, 10, sizeof(int)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Free once + mem_free(&ptr); + munit_assert_null(ptr); + + // Free again - should be a no-op since ptr is now nullptr + mem_free(&ptr); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_free_after_resize(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr = nullptr; + error_t *err; + + // Allocate memory + err = mem_alloc(&ptr, 5, sizeof(uint64_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Resize it + err = mem_resize(&ptr, 20, sizeof(uint64_t)); + munit_assert_null(err); + munit_assert_not_null(ptr); + + // Free the resized memory + mem_free(&ptr); + munit_assert_null(ptr); + + return MUNIT_OK; +} + +MunitResult test_free_multiple_allocations(const MunitParameter params[], void *data) { + (void)params; + (void)data; + + void *ptr1 = nullptr, *ptr2 = nullptr, *ptr3 = nullptr; + error_t *err; + + // Allocate multiple blocks + err = mem_alloc(&ptr1, 10, sizeof(int)); + munit_assert_null(err); + munit_assert_not_null(ptr1); + + err = mem_alloc(&ptr2, 20, sizeof(char)); + munit_assert_null(err); + munit_assert_not_null(ptr2); + + err = mem_alloc(&ptr3, 30, sizeof(double)); + munit_assert_null(err); + munit_assert_not_null(ptr3); + + // Free them in different order than allocated + mem_free(&ptr2); + munit_assert_null(ptr2); + + mem_free(&ptr1); + munit_assert_null(ptr1); + + mem_free(&ptr3); + munit_assert_null(ptr3); + + return MUNIT_OK; +} + +MunitTest memory_tests[] = { + {.name = "/alloc/success", .test = test_alloc_success}, + {.name = "/alloc/zeroed", .test = test_alloc_zeroed}, + {.name = "/alloc/invalid_null_ptr", .test = test_alloc_invalid_null_ptr}, + {.name = "/alloc/invalid_zero_n", .test = test_alloc_invalid_zero_n}, + {.name = "/alloc/invalid_zero_size", .test = test_alloc_invalid_zero_size}, + {.name = "/alloc/invalid_zero_both", .test = test_alloc_invalid_zero_both}, + {.name = "/alloc/integer_overflow", .test = test_alloc_integer_overflow}, + {.name = "/resize/grow", .test = test_resize_grow}, + {.name = "/resize/shrink", .test = test_resize_shrink}, + {.name = "/resize/same_size", .test = test_resize_same_size}, + {.name = "/resize/invalid_params", .test = test_resize_invalid_params}, + {.name = "/resize/failure_preserves_ptr", .test = test_resize_failure_preserves_ptr}, + {.name = "/resize/minimal_to_minimal", .test = test_resize_minimal_to_minimal}, + {.name = "/resize/shrink_then_grow_zeroinit", .test = test_resize_shrink_then_grow_zeroinit}, + {.name = "/resize/odd_boundaries_zeroinit", .test = test_resize_odd_boundaries_zeroinit}, + {.name = "/resize/multiple_times", .test = test_resize_multiple_times}, + {.name = "/free/normal", .test = test_free_normal}, + {.name = "/free/null_ptr_param", .test = test_free_null_ptr_param}, + {.name = "/free/null_deref_ptr", .test = test_free_null_deref_ptr}, + {.name = "/free/double_free", .test = test_free_double_free}, + {.name = "/free/after_resize", .test = test_free_after_resize}, + {.name = "/free/multiple_allocations", .test = test_free_multiple_allocations}, + {} +};