fuzz_json.c (4117B)
1 // Copyright 2025 Google LLC 2 // Fuzz target for QuickJS JSON operations 3 4 #include "quickjs.h" 5 #include "quickjs-libc.h" 6 #include <stdint.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <stdio.h> 10 11 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 12 if (size < 1) return 0; 13 14 JSRuntime* rt = JS_NewRuntime(); 15 if (!rt) return 0; 16 17 JSContext* ctx = JS_NewContext(rt); 18 if (!ctx) { 19 JS_FreeRuntime(rt); 20 return 0; 21 } 22 23 char* input = malloc(size + 1); 24 if (!input) { 25 JS_FreeContext(ctx); 26 JS_FreeRuntime(rt); 27 return 0; 28 } 29 memcpy(input, data, size); 30 input[size] = '\0'; 31 32 char parse_script[8192]; 33 snprintf(parse_script, sizeof(parse_script), 34 "JSON.parse(JSON.stringify(%s))", input); 35 36 JSValue parse_result = JS_Eval(ctx, parse_script, strlen(parse_script), 37 "<json-fuzz>", 0); 38 39 if (JS_IsException(parse_result)) { 40 JS_GetException(ctx); 41 } else { 42 JS_FreeValue(ctx, parse_result); 43 } 44 45 char direct_parse[16384]; 46 snprintf(direct_parse, sizeof(direct_parse), "JSON.parse('"); 47 48 size_t script_len = strlen(direct_parse); 49 for (size_t i = 0; i < size && script_len < sizeof(direct_parse) - 20; i++) { 50 char c = input[i]; 51 if (c == '\\' || c == '\'') { 52 direct_parse[script_len++] = '\\'; 53 } 54 if (c >= 32 && c < 127) { 55 direct_parse[script_len++] = c; 56 } 57 } 58 strcat(direct_parse + script_len, "');"); 59 60 JSValue direct_result = JS_Eval(ctx, direct_parse, strlen(direct_parse), 61 "<json-direct>", 0); 62 if (JS_IsException(direct_result)) { 63 JS_GetException(ctx); 64 } else { 65 JS_FreeValue(ctx, direct_result); 66 } 67 68 char stringify_script[8192]; 69 snprintf(stringify_script, sizeof(stringify_script), 70 "var obj = { data: %s, num: 123, str: 'test', bool: true, nullv: null, " 71 "arr: [1,2,3], nested: { a: 1 } }; JSON.stringify(obj);", 72 input); 73 74 JSValue stringify_result = JS_Eval(ctx, stringify_script, 75 strlen(stringify_script), "<json-stringify>", 0); 76 if (JS_IsException(stringify_result)) { 77 JS_GetException(ctx); 78 } else { 79 const char* str = JS_ToCString(ctx, stringify_result); 80 if (str) { 81 JS_FreeCString(ctx, str); 82 } 83 JS_FreeValue(ctx, stringify_result); 84 } 85 86 const char* spacing_tests[] = { 87 "JSON.stringify({a:1,b:2})", 88 "JSON.stringify({a:1,b:2}, null, 2)", 89 "JSON.stringify({a:1,b:2}, null, ' ')", 90 "JSON.stringify([1,2,3])", 91 "JSON.stringify(null)", 92 "JSON.stringify(undefined)", 93 "JSON.stringify(123)", 94 "JSON.stringify('string')", 95 "JSON.stringify(true)", 96 }; 97 98 for (size_t i = 0; i < sizeof(spacing_tests) / sizeof(spacing_tests[0]); i++) { 99 JSValue r = JS_Eval(ctx, spacing_tests[i], strlen(spacing_tests[i]), 100 "<json-test>", 0); 101 if (!JS_IsException(r)) { 102 JS_FreeValue(ctx, r); 103 } else { 104 JS_GetException(ctx); 105 } 106 } 107 108 const char* reviver_test = "JSON.parse('{\"a\":1,\"b\":2}', function(k,v) { return v; })"; 109 JSValue reviver_result = JS_Eval(ctx, reviver_test, strlen(reviver_test), 110 "<json-reviver>", 0); 111 if (!JS_IsException(reviver_result)) { 112 JS_FreeValue(ctx, reviver_result); 113 } else { 114 JS_GetException(ctx); 115 } 116 117 const char* replacer_test = "JSON.stringify({a:1,b:2}, function(k,v) { return v; })"; 118 JSValue replacer_result = JS_Eval(ctx, replacer_test, strlen(replacer_test), 119 "<json-replacer>", 0); 120 if (!JS_IsException(replacer_result)) { 121 JS_FreeValue(ctx, replacer_result); 122 } else { 123 JS_GetException(ctx); 124 } 125 126 free(input); 127 JS_FreeContext(ctx); 128 JS_FreeRuntime(rt); 129 130 return 0; 131 }