/src/r-oss-fuzz/harnesses/io.c
Line | Count | Source |
1 | | /* |
2 | | * Exercise readBin() / writeBin() / readChar() / writeChar() over raw |
3 | | * vectors and in-memory raw connections. |
4 | | * |
5 | | * The first input byte selects a slot. The first N_BIN slots are the |
6 | | * original typed readBin round trips (read, write back, read again, |
7 | | * and abort if the two reads differ). The rest wrap the input in R |
8 | | * closures that reach the string readers (readChar, readBin of |
9 | | * "character"), the rawConnection layer (seek, readLines, the |
10 | | * connection-backed readBin), and the writeChar encoder -- none of |
11 | | * which the round trips touched. |
12 | | */ |
13 | | |
14 | | #include <stdint.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include "common.h" |
18 | | |
19 | 1.46k | #define FUZZ_MAX_INPUT (1024 * 64) |
20 | 4.12k | #define N_BIN 8 |
21 | 1.52k | #define N_WRAP 10 |
22 | 1.45k | #define N_CALLS (N_BIN + N_WRAP) |
23 | | |
24 | | static SEXP read_calls[N_BIN]; |
25 | | static SEXP write_calls[N_BIN]; |
26 | | static SEXP wrap_calls[N_WRAP]; |
27 | | |
28 | | /* Connections are closed on exit from each closure: a read error must |
29 | | * not leak one of R's 128 connection slots across iterations. */ |
30 | | static const char *const wrap_sources[N_WRAP] = { |
31 | | "function(x) readChar(x, c(1L, 5L, 16L, 64L), useBytes = TRUE)", |
32 | | "function(x) readChar(x, c(1L, 5L, 16L, 64L))", |
33 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); " |
34 | | "readChar(con, c(1L, 5L, 16L, 64L)) }", |
35 | | "function(x) { s <- readChar(x, c(4L, 8L, 16L), useBytes = TRUE); " |
36 | | "r <- writeChar(s, raw(), nchars = c(4L, 8L, 16L), eos = NULL); " |
37 | | "readChar(r, c(4L, 8L, 16L), useBytes = TRUE) }", |
38 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); " |
39 | | "readBin(con, \"character\", n = 64L) }", |
40 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); " |
41 | | "readBin(con, \"integer\", n = 16384L, size = 2L, signed = FALSE) }", |
42 | | "function(x) readBin(x, \"integer\", n = 16384L, size = 1L, signed = TRUE)", |
43 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); seek(con, 3L); " |
44 | | "readBin(con, \"double\", n = 16384L, size = 4L, endian = \"big\") }", |
45 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); readLines(con) }", |
46 | | "function(x) { con <- rawConnection(x); on.exit(close(con)); " |
47 | | "readBin(con, \"numeric\", n = 16384L, endian = \"swap\") }", |
48 | | }; |
49 | | |
50 | | typedef struct { |
51 | | int selected; |
52 | | const uint8_t *data; |
53 | | size_t size; |
54 | | } io_input_t; |
55 | | |
56 | | static void run_io(void *data) |
57 | 299 | { |
58 | 299 | io_input_t *input = data; |
59 | 299 | int error = 0; |
60 | 299 | SEXP raw, value, encoded, roundtrip; |
61 | 299 | Rf_protect(raw = Rf_allocVector(RAWSXP, (R_xlen_t)input->size)); |
62 | 299 | memcpy(RAW(raw), input->data, input->size); |
63 | 299 | SETCADR(read_calls[input->selected], raw); |
64 | | |
65 | 299 | value = R_tryEvalSilent(read_calls[input->selected], R_GlobalEnv, &error); |
66 | 299 | if (!error) { |
67 | 299 | SETCADR(write_calls[input->selected], value); |
68 | 299 | encoded = R_tryEvalSilent(write_calls[input->selected], R_GlobalEnv, |
69 | 299 | &error); |
70 | 299 | } |
71 | 299 | if (!error) { |
72 | 299 | SETCADR(read_calls[input->selected], encoded); |
73 | 299 | roundtrip = R_tryEvalSilent(read_calls[input->selected], R_GlobalEnv, |
74 | 299 | &error); |
75 | 299 | } |
76 | 299 | if (!error) { |
77 | 299 | Rf_protect(roundtrip); |
78 | 299 | if (!R_compute_identical(value, roundtrip, IDENT_USE_CLOENV)) |
79 | 0 | abort(); |
80 | 299 | Rf_unprotect(1); |
81 | 299 | } |
82 | 299 | Rf_unprotect(1); |
83 | 299 | } |
84 | | |
85 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
86 | 6 | { |
87 | 6 | (void)argc; |
88 | 6 | (void)argv; |
89 | 6 | fuzz_init_r(); |
90 | | |
91 | 6 | SEXP what[N_BIN], sizes[N_BIN], endian[N_BIN]; |
92 | 6 | Rcomplex zero; |
93 | 6 | zero.r = 0.0; |
94 | 6 | zero.i = 0.0; |
95 | 6 | Rf_protect(what[0] = Rf_allocVector(RAWSXP, 0)); |
96 | 6 | Rf_protect(what[1] = Rf_ScalarInteger(0)); |
97 | 6 | Rf_protect(what[2] = Rf_ScalarInteger(0)); |
98 | 6 | Rf_protect(what[3] = Rf_ScalarReal(0)); |
99 | 6 | Rf_protect(what[4] = Rf_ScalarReal(0)); |
100 | 6 | Rf_protect(what[5] = Rf_ScalarComplex(zero)); |
101 | 6 | Rf_protect(what[6] = Rf_mkString("")); |
102 | 6 | Rf_protect(what[7] = Rf_ScalarLogical(FALSE)); |
103 | | |
104 | 6 | int size_values[N_BIN] = { 1, 1, 4, 4, 8, 16, 1, 4 }; |
105 | 6 | const char *endian_values[N_BIN] = { |
106 | 6 | "little", "big", "little", "big", "little", "big", "little", |
107 | 6 | "little" |
108 | 6 | }; |
109 | 6 | SEXP placeholder, count, output; |
110 | 6 | Rf_protect(placeholder = Rf_allocVector(RAWSXP, 0)); |
111 | 6 | Rf_protect(count = Rf_ScalarInteger(16384)); |
112 | 6 | Rf_protect(output = Rf_allocVector(RAWSXP, 0)); |
113 | 6 | SEXP read_bin = Rf_install("readBin"); |
114 | 6 | SEXP write_bin = Rf_install("writeBin"); |
115 | | |
116 | 66 | for (int i = 0; i < N_BIN; i++) { |
117 | 60 | Rf_protect(sizes[i] = Rf_ScalarInteger(size_values[i])); |
118 | 60 | Rf_protect(endian[i] = Rf_mkString(endian_values[i])); |
119 | 60 | Rf_protect(read_calls[i] = Rf_lang6(read_bin, placeholder, what[i], count, |
120 | 60 | sizes[i], endian[i])); |
121 | 60 | SET_TAG(CDR(CDR(CDDR(read_calls[i]))), Rf_install("size")); |
122 | 60 | SET_TAG(CDR(CDR(CDR(CDDR(read_calls[i])))), Rf_install("endian")); |
123 | | |
124 | 60 | Rf_protect(write_calls[i] = Rf_lang5(write_bin, R_NilValue, output, |
125 | 60 | sizes[i], endian[i])); |
126 | 60 | SET_TAG(CDDDR(write_calls[i]), Rf_install("size")); |
127 | 60 | SET_TAG(CDR(CDDDR(write_calls[i])), Rf_install("endian")); |
128 | 60 | } |
129 | | |
130 | 70 | for (int i = 0; i < N_WRAP; i++) { |
131 | 64 | SEXP wrapper; |
132 | 64 | Rf_protect(wrapper = fuzz_make_wrapper(wrap_sources[i])); |
133 | 64 | Rf_protect(wrap_calls[i] = Rf_lang2(wrapper, placeholder)); |
134 | 64 | } |
135 | 6 | return 0; |
136 | 6 | } |
137 | | |
138 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
139 | 1.46k | { |
140 | 1.46k | if (size < 2 || size > FUZZ_MAX_INPUT) |
141 | 11 | return 0; |
142 | | |
143 | 1.45k | int selected = data[0] % N_CALLS; |
144 | 1.45k | if (selected < N_BIN) { |
145 | 299 | io_input_t input = { selected, data + 1, size - 1 }; |
146 | 299 | R_ToplevelExec(run_io, &input); |
147 | 299 | return 0; |
148 | 299 | } |
149 | | |
150 | 1.15k | SEXP call = wrap_calls[selected - N_BIN]; |
151 | 1.15k | if (!fuzz_set_raw_arg(call, data + 1, size - 1)) |
152 | 0 | return 0; |
153 | | |
154 | 1.15k | fuzz_eval_silent(call, R_GlobalEnv); |
155 | 1.15k | return 0; |
156 | 1.15k | } |