/src/r-oss-fuzz/harnesses/character.c
Line | Count | Source |
1 | | /* |
2 | | * Exercise byte-, character-, width-, substring-, case-, translation- and |
3 | | * quoting operations on strings (src/main/character.c, raw.c, and the |
4 | | * strsplit entry of grep.c). |
5 | | * |
6 | | * Input layout: the first byte selects a call slot, the rest is the |
7 | | * payload. Slots 0-8 take the whole payload as the string (the original |
8 | | * shape of this target, so the stored corpus keeps its meaning). Slots 9 |
9 | | * and up split the payload at its first newline into an auxiliary string |
10 | | * (regex or fixed split pattern, chartr translation spec, replacement |
11 | | * text) before the newline and the subject string after it; without a |
12 | | * newline a default auxiliary string is used. |
13 | | * |
14 | | * Several slots hand R the same bytes marked as UTF-8 or latin1 instead |
15 | | * of native. R does not validate the mark (users can set it with |
16 | | * Encoding<-), so this reaches the multibyte branches and their handling |
17 | | * of invalid sequences without changing the process locale. |
18 | | */ |
19 | | |
20 | | #include <stdint.h> |
21 | | #include <string.h> |
22 | | |
23 | | #include "common.h" |
24 | | |
25 | 5.73k | #define FUZZ_MAX_INPUT (1024 * 64) |
26 | 2.88k | #define N_FIXED_CALLS 9 |
27 | 2.86k | #define N_CALLS 34 |
28 | | |
29 | 2.86k | #define DEFAULT_AUX "[ ,;]+" |
30 | | |
31 | | /* Which input vectors a slot reads; only those are refilled. */ |
32 | 2.89k | #define NEED_X 0x01 |
33 | 2.87k | #define NEED_XU 0x02 |
34 | 2.85k | #define NEED_XL 0x04 |
35 | 2.86k | #define NEED_AUX 0x08 |
36 | 2.86k | #define NEED_AUXU 0x10 |
37 | 2.86k | #define NEED_REGEX 0x20 /* aux is a regex: apply the repeat guard */ |
38 | | |
39 | | static SEXP input_string; /* native */ |
40 | | static SEXP input_utf8; /* same bytes, marked UTF-8 */ |
41 | | static SEXP input_latin1; /* same bytes, marked latin1 */ |
42 | | static SEXP aux_string; |
43 | | static SEXP aux_utf8; |
44 | | |
45 | | static SEXP calls[N_CALLS]; |
46 | | static unsigned needs[N_CALLS]; |
47 | | |
48 | | static SEXP tagged(SEXP call, int pos, const char *tag) |
49 | 36 | { |
50 | 36 | SEXP cell = call; |
51 | 140 | for (int i = 0; i < pos; i++) |
52 | 104 | cell = CDR(cell); |
53 | 36 | SET_TAG(cell, Rf_install(tag)); |
54 | 36 | return call; |
55 | 36 | } |
56 | | |
57 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
58 | 2 | { |
59 | 2 | (void)argc; |
60 | 2 | (void)argv; |
61 | 2 | fuzz_init_r(); |
62 | | |
63 | 2 | SEXP bytes, chars, width, true_value, one, end, empty, quote, none; |
64 | 2 | Rf_protect(input_string = Rf_allocVector(STRSXP, 1)); |
65 | 2 | Rf_protect(input_utf8 = Rf_allocVector(STRSXP, 1)); |
66 | 2 | Rf_protect(input_latin1 = Rf_allocVector(STRSXP, 1)); |
67 | 2 | Rf_protect(aux_string = Rf_allocVector(STRSXP, 1)); |
68 | 2 | Rf_protect(aux_utf8 = Rf_allocVector(STRSXP, 1)); |
69 | 2 | Rf_protect(bytes = Rf_mkString("bytes")); |
70 | 2 | Rf_protect(chars = Rf_mkString("chars")); |
71 | 2 | Rf_protect(width = Rf_mkString("width")); |
72 | 2 | Rf_protect(true_value = Rf_ScalarLogical(TRUE)); |
73 | 2 | Rf_protect(one = Rf_ScalarInteger(1)); |
74 | 2 | Rf_protect(end = Rf_ScalarInteger(32)); |
75 | 2 | Rf_protect(empty = Rf_mkString("")); |
76 | 2 | Rf_protect(quote = Rf_mkString("\"")); |
77 | 2 | Rf_protect(none = Rf_mkString("none")); |
78 | | |
79 | 2 | SEXP nchar = Rf_install("nchar"); |
80 | 2 | Rf_protect(calls[0] = Rf_lang3(nchar, input_string, bytes)); |
81 | 2 | SET_TAG(CDDR(calls[0]), Rf_install("type")); |
82 | 2 | Rf_protect(calls[1] = Rf_lang3(nchar, input_string, chars)); |
83 | 2 | SET_TAG(CDDR(calls[1]), Rf_install("type")); |
84 | 2 | Rf_protect(calls[2] = Rf_lang3(nchar, input_string, width)); |
85 | 2 | SET_TAG(CDDR(calls[2]), Rf_install("type")); |
86 | 2 | Rf_protect(calls[3] = Rf_lang4(Rf_install("substr"), input_string, one, end)); |
87 | 2 | Rf_protect(calls[4] = Rf_lang4(Rf_install("substring"), input_string, one, end)); |
88 | 2 | Rf_protect(calls[5] = Rf_lang4(Rf_install("strsplit"), input_string, empty, |
89 | 2 | true_value)); |
90 | 2 | SET_TAG(CDDDR(calls[5]), Rf_install("fixed")); |
91 | 2 | Rf_protect(calls[6] = Rf_lang5(Rf_install("encodeString"), input_string, |
92 | 2 | quote, none, true_value)); |
93 | 2 | SET_TAG(CDDR(calls[6]), Rf_install("quote")); |
94 | 2 | SET_TAG(CDDDR(calls[6]), Rf_install("justify")); |
95 | 2 | SET_TAG(CDR(CDDDR(calls[6])), Rf_install("na.encode")); |
96 | 2 | Rf_protect(calls[7] = Rf_lang3(Rf_install("make.names"), input_string, |
97 | 2 | true_value)); |
98 | 2 | SET_TAG(CDDR(calls[7]), Rf_install("unique")); |
99 | | |
100 | 2 | SEXP to_raw; |
101 | 2 | Rf_protect(to_raw = Rf_lang2(Rf_install("charToRaw"), input_string)); |
102 | 2 | Rf_protect(calls[8] = Rf_lang3(Rf_install("rawToChar"), to_raw, true_value)); |
103 | 2 | SET_TAG(CDDR(calls[8]), Rf_install("multiple")); |
104 | 20 | for (int i = 0; i < N_FIXED_CALLS; i++) |
105 | 18 | needs[i] = NEED_X; |
106 | | |
107 | 2 | SEXP strsplit = Rf_install("strsplit"); |
108 | 2 | SEXP strtoi = Rf_install("strtoi"); |
109 | 2 | SEXP toupper = Rf_install("toupper"); |
110 | 2 | SEXP chartr = Rf_install("chartr"); |
111 | 2 | SEXP utf8ToInt = Rf_install("utf8ToInt"); |
112 | 2 | SEXP intToUtf8 = Rf_install("intToUtf8"); |
113 | 2 | SEXP two, three, four, ten, sixteen, thirtysix, scale; |
114 | 2 | Rf_protect(two = Rf_ScalarInteger(2)); |
115 | 2 | Rf_protect(three = Rf_ScalarInteger(3)); |
116 | 2 | Rf_protect(four = Rf_ScalarInteger(4)); |
117 | 2 | Rf_protect(ten = Rf_ScalarInteger(10)); |
118 | 2 | Rf_protect(sixteen = Rf_ScalarInteger(16)); |
119 | 2 | Rf_protect(thirtysix = Rf_ScalarInteger(36)); |
120 | 2 | Rf_protect(scale = Rf_ScalarInteger(4099)); |
121 | | |
122 | | /* Inner calls are built first and protected, since the outer |
123 | | * Rf_lang* allocates while holding them. */ |
124 | 2 | SEXP upper_aux, upper_aux_utf8, codepoints, scaled_bytes; |
125 | 2 | Rf_protect(upper_aux = Rf_lang2(toupper, aux_string)); |
126 | 2 | Rf_protect(upper_aux_utf8 = Rf_lang2(toupper, aux_utf8)); |
127 | 2 | Rf_protect(codepoints = Rf_lang2(utf8ToInt, input_string)); |
128 | 2 | Rf_protect(scaled_bytes = Rf_lang2(Rf_install("as.integer"), to_raw)); |
129 | 2 | Rf_protect(scaled_bytes = Rf_lang3(Rf_install("*"), scaled_bytes, scale)); |
130 | | |
131 | 2 | int k = N_FIXED_CALLS; |
132 | | |
133 | | /* strsplit: PCRE, fixed and byte-wise matching, on native and |
134 | | * UTF-8-marked input. Only the empty fixed split ran before. The |
135 | | * default engine, TRE, is left alone on purpose (see README.md), so |
136 | | * every regex slot passes perl = TRUE. */ |
137 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_string, |
138 | 2 | aux_string, true_value, |
139 | 2 | true_value), |
140 | 2 | 3, "perl"), 4, "useBytes")); |
141 | 2 | needs[k++] = NEED_X | NEED_AUX | NEED_REGEX; |
142 | 2 | Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_string, aux_string, |
143 | 2 | true_value), 3, "perl")); |
144 | 2 | needs[k++] = NEED_X | NEED_AUX | NEED_REGEX; |
145 | 2 | Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_string, aux_string, |
146 | 2 | true_value), 3, "fixed")); |
147 | 2 | needs[k++] = NEED_X | NEED_AUX; |
148 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_string, |
149 | 2 | aux_string, true_value, |
150 | 2 | true_value), |
151 | 2 | 3, "fixed"), 4, "useBytes")); |
152 | 2 | needs[k++] = NEED_X | NEED_AUX; |
153 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_utf8, |
154 | 2 | aux_utf8, true_value, |
155 | 2 | true_value), |
156 | 2 | 3, "perl"), 4, "useBytes")); |
157 | 2 | needs[k++] = NEED_XU | NEED_AUXU | NEED_REGEX; |
158 | 2 | Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_utf8, aux_utf8, |
159 | 2 | true_value), 3, "perl")); |
160 | 2 | needs[k++] = NEED_XU | NEED_AUXU | NEED_REGEX; |
161 | 2 | Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_utf8, aux_utf8, |
162 | 2 | true_value), 3, "fixed")); |
163 | 2 | needs[k++] = NEED_XU | NEED_AUXU; |
164 | | |
165 | | /* nchar on marked strings: the UTF-8 and latin1 branches of R_nchar, |
166 | | * including allowNA for invalid sequences. */ |
167 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_utf8, chars, |
168 | 2 | true_value), 2, "type"), |
169 | 2 | 3, "allowNA")); |
170 | 2 | needs[k++] = NEED_XU; |
171 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_utf8, width, |
172 | 2 | true_value), 2, "type"), |
173 | 2 | 3, "allowNA")); |
174 | 2 | needs[k++] = NEED_XU; |
175 | 2 | Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_latin1, chars, |
176 | 2 | true_value), 2, "type"), |
177 | 2 | 3, "allowNA")); |
178 | 2 | needs[k++] = NEED_XL; |
179 | | |
180 | | /* chartr(old, new, x): old and new must have the same number of |
181 | | * characters, which toupper preserves, so the spec parser (ranges, |
182 | | * escapes) runs on fuzzed text while the call stays valid. */ |
183 | 2 | Rf_protect(calls[k] = Rf_lang4(chartr, aux_string, upper_aux, input_string)); |
184 | 2 | needs[k++] = NEED_X | NEED_AUX; |
185 | 2 | Rf_protect(calls[k] = Rf_lang4(chartr, aux_utf8, upper_aux_utf8, input_utf8)); |
186 | 2 | needs[k++] = NEED_XU | NEED_AUXU; |
187 | | |
188 | | /* strtoi in the bases with distinct digit handling */ |
189 | 2 | Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, ten)); |
190 | 2 | needs[k++] = NEED_X; |
191 | 2 | Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, sixteen)); |
192 | 2 | needs[k++] = NEED_X; |
193 | 2 | Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, thirtysix)); |
194 | 2 | needs[k++] = NEED_X; |
195 | | |
196 | 2 | Rf_protect(calls[k] = Rf_lang3(Rf_install("strtrim"), input_utf8, ten)); |
197 | 2 | needs[k++] = NEED_XU; |
198 | 2 | Rf_protect(calls[k] = Rf_lang2(toupper, input_utf8)); |
199 | 2 | needs[k++] = NEED_XU; |
200 | 2 | Rf_protect(calls[k] = Rf_lang2(Rf_install("tolower"), input_latin1)); |
201 | 2 | needs[k++] = NEED_XL; |
202 | 2 | Rf_protect(calls[k] = tagged(Rf_lang3(Rf_install("abbreviate"), input_string, |
203 | 2 | four), 2, "minlength")); |
204 | 2 | needs[k++] = NEED_X; |
205 | | |
206 | | /* substr(x, 2, 10) <- aux */ |
207 | 2 | Rf_protect(calls[k] = Rf_lang5(Rf_install("substr<-"), input_utf8, two, ten, |
208 | 2 | aux_utf8)); |
209 | 2 | needs[k++] = NEED_XU | NEED_AUXU; |
210 | | |
211 | 2 | Rf_protect(calls[k] = Rf_lang3(Rf_install("strrep"), input_string, three)); |
212 | 2 | needs[k++] = NEED_X; |
213 | | |
214 | | /* UTF-8 <-> code point conversion in raw.c, and intToUtf8 fed code |
215 | | * points scaled out of the bytes to reach surrogates and 4-byte |
216 | | * forms. */ |
217 | 2 | calls[k] = codepoints; |
218 | 2 | needs[k++] = NEED_X; |
219 | 2 | Rf_protect(calls[k] = tagged(Rf_lang3(intToUtf8, codepoints, true_value), |
220 | 2 | 2, "multiple")); |
221 | 2 | needs[k++] = NEED_X; |
222 | 2 | Rf_protect(calls[k] = Rf_lang2(intToUtf8, scaled_bytes)); |
223 | 2 | needs[k++] = NEED_X; |
224 | | |
225 | 2 | Rf_protect(calls[k] = Rf_lang4(Rf_install("substr"), input_utf8, two, end)); |
226 | 2 | needs[k++] = NEED_XU; |
227 | | |
228 | 2 | if (k != N_CALLS) |
229 | 0 | abort(); |
230 | | |
231 | 2 | SET_STRING_ELT(input_string, 0, Rf_mkChar("fuzz")); |
232 | 2 | SET_STRING_ELT(input_utf8, 0, Rf_mkCharCE("fuzz", CE_UTF8)); |
233 | 2 | SET_STRING_ELT(input_latin1, 0, Rf_mkCharCE("fuzz", CE_LATIN1)); |
234 | 2 | SET_STRING_ELT(aux_string, 0, Rf_mkChar(DEFAULT_AUX)); |
235 | 2 | SET_STRING_ELT(aux_utf8, 0, Rf_mkCharCE(DEFAULT_AUX, CE_UTF8)); |
236 | 2 | return 0; |
237 | 2 | } |
238 | | |
239 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
240 | 2.87k | { |
241 | 2.87k | if (size < 2 || size > FUZZ_MAX_INPUT || memchr(data + 1, '\0', size - 1)) |
242 | 11 | return 0; |
243 | | |
244 | 2.85k | int selected = data[0] % N_CALLS; |
245 | 2.85k | unsigned need = needs[selected]; |
246 | | |
247 | 2.85k | char buffer[FUZZ_MAX_INPUT]; |
248 | 2.85k | memcpy(buffer, data + 1, size - 1); |
249 | 2.85k | buffer[size - 1] = '\0'; |
250 | | |
251 | 2.85k | const char *aux = DEFAULT_AUX; |
252 | 2.85k | const char *subject = buffer; |
253 | 2.85k | if (selected >= N_FIXED_CALLS) { |
254 | 2.32k | char *nl = memchr(buffer, '\n', size - 1); |
255 | 2.32k | if (nl != NULL) { |
256 | 709 | *nl = '\0'; |
257 | 709 | aux = buffer; |
258 | 709 | subject = nl + 1; |
259 | 709 | } |
260 | 2.32k | } |
261 | | |
262 | | /* TRE duplicates the AST for counted repeats; see common.h. */ |
263 | 2.85k | if ((need & NEED_REGEX) && fuzz_repeat_product_excessive(aux)) |
264 | 9 | return 0; |
265 | | |
266 | 2.85k | if ((need & NEED_X) && !fuzz_set_string(input_string, subject)) |
267 | 0 | return 0; |
268 | 2.85k | if ((need & NEED_XU) && !fuzz_set_string_ce(input_utf8, subject, CE_UTF8)) |
269 | 0 | return 0; |
270 | 2.85k | if ((need & NEED_XL) && !fuzz_set_string_ce(input_latin1, subject, CE_LATIN1)) |
271 | 0 | return 0; |
272 | 2.85k | if ((need & NEED_AUX) && !fuzz_set_string(aux_string, aux)) |
273 | 0 | return 0; |
274 | 2.85k | if ((need & NEED_AUXU) && !fuzz_set_string_ce(aux_utf8, aux, CE_UTF8)) |
275 | 0 | return 0; |
276 | | |
277 | 2.85k | fuzz_eval_silent(calls[selected], R_GlobalEnv); |
278 | 2.85k | return 0; |
279 | 2.85k | } |