summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/modules/vector-sets/fastjson_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/modules/vector-sets/fastjson_test.c')
-rw-r--r--examples/redis-unstable/modules/vector-sets/fastjson_test.c406
1 files changed, 0 insertions, 406 deletions
diff --git a/examples/redis-unstable/modules/vector-sets/fastjson_test.c b/examples/redis-unstable/modules/vector-sets/fastjson_test.c
deleted file mode 100644
index 1ea76a9..0000000
--- a/examples/redis-unstable/modules/vector-sets/fastjson_test.c
+++ /dev/null
@@ -1,406 +0,0 @@
1/* fastjson_test.c - Stress test for fastjson.c
2 *
3 * This performs boundary and corruption tests to ensure
4 * the JSON parser handles edge cases without accessing
5 * memory outside the bounds of the input.
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <signal.h>
13#include <time.h>
14#include <sys/mman.h>
15#include <sys/types.h>
16#include <fcntl.h>
17#include <errno.h>
18#include <setjmp.h>
19
20/* Page size constant - typically 4096 or 16k bytes (Apple Silicon).
21 * We use 16k so that it will work on both, but not with Linux huge pages. */
22#define PAGE_SIZE 4096*4
23#define MAX_JSON_SIZE (PAGE_SIZE - 128) /* Keep some margin */
24#define MAX_FIELD_SIZE 64
25#define NUM_TEST_ITERATIONS 100000
26#define NUM_CORRUPTION_TESTS 10000
27#define NUM_BOUNDARY_TESTS 10000
28
29/* Test state tracking */
30static char *safe_page = NULL; /* Start of readable/writable page */
31static char *unsafe_page = NULL; /* Start of inaccessible guard page */
32static int boundary_violation = 0; /* Flag for boundary violations */
33static jmp_buf jmpbuf; /* For signal handling */
34static int tests_passed = 0;
35static int tests_failed = 0;
36static int corruptions_passed = 0;
37static int boundary_tests_passed = 0;
38
39/* Test metadata for tracking */
40typedef struct {
41 char *json;
42 size_t json_len;
43 char field[MAX_FIELD_SIZE];
44 size_t field_len;
45 int expected_result;
46} test_case_t;
47
48/* Forward declarations for test JSON generation */
49char *generate_random_json(size_t *len, char *field, size_t *field_len, int *has_field);
50void corrupt_json(char *json, size_t len);
51void setup_test_memory(void);
52void cleanup_test_memory(void);
53void run_normal_tests(void);
54void run_corruption_tests(void);
55void run_boundary_tests(void);
56void print_test_summary(void);
57
58/* Signal handler for segmentation violations */
59static void sigsegv_handler(int sig) {
60 boundary_violation = 1;
61 printf("Boundary violation detected! Caught signal %d\n", sig);
62 longjmp(jmpbuf, 1);
63}
64
65/* Wrapper for jsonExtractField to check for boundary violations */
66exprtoken *safe_extract_field(const char *json, size_t json_len,
67 const char *field, size_t field_len) {
68 boundary_violation = 0;
69
70 if (setjmp(jmpbuf) == 0) {
71 return jsonExtractField(json, json_len, field, field_len);
72 } else {
73 return NULL; /* Return NULL if boundary violation occurred */
74 }
75}
76
77/* Setup two adjacent memory pages - one readable/writable, one inaccessible */
78void setup_test_memory(void) {
79 /* Request a page of memory, with specific alignment. We rely on the
80 * fact that hopefully the page after that will cause a segfault if
81 * accessed. */
82 void *region = mmap(NULL, PAGE_SIZE,
83 PROT_READ | PROT_WRITE,
84 MAP_PRIVATE | MAP_ANONYMOUS,
85 -1, 0);
86
87 if (region == MAP_FAILED) {
88 perror("mmap failed");
89 exit(EXIT_FAILURE);
90 }
91
92 safe_page = (char*)region;
93 unsafe_page = safe_page + PAGE_SIZE;
94 // Uncomment to make sure it crashes :D
95 // printf("%d\n", unsafe_page[5]);
96
97 /* Set up signal handlers for memory access violations */
98 struct sigaction sa;
99 sa.sa_handler = sigsegv_handler;
100 sigemptyset(&sa.sa_mask);
101 sa.sa_flags = 0;
102
103 sigaction(SIGSEGV, &sa, NULL);
104 sigaction(SIGBUS, &sa, NULL);
105}
106
107void cleanup_test_memory(void) {
108 if (safe_page != NULL) {
109 munmap(safe_page, PAGE_SIZE);
110 safe_page = NULL;
111 unsafe_page = NULL;
112 }
113}
114
115/* Generate random strings with proper escaping for JSON */
116void generate_random_string(char *buffer, size_t max_len) {
117 static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
118 size_t len = 1 + rand() % (max_len - 2); /* Ensure at least 1 char */
119
120 for (size_t i = 0; i < len; i++) {
121 buffer[i] = charset[rand() % (sizeof(charset) - 1)];
122 }
123 buffer[len] = '\0';
124}
125
126/* Generate random numbers as strings */
127void generate_random_number(char *buffer, size_t max_len) {
128 double num = (double)rand() / RAND_MAX * 1000.0;
129
130 /* Occasionally make it negative or add decimal places */
131 if (rand() % 5 == 0) num = -num;
132 if (rand() % 3 != 0) num += (double)(rand() % 100) / 100.0;
133
134 snprintf(buffer, max_len, "%.6g", num);
135}
136
137/* Generate a random field name */
138void generate_random_field(char *field, size_t *field_len) {
139 generate_random_string(field, MAX_FIELD_SIZE / 2);
140 *field_len = strlen(field);
141}
142
143/* Generate a random JSON object with fields */
144char *generate_random_json(size_t *len, char *field, size_t *field_len, int *has_field) {
145 char *json = malloc(MAX_JSON_SIZE);
146 if (json == NULL) {
147 perror("malloc");
148 exit(EXIT_FAILURE);
149 }
150
151 char buffer[MAX_JSON_SIZE / 4]; /* Buffer for generating values */
152 int pos = 0;
153 int num_fields = 1 + rand() % 10; /* Random number of fields */
154 int target_field_index = rand() % num_fields; /* Which field to return */
155
156 /* Start the JSON object */
157 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "{");
158
159 /* Generate random field/value pairs */
160 for (int i = 0; i < num_fields; i++) {
161 /* Add a comma if not the first field */
162 if (i > 0) {
163 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, ", ");
164 }
165
166 /* Generate a field name */
167 if (i == target_field_index) {
168 /* This is our target field - save it for the caller */
169 generate_random_field(field, field_len);
170 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "\"%s\": ", field);
171 *has_field = 1;
172 /* Sometimes change the last char so that it will not match. */
173 if (rand() % 2) {
174 *has_field = 0;
175 field[*field_len-1] = '!';
176 }
177 } else {
178 generate_random_string(buffer, MAX_FIELD_SIZE / 4);
179 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "\"%s\": ", buffer);
180 }
181
182 /* Generate a random value type */
183 int value_type = rand() % 5;
184 switch (value_type) {
185 case 0: /* String */
186 generate_random_string(buffer, MAX_JSON_SIZE / 8);
187 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "\"%s\"", buffer);
188 break;
189
190 case 1: /* Number */
191 generate_random_number(buffer, MAX_JSON_SIZE / 8);
192 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "%s", buffer);
193 break;
194
195 case 2: /* Boolean: true */
196 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "true");
197 break;
198
199 case 3: /* Boolean: false */
200 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "false");
201 break;
202
203 case 4: /* Null */
204 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "null");
205 break;
206
207 case 5: /* Array (simple) */
208 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "[");
209 int array_items = 1 + rand() % 5;
210 for (int j = 0; j < array_items; j++) {
211 if (j > 0) pos += snprintf(json + pos, MAX_JSON_SIZE - pos, ", ");
212
213 /* Array items - either number or string */
214 if (rand() % 2) {
215 generate_random_number(buffer, MAX_JSON_SIZE / 16);
216 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "%s", buffer);
217 } else {
218 generate_random_string(buffer, MAX_JSON_SIZE / 16);
219 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "\"%s\"", buffer);
220 }
221 }
222 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "]");
223 break;
224 }
225 }
226
227 /* Close the JSON object */
228 pos += snprintf(json + pos, MAX_JSON_SIZE - pos, "}");
229 *len = pos;
230
231 return json;
232}
233
234/* Corrupt JSON by replacing random characters */
235void corrupt_json(char *json, size_t len) {
236 if (len < 2) return; /* Too short to corrupt safely */
237
238 /* Corrupt 1-3 characters */
239 int num_corruptions = 1 + rand() % 3;
240 for (int i = 0; i < num_corruptions; i++) {
241 size_t pos = rand() % len;
242 char corruption = " \t\n{}[]\":,0123456789abcdefXYZ"[rand() % 30];
243 json[pos] = corruption;
244 }
245}
246
247/* Run standard parser tests with generated valid JSON */
248void run_normal_tests(void) {
249 printf("Running normal JSON extraction tests...\n");
250
251 for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
252 char field[MAX_FIELD_SIZE] = {0};
253 size_t field_len = 0;
254 size_t json_len = 0;
255 int has_field = 0;
256
257 /* Generate random JSON */
258 char *json = generate_random_json(&json_len, field, &field_len, &has_field);
259
260 /* Use valid field to test parser */
261 exprtoken *token = safe_extract_field(json, json_len, field, field_len);
262
263 /* Check if we got a token as expected */
264 if (has_field && token != NULL) {
265 exprTokenRelease(token);
266 tests_passed++;
267 } else if (!has_field && token == NULL) {
268 tests_passed++;
269 } else {
270 tests_failed++;
271 }
272
273 /* Test with a non-existent field */
274 char nonexistent_field[MAX_FIELD_SIZE] = "nonexistent_field";
275 token = safe_extract_field(json, json_len, nonexistent_field, strlen(nonexistent_field));
276
277 if (token == NULL) {
278 tests_passed++;
279 } else {
280 exprTokenRelease(token);
281 tests_failed++;
282 }
283
284 free(json);
285 }
286}
287
288/* Run tests with corrupted JSON */
289void run_corruption_tests(void) {
290 printf("Running JSON corruption tests...\n");
291
292 for (int i = 0; i < NUM_CORRUPTION_TESTS; i++) {
293 char field[MAX_FIELD_SIZE] = {0};
294 size_t field_len = 0;
295 size_t json_len = 0;
296 int has_field = 0;
297
298 /* Generate random JSON */
299 char *json = generate_random_json(&json_len, field, &field_len, &has_field);
300
301 /* Make a copy and corrupt it */
302 char *corrupted = malloc(json_len + 1);
303 if (!corrupted) {
304 perror("malloc");
305 free(json);
306 exit(EXIT_FAILURE);
307 }
308
309 memcpy(corrupted, json, json_len + 1);
310 corrupt_json(corrupted, json_len);
311
312 /* Test with corrupted JSON */
313 exprtoken *token = safe_extract_field(corrupted, json_len, field, field_len);
314
315 /* We're just testing that it doesn't crash or access invalid memory */
316 if (boundary_violation) {
317 printf("Boundary violation with corrupted JSON!\n");
318 tests_failed++;
319 } else {
320 if (token != NULL) {
321 exprTokenRelease(token);
322 }
323 corruptions_passed++;
324 }
325
326 free(corrupted);
327 free(json);
328 }
329}
330
331/* Run tests at memory boundaries */
332void run_boundary_tests(void) {
333 printf("Running memory boundary tests...\n");
334
335 for (int i = 0; i < NUM_BOUNDARY_TESTS; i++) {
336 char field[MAX_FIELD_SIZE] = {0};
337 size_t field_len = 0;
338 size_t json_len = 0;
339 int has_field = 0;
340
341 /* Generate random JSON */
342 char *temp_json = generate_random_json(&json_len, field, &field_len, &has_field);
343
344 /* Truncate the JSON to a random length */
345 size_t truncated_len = 1 + rand() % json_len;
346
347 /* Place at the edge of the safe page */
348 size_t offset = PAGE_SIZE - truncated_len;
349 memcpy(safe_page + offset, temp_json, truncated_len);
350
351 /* Test parsing with non-existent field (forcing it to scan to end) */
352 char nonexistent_field[MAX_FIELD_SIZE] = "nonexistent_field";
353 exprtoken *token = safe_extract_field(safe_page + offset, truncated_len,
354 nonexistent_field, strlen(nonexistent_field));
355
356 /* We're just testing that it doesn't access memory beyond the boundary */
357 if (boundary_violation) {
358 printf("Boundary violation at edge of memory page!\n");
359 tests_failed++;
360 } else {
361 if (token != NULL) {
362 exprTokenRelease(token);
363 }
364 boundary_tests_passed++;
365 }
366
367 free(temp_json);
368 }
369}
370
371/* Print summary of test results */
372void print_test_summary(void) {
373 printf("\n===== FASTJSON PARSER TEST SUMMARY =====\n");
374 printf("Normal tests passed: %d/%d\n", tests_passed, NUM_TEST_ITERATIONS * 2);
375 printf("Corruption tests passed: %d/%d\n", corruptions_passed, NUM_CORRUPTION_TESTS);
376 printf("Boundary tests passed: %d/%d\n", boundary_tests_passed, NUM_BOUNDARY_TESTS);
377 printf("Failed tests: %d\n", tests_failed);
378
379 if (tests_failed == 0) {
380 printf("\nALL TESTS PASSED! The JSON parser appears to be robust.\n");
381 } else {
382 printf("\nSome tests FAILED. The JSON parser may be vulnerable.\n");
383 }
384}
385
386/* Entry point for fastjson parser test */
387void run_fastjson_test(void) {
388 printf("Starting fastjson parser stress test...\n");
389
390 /* Seed the random number generator */
391 srand(time(NULL));
392
393 /* Setup test memory environment */
394 setup_test_memory();
395
396 /* Run the various test phases */
397 run_normal_tests();
398 run_corruption_tests();
399 run_boundary_tests();
400
401 /* Print summary */
402 print_test_summary();
403
404 /* Cleanup */
405 cleanup_test_memory();
406}