quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

fuzz_regexp_compile.c (5700B)


      1 // Copyright 2025 Google LLC
      2 // Fuzz target for QuickJS RegExp compilation
      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 < 2) 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     size_t pattern_len = size / 2;
     24     size_t flags_len = size - pattern_len;
     25     
     26     if (pattern_len == 0 || flags_len == 0) {
     27         JS_FreeContext(ctx);
     28         JS_FreeRuntime(rt);
     29         return 0;
     30     }
     31     
     32     char* pattern = malloc(pattern_len + 1);
     33     char* flags = malloc(flags_len + 1);
     34     
     35     if (!pattern || !flags) {
     36         free(pattern);
     37         free(flags);
     38         JS_FreeContext(ctx);
     39         JS_FreeRuntime(rt);
     40         return 0;
     41     }
     42     
     43     memcpy(pattern, data, pattern_len);
     44     pattern[pattern_len] = '\0';
     45     
     46     memcpy(flags, data + pattern_len, flags_len);
     47     flags[flags_len] = '\0';
     48     
     49     char valid_flags[16];
     50     size_t valid_idx = 0;
     51     const char* valid = "gimsuy";
     52     for (size_t i = 0; i < flags_len && valid_idx < sizeof(valid_flags) - 1; i++) {
     53         if (strchr(valid, flags[i]) && !strchr(valid_flags, flags[i])) {
     54             valid_flags[valid_idx++] = flags[i];
     55         }
     56     }
     57     valid_flags[valid_idx] = '\0';
     58     
     59     char script[8192];
     60     char escaped_pattern[4096];
     61     size_t esc_idx = 0;
     62     for (size_t i = 0; i < pattern_len && esc_idx < sizeof(escaped_pattern) - 2; i++) {
     63         if (pattern[i] == '\\' || pattern[i] == '"' || pattern[i] == '\n' || 
     64             pattern[i] == '\r' || pattern[i] == '\t') {
     65             escaped_pattern[esc_idx++] = '\\';
     66         }
     67         escaped_pattern[esc_idx++] = pattern[i];
     68     }
     69     escaped_pattern[esc_idx] = '\0';
     70     
     71     snprintf(script, sizeof(script), "new RegExp(\"%s\", \"%s\")", 
     72              escaped_pattern, valid_flags);
     73     
     74     JSValue regexp_result = JS_Eval(ctx, script, strlen(script), "<regexp>", 0);
     75     
     76     if (!JS_IsException(regexp_result)) {
     77         const char* test_strings[] = {
     78             "'test string'",
     79             "''",
     80             "'aaaaaaaaaa'",
     81             "'1234567890'",
     82             "'!@#$%^&*()'",
     83         };
     84         
     85         for (size_t i = 0; i < sizeof(test_strings) / sizeof(test_strings[0]); i++) {
     86             char match_script[4096];
     87             snprintf(match_script, sizeof(match_script),
     88                      "var re = %s; re.test(%s); re.exec(%s); %s.match(re);",
     89                      script, test_strings[i], test_strings[i], test_strings[i]);
     90             
     91             JSValue match_result = JS_Eval(ctx, match_script, strlen(match_script), 
     92                                            "<regexp-match>", 0);
     93             if (!JS_IsException(match_result)) {
     94                 JS_FreeValue(ctx, match_result);
     95             } else {
     96                 JS_GetException(ctx);
     97             }
     98         }
     99         
    100         const char* split_test = "'a,b,c,d'.split(/,/)";
    101         JSValue split_result = JS_Eval(ctx, split_test, strlen(split_test), 
    102                                        "<regexp-split>", 0);
    103         if (!JS_IsException(split_result)) {
    104             JS_FreeValue(ctx, split_result);
    105         } else {
    106             JS_GetException(ctx);
    107         }
    108         
    109         const char* replace_test = "'hello world'.replace(/world/, 'universe')";
    110         JSValue replace_result = JS_Eval(ctx, replace_test, strlen(replace_test),
    111                                           "<regexp-replace>", 0);
    112         if (!JS_IsException(replace_result)) {
    113             JS_FreeValue(ctx, replace_result);
    114         } else {
    115             JS_GetException(ctx);
    116         }
    117         
    118         const char* search_test = "'abc123def'.search(/[0-9]+/)";
    119         JSValue search_result = JS_Eval(ctx, search_test, strlen(search_test),
    120                                          "<regexp-search>", 0);
    121         if (!JS_IsException(search_result)) {
    122             JS_FreeValue(ctx, search_result);
    123         } else {
    124             JS_GetException(ctx);
    125         }
    126         
    127         JS_FreeValue(ctx, regexp_result);
    128     } else {
    129         JS_GetException(ctx);
    130     }
    131     
    132     char literal_script[4096];
    133     char slash_escaped[2048];
    134     size_t slash_idx = 0;
    135     for (size_t i = 0; i < pattern_len && slash_idx < sizeof(slash_escaped) - 2; i++) {
    136         if (pattern[i] == '/') {
    137             slash_escaped[slash_idx++] = '\\';
    138         }
    139         slash_escaped[slash_idx++] = pattern[i];
    140     }
    141     slash_escaped[slash_idx] = '\0';
    142     
    143     snprintf(literal_script, sizeof(literal_script), "/%s/%s.test('test')", slash_escaped, valid_flags);
    144     
    145     JSValue literal_result = JS_Eval(ctx, literal_script, strlen(literal_script),
    146                                       "<regexp-literal>", 0);
    147     if (!JS_IsException(literal_result)) {
    148         JS_FreeValue(ctx, literal_result);
    149     } else {
    150         JS_GetException(ctx);
    151     }
    152     
    153     const char* builtin_tests[] = {
    154         "RegExp.prototype.compile",
    155         "/a/g[Symbol.match]('a')",
    156         "/a/g[Symbol.replace]('a', 'b')",
    157         "/a/g[Symbol.search]('a')",
    158         "/a/g[Symbol.split]('a,b,a')",
    159     };
    160     
    161     for (size_t i = 0; i < sizeof(builtin_tests) / sizeof(builtin_tests[0]); i++) {
    162         JSValue r = JS_Eval(ctx, builtin_tests[i], strlen(builtin_tests[i]),
    163                             "<regexp-builtin>", 0);
    164         if (!JS_IsException(r)) {
    165             JS_FreeValue(ctx, r);
    166         } else {
    167             JS_GetException(ctx);
    168         }
    169     }
    170     
    171     free(pattern);
    172     free(flags);
    173     JS_FreeContext(ctx);
    174     JS_FreeRuntime(rt);
    175     
    176     return 0;
    177 }