#include #include #include #include #include typedef struct { char* arr; size_t size; size_t capacity; } Vec8_t; Vec8_t create(Vec8_t* input) { Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 }; if(input == nullptr) { vec.arr = calloc(vec.capacity, sizeof(char)); return vec; } if (input->size > 0) { vec.size = input->size + 1; } if (input->capacity >= vec.capacity) { vec.capacity = 2 * input->capacity; } vec.arr = calloc(vec.capacity, sizeof(char)); return vec; } void delete(Vec8_t* vec) { if (vec->arr != nullptr) free(vec->arr); vec->arr = nullptr; } char at(Vec8_t* vec, int idx) { if (vec == nullptr) return -2; if (vec->arr == nullptr) return -3; if (vec->size <= idx) return -4; if (vec != nullptr && vec->arr != nullptr && vec->size > idx) { return vec->arr[idx]; } return -1; } int begin(Vec8_t* vec) { if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0; return -1; } int end(Vec8_t* vec) { if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1); return -1; } char front(Vec8_t* vec) { return at(vec, 0); } char back(Vec8_t* vec) { if (vec) return at(vec, (int)vec->size - 1); return -1; } Vec8_t add_back(Vec8_t* vec, char val) { Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 }; if (vec->size < vec->capacity) { vec->arr[vec->size] = val; vec->size++; return *vec; } if (vec->size >= vec->capacity) { Vec8_t nvec = create(vec); memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char)); if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) { nvec.arr[vec->size] = val; } free(vec->arr); return nvec; } return ret; } int tests_passed = 0; int tests_failed = 0; int test_num = 0; void test(const char* name, int passed) { test_num++; if (passed) { printf("[PASS] #%d: %s\n", test_num, name); tests_passed++; } else { printf("[FAIL] #%d: %s\n", test_num, name); tests_failed++; } } int main() { printf("=== EDGE CASE BRUTAL TESTS ===\n\n"); Vec8_t vec; char* ptr; // === Test 1: Use after reallocation (expected behavior) === printf("--- Reallocation Behavior ---\n"); vec = create(nullptr); for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i)); ptr = vec.arr; // save pointer (will be freed on next realloc) for (int i = 0; i < 100; i++) vec = add_back(&vec, (char)('0' + (i % 10))); // Pointer changes after reallocation - this is expected test("after reallocation, vec.arr is new", ptr != vec.arr); test("data preserved in new vec", vec.arr[0] == 'A' && vec.arr[9] == 'J'); test("vec must be reassigned: vec = add_back()", 1); delete(&vec); // === Test 2: Stack-allocated struct (not created via create) === printf("\n--- Stack-Allocated Struct ---\n"); Vec8_t stack_vec; stack_vec.arr = nullptr; stack_vec.size = 0; stack_vec.capacity = 0; // This will fail because capacity starts at 0, not 4 // and there's no way to use add_back properly // BUG: User can't easily use functions without calling create() test("stack vec: add_back modifies nothing (capacity 0)", 1); // informational // === Test 3: Integer overflow potential === printf("\n--- Integer Overflow ---\n"); // If someone passes SIZE_MAX or near-max to create // The doubling could overflow vec = create(nullptr); vec.capacity = (size_t)-1; // max size_t vec.size = 0; vec.arr = calloc(1, 1); // tiny allocation // BUG: 2 * capacity overflows to 0 size_t doubled = 2 * vec.capacity; test("2 * SIZE_MAX overflows (demonstrates overflow)", 1); // informational free(vec.arr); // === Test 4: Negative index with signed int === printf("\n--- Signed/Unsigned Mixing ---\n"); vec = create(nullptr); vec = add_back(&vec, 'X'); int neg_idx = -1; // BUG: -1 as unsigned is huge, causing out-of-bounds access char result = at(&vec, neg_idx); test("at() with negative -1 returns error", result == -4); neg_idx = -2147483648; result = at(&vec, neg_idx); test("at() with INT_MIN returns error", result == -4); delete(&vec); // === Test 5: Size very large === printf("\n--- Huge Size Values ---\n"); vec = create(nullptr); vec.size = 1000000; // manually corrupt size (bad user code) vec.capacity = 4; vec.arr = calloc(4, 1); // BUG: size > capacity, add_back will try to create with size+1 = 1000001 // which allocates based on old logic result = at(&vec, 0); // Should still work for valid indices within actual capacity test("at() works for valid idx despite bad size", result == 0); delete(&vec); // === Test 6: Uninitialized struct === printf("\n--- Uninitialized Struct ---\n"); Vec8_t uninit; // BUG: arr, size, capacity contain garbage // Any function call is undefined behavior printf(" Calling at() on uninitialized struct...\n"); printf(" This is undefined behavior - results unpredictable!\n"); test("uninitialized: expect garbage or crash (UB)", 1); // informational // === Test 7: Capacity not matching allocation === printf("\n--- Capacity Mismatch ---\n"); vec = create(nullptr); vec.arr = realloc(vec.arr, 1000); // manually grow vec.capacity = 1000; // but don't tell anyone for (int i = 0; i < 50; i++) vec = add_back(&vec, (char)i); test("manual realloc: capacity grows (add_back doesn't know)", 1); // informational delete(&vec); // === Test 8: memcpy with null === printf("\n--- Memcpy Edge Cases ---\n"); vec = create(nullptr); vec.size = 0; // BUG: memcpy(&vec.arr[0], &vec.arr[0], 0) - this is actually fine (no-op) for (int i = 0; i < 5; i++) vec = add_back(&vec, (char)('A' + i)); test("empty memcpy edge case: works fine", vec.size == 5); delete(&vec); // === Test 9: Return value ignored === printf("\n--- Ignored Return Values ---\n"); vec = create(nullptr); add_back(&vec, 'X'); // Modifies in-place when capacity available test("add_back modifies in-place when capacity available", vec.size == 1); test("data stored correctly", vec.arr[0] == 'X'); // Fill to capacity (4): size goes 1->4 for (int i = 0; i < 3; i++) vec = add_back(&vec, (char)('A' + i)); // Now size=4, cap=4 // Add more: this triggers reallocation (size >= capacity) char* old_ptr = vec.arr; vec = add_back(&vec, 'Z'); // reallocates! size becomes 5, cap becomes 8 test("reallocation triggered", old_ptr != vec.arr); test("realloc: data preserved in new vec", vec.arr[4] == 'Z'); delete(&vec); // === Test 10: Multiple vectors, aliasing === printf("\n--- Aliasing Issues ---\n"); Vec8_t v1 = create(nullptr); Vec8_t v2 = create(nullptr); for (int i = 0; i < 5; i++) v1 = add_back(&v1, (char)('A' + i)); for (int i = 0; i < 5; i++) v2 = add_back(&v2, (char)('1' + i)); test("v1 and v2 are separate", v1.arr != v2.arr); test("v1 data correct", v1.arr[0] == 'A'); test("v2 data correct", v2.arr[0] == '1'); delete(&v1); delete(&v2); // === Test 11: Freeing already-freed memory === printf("\n--- Double Free ---\n"); vec = create(nullptr); vec = add_back(&vec, 'X'); char* data = vec.arr; delete(&vec); free(data); // BUG: double free! printf(" Double free detected (would crash with sanitizers)\n"); test("double free: undefined behavior", 1); // informational // === Test 12: Realloc failure === printf("\n--- Realloc Failure ---\n"); // Hard to test without mocking, but conceptually: // If malloc/calloc fails, the library doesn't check // BUG: nvec.arr could be NULL, then memcpy with NULL crashes printf(" If realloc fails: nvec.arr is NULL, memcpy crashes\n"); test("no malloc failure check in create()", 1); // informational // === Test 13: Reading freed memory === printf("\n--- Use After Delete ---\n"); vec = create(nullptr); for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)i); delete(&vec); // BUG: vec.arr is nullptr, vec.size is still 10 // User might expect size to be 0 after delete test("after delete: arr is null", vec.arr == nullptr); test("after delete: size is still 10 (not cleared)", vec.size == 10); test("after delete: accessing returns error", at(&vec, 0) == -3); // === Test 14: Size 0 with capacity 0 === printf("\n--- Size/Capacity Edge ---\n"); vec.arr = malloc(100); vec.size = 0; vec.capacity = 100; // BUG: begin() checks size > 0, so returns -1 even though we have capacity test("begin() returns -1 when size=0", begin(&vec) == -1); test("end() returns -1 when size=0", end(&vec) == -1); test("front() returns -4 when size=0", front(&vec) == -4); test("back() returns -4 when size=0", back(&vec) == -4); free(vec.arr); // === Test 15: Concurrent modification (conceptual) === printf("\n--- Iterator Invalidation Concept ---\n"); vec = create(nullptr); for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i)); // If user iterates while adding: // for (int i = 0; i < 1000; i++) { // if (i == 10) vec = add_back(&vec, 'Z'); // reallocation! // use vec.arr[i]; // BUG: pointer invalid after realloc // } printf(" Iteration + reallocation invalidates pointers\n"); test("conceptual: iteration during add_back is unsafe", 1); delete(&vec); // === Summary === printf("\n=== Summary ===\n"); printf("Passed: %d\n", tests_passed); printf("Failed: %d\n", tests_failed); printf("Note: 'informational' tests always pass, real bugs above\n"); return 0; }