/src/r-oss-fuzz/harnesses/coerce.c
Line | Count | Source |
1 | | /* |
2 | | * libFuzzer harness for R's type coercion / string-to-X parsing. |
3 | | * |
4 | | * Passes fuzzed input as a character string through R's type conversion |
5 | | * functions: as.numeric (R_strtod4), as.complex (complex number parsing), |
6 | | * as.logical, and type.convert (auto-detection). |
7 | | * |
8 | | * Adapted from r-afl's coerce harness. |
9 | | */ |
10 | | |
11 | | #include <stdint.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "common.h" |
15 | | |
16 | 5.96k | #define FUZZ_MAX_INPUT (1024 * 64) |
17 | 38.9k | #define N_CALLS 4 |
18 | | |
19 | | static SEXP x_str; |
20 | | static SEXP calls[N_CALLS]; |
21 | | |
22 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
23 | 4 | { |
24 | 4 | fuzz_init_r(); |
25 | | |
26 | 4 | SEXP sym_as_numeric = Rf_install("as.numeric"); |
27 | 4 | SEXP sym_as_complex = Rf_install("as.complex"); |
28 | 4 | SEXP sym_as_logical = Rf_install("as.logical"); |
29 | 4 | SEXP sym_type_convert = Rf_install("type.convert"); |
30 | | |
31 | 4 | SEXP true_val; |
32 | 4 | Rf_protect(true_val = Rf_ScalarLogical(TRUE)); |
33 | | |
34 | | /* Pre-build reusable string container and all call objects. */ |
35 | 4 | Rf_protect(x_str = Rf_allocVector(STRSXP, 1)); |
36 | | |
37 | | /* as.numeric(x) -- exercises R_strtod4 */ |
38 | 4 | Rf_protect(calls[0] = Rf_lang2(sym_as_numeric, x_str)); |
39 | | |
40 | | /* as.complex(x) -- exercises complex number parsing (a+bi) */ |
41 | 4 | Rf_protect(calls[1] = Rf_lang2(sym_as_complex, x_str)); |
42 | | |
43 | | /* as.logical(x) -- exercises logical string matching */ |
44 | 4 | Rf_protect(calls[2] = Rf_lang2(sym_as_logical, x_str)); |
45 | | |
46 | | /* type.convert(x, as.is=TRUE) -- auto-detection heuristic */ |
47 | 4 | Rf_protect(calls[3] = Rf_lang3(sym_type_convert, x_str, true_val)); |
48 | 4 | SET_TAG(CDDR(calls[3]), Rf_install("as.is")); |
49 | | |
50 | | /* Warmup: prime coercion code paths. */ |
51 | 4 | { |
52 | 4 | int error = 0; |
53 | 4 | SET_STRING_ELT(x_str, 0, Rf_mkChar("1")); |
54 | 20 | for (int i = 0; i < N_CALLS; i++) { |
55 | 16 | R_tryEval(calls[i], R_GlobalEnv, &error); |
56 | 16 | error = 0; |
57 | 16 | } |
58 | 4 | } |
59 | | |
60 | 4 | return 0; |
61 | 4 | } |
62 | | |
63 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
64 | 5.96k | { |
65 | 5.96k | if (size == 0 || size > FUZZ_MAX_INPUT) |
66 | 17 | return 0; |
67 | | |
68 | 5.94k | char buf[FUZZ_MAX_INPUT + 1]; |
69 | 5.94k | memcpy(buf, data, size); |
70 | 5.94k | buf[size] = '\0'; |
71 | | |
72 | 5.94k | if (!fuzz_set_string(x_str, buf)) |
73 | 0 | return 0; |
74 | | |
75 | 5.94k | fuzz_eval_data_t ed = { .env = R_GlobalEnv }; |
76 | 38.9k | for (int i = 0; i < N_CALLS; i++) { |
77 | 32.9k | ed.call = calls[i]; |
78 | 32.9k | R_ToplevelExec(fuzz_do_eval, &ed); |
79 | 32.9k | } |
80 | | |
81 | 5.94k | return 0; |
82 | 5.94k | } |