/src/r-oss-fuzz/harnesses/encoding.c
Line | Count | Source |
1 | | /* |
2 | | * Exercise R's iconv wrapper (do_iconv in src/main/sysutils.c) and the |
3 | | * encoding-mark helpers around it with arbitrary byte strings. |
4 | | * |
5 | | * The first byte selects a call slot, the rest is the payload. Slots 0-5 |
6 | | * hand the payload to iconv() as a raw vector in a list (the original |
7 | | * shape of this target). Later slots pass it as a character vector -- |
8 | | * native, or the same bytes marked UTF-8 or latin1 without validation, |
9 | | * as Encoding<- lets users do -- which is what reaches the substitution |
10 | | * modes (sub=NA, "", "byte", "Unicode", "c99"), the mark= handling, the |
11 | | * from="" branches keyed on the CHARSXP mark, and enc2utf8/enc2native/ |
12 | | * validUTF8/validEnc. A few more raw-list slots use stateful or |
13 | | * BOM-carrying encodings so the error and restart handling in R's |
14 | | * conversion loop sees EINVAL as well as EILSEQ. |
15 | | */ |
16 | | |
17 | | #include <stdint.h> |
18 | | #include <string.h> |
19 | | |
20 | | #include "common.h" |
21 | | |
22 | 1.60k | #define FUZZ_MAX_INPUT (1024 * 64) |
23 | 34 | #define N_RAW_CALLS 6 |
24 | 1.60k | #define N_CALLS 28 |
25 | | |
26 | | /* Which input a slot reads */ |
27 | 1.65k | #define IN_RAW 0 |
28 | 36 | #define IN_STR 1 |
29 | 142 | #define IN_UTF8 2 |
30 | 52 | #define IN_LATIN1 3 |
31 | | |
32 | | static SEXP x_list; /* list(<raw>) */ |
33 | | static SEXP x_str; /* native character */ |
34 | | static SEXP x_utf8; /* same bytes marked UTF-8 */ |
35 | | static SEXP x_latin1; /* same bytes marked latin1 */ |
36 | | static SEXP calls[N_CALLS]; |
37 | | static int inputs[N_CALLS]; |
38 | | |
39 | | typedef struct { |
40 | | const uint8_t *data; |
41 | | size_t size; |
42 | | } encoding_input_t; |
43 | | |
44 | | static void set_encoding_input(void *data) |
45 | 693 | { |
46 | 693 | encoding_input_t *input = data; |
47 | 693 | SEXP raw = Rf_allocVector(RAWSXP, (R_xlen_t)input->size); |
48 | 693 | memcpy(RAW(raw), input->data, input->size); |
49 | 693 | SET_VECTOR_ELT(x_list, 0, raw); |
50 | 693 | } |
51 | | |
52 | | /* iconv(x, from, to, sub, <extra>) with every argument named. extra is |
53 | | * either mark= or toRaw=, which keeps each call within Rf_lang6. */ |
54 | | static SEXP iconv_call(SEXP x, const char *from, const char *to, SEXP sub, |
55 | | const char *extra_name, SEXP extra) |
56 | 46 | { |
57 | 46 | SEXP from_string, to_string, call; |
58 | 46 | Rf_protect(from_string = Rf_mkString(from)); |
59 | 46 | Rf_protect(to_string = Rf_mkString(to)); |
60 | 46 | if (extra == NULL) |
61 | 24 | call = Rf_lang5(Rf_install("iconv"), x, from_string, to_string, sub); |
62 | 22 | else |
63 | 22 | call = Rf_lang6(Rf_install("iconv"), x, from_string, to_string, sub, |
64 | 22 | extra); |
65 | 46 | Rf_protect(call); |
66 | | |
67 | 46 | SET_TAG(CDDR(call), Rf_install("from")); |
68 | 46 | SET_TAG(CDR(CDDR(call)), Rf_install("to")); |
69 | 46 | SET_TAG(CDR(CDR(CDDR(call))), Rf_install("sub")); |
70 | 46 | if (extra != NULL) |
71 | 22 | SET_TAG(CDR(CDR(CDR(CDDR(call)))), Rf_install(extra_name)); |
72 | | |
73 | | /* The caller protects the returned call before anything allocates. */ |
74 | 46 | Rf_unprotect(3); |
75 | 46 | return call; |
76 | 46 | } |
77 | | |
78 | | int LLVMFuzzerInitialize(int *argc, char ***argv) |
79 | 4 | { |
80 | 4 | (void)argc; |
81 | 4 | (void)argv; |
82 | 4 | fuzz_init_r(); |
83 | | |
84 | 4 | static const char *const from[N_RAW_CALLS] = { |
85 | 4 | "UTF-8", "latin1", "CP1252", "UTF-16LE", "UTF-16BE", "" |
86 | 4 | }; |
87 | 4 | static const char *const to[N_RAW_CALLS] = { |
88 | 4 | "UTF-8", "UTF-8", "UTF-8", "UTF-8", "UTF-8", "ASCII//TRANSLIT" |
89 | 4 | }; |
90 | | |
91 | 4 | Rf_protect(x_list = Rf_allocVector(VECSXP, 1)); |
92 | 4 | SET_VECTOR_ELT(x_list, 0, Rf_allocVector(RAWSXP, 0)); |
93 | 4 | Rf_protect(x_str = Rf_allocVector(STRSXP, 1)); |
94 | 4 | Rf_protect(x_utf8 = Rf_allocVector(STRSXP, 1)); |
95 | 4 | Rf_protect(x_latin1 = Rf_allocVector(STRSXP, 1)); |
96 | | |
97 | 4 | SEXP sub_byte, sub_unicode, sub_c99, sub_empty, sub_na, sub_qm; |
98 | 4 | SEXP true_val, false_val; |
99 | 4 | Rf_protect(sub_byte = Rf_mkString("byte")); |
100 | 4 | Rf_protect(sub_unicode = Rf_mkString("Unicode")); |
101 | 4 | Rf_protect(sub_c99 = Rf_mkString("c99")); |
102 | 4 | Rf_protect(sub_empty = Rf_mkString("")); |
103 | 4 | Rf_protect(sub_na = Rf_ScalarString(NA_STRING)); |
104 | 4 | Rf_protect(sub_qm = Rf_mkString("?")); |
105 | 4 | Rf_protect(true_val = Rf_ScalarLogical(TRUE)); |
106 | 4 | Rf_protect(false_val = Rf_ScalarLogical(FALSE)); |
107 | | |
108 | 4 | int k = 0; |
109 | 34 | for (int i = 0; i < N_RAW_CALLS; i++) { |
110 | 30 | Rf_protect(calls[k] = iconv_call(x_list, from[i], to[i], sub_byte, |
111 | 30 | "toRaw", true_val)); |
112 | 30 | inputs[k++] = IN_RAW; |
113 | 30 | } |
114 | | |
115 | | /* Character input, explicit from: the substitution modes */ |
116 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "latin1", sub_byte, |
117 | 4 | NULL, NULL)); |
118 | 4 | inputs[k++] = IN_STR; |
119 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "ASCII", sub_unicode, |
120 | 4 | NULL, NULL)); |
121 | 4 | inputs[k++] = IN_STR; |
122 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "ASCII", sub_c99, |
123 | 4 | NULL, NULL)); |
124 | 4 | inputs[k++] = IN_STR; |
125 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "UTF-16LE", sub_byte, |
126 | 4 | "toRaw", true_val)); |
127 | 4 | inputs[k++] = IN_STR; |
128 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "latin1", sub_na, |
129 | 4 | NULL, NULL)); |
130 | 4 | inputs[k++] = IN_STR; |
131 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "ASCII", sub_empty, |
132 | 4 | NULL, NULL)); |
133 | 4 | inputs[k++] = IN_STR; |
134 | | |
135 | | /* from="" with marked input: do_iconv keys the converter on the |
136 | | * CHARSXP mark, with its own fallback substitutions */ |
137 | 4 | Rf_protect(calls[k] = iconv_call(x_utf8, "", "latin1", sub_qm, |
138 | 4 | "mark", true_val)); |
139 | 4 | inputs[k++] = IN_UTF8; |
140 | 4 | Rf_protect(calls[k] = iconv_call(x_latin1, "", "UTF-8", sub_byte, |
141 | 4 | "mark", true_val)); |
142 | 4 | inputs[k++] = IN_LATIN1; |
143 | 4 | Rf_protect(calls[k] = iconv_call(x_utf8, "", "ASCII//TRANSLIT", sub_byte, |
144 | 4 | NULL, NULL)); |
145 | 4 | inputs[k++] = IN_UTF8; |
146 | 4 | Rf_protect(calls[k] = iconv_call(x_str, "UTF-8", "UTF-8", sub_unicode, |
147 | 4 | NULL, NULL)); |
148 | 4 | inputs[k++] = IN_STR; |
149 | 4 | Rf_protect(calls[k] = iconv_call(x_latin1, "latin1", "UTF-8", sub_byte, |
150 | 4 | "mark", false_val)); |
151 | 4 | inputs[k++] = IN_LATIN1; |
152 | | |
153 | | /* The mark helpers around iconv */ |
154 | 4 | Rf_protect(calls[k] = Rf_lang2(Rf_install("enc2utf8"), x_latin1)); |
155 | 4 | inputs[k++] = IN_LATIN1; |
156 | 4 | Rf_protect(calls[k] = Rf_lang2(Rf_install("enc2native"), x_utf8)); |
157 | 4 | inputs[k++] = IN_UTF8; |
158 | 4 | Rf_protect(calls[k] = Rf_lang2(Rf_install("validUTF8"), x_str)); |
159 | 4 | inputs[k++] = IN_STR; |
160 | 4 | Rf_protect(calls[k] = Rf_lang2(Rf_install("validEnc"), x_utf8)); |
161 | 4 | inputs[k++] = IN_UTF8; |
162 | 4 | { |
163 | 4 | SEXP latin1, set_enc; |
164 | 4 | Rf_protect(latin1 = Rf_mkString("latin1")); |
165 | 4 | Rf_protect(set_enc = Rf_lang3(Rf_install("Encoding<-"), x_str, latin1)); |
166 | 4 | Rf_protect(calls[k] = Rf_lang2(Rf_install("enc2utf8"), set_enc)); |
167 | 4 | inputs[k++] = IN_STR; |
168 | 4 | } |
169 | | |
170 | | /* Raw input through decoders with BOMs and shift states */ |
171 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "UTF-16", "UTF-8", sub_byte, |
172 | 4 | NULL, NULL)); |
173 | 4 | inputs[k++] = IN_RAW; |
174 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "UTF-32", "UTF-8", sub_byte, |
175 | 4 | NULL, NULL)); |
176 | 4 | inputs[k++] = IN_RAW; |
177 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "UTF-7", "UTF-8", sub_byte, |
178 | 4 | NULL, NULL)); |
179 | 4 | inputs[k++] = IN_RAW; |
180 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "ISO-2022-JP", "UTF-8", sub_byte, |
181 | 4 | NULL, NULL)); |
182 | 4 | inputs[k++] = IN_RAW; |
183 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "GB18030", "UTF-8", sub_byte, |
184 | 4 | NULL, NULL)); |
185 | 4 | inputs[k++] = IN_RAW; |
186 | 4 | Rf_protect(calls[k] = iconv_call(x_list, "UTF-8", "UTF-16", sub_byte, |
187 | 4 | "toRaw", true_val)); |
188 | 4 | inputs[k++] = IN_RAW; |
189 | | |
190 | 4 | if (k != N_CALLS) |
191 | 0 | abort(); |
192 | | |
193 | 4 | SET_STRING_ELT(x_str, 0, Rf_mkChar("fuzz")); |
194 | 4 | SET_STRING_ELT(x_utf8, 0, Rf_mkCharCE("fuzz", CE_UTF8)); |
195 | 4 | SET_STRING_ELT(x_latin1, 0, Rf_mkCharCE("fuzz", CE_LATIN1)); |
196 | 4 | return 0; |
197 | 4 | } |
198 | | |
199 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
200 | 1.60k | { |
201 | 1.60k | if (size < 2 || size > FUZZ_MAX_INPUT) |
202 | 6 | return 0; |
203 | | |
204 | 1.60k | int selected = data[0] % N_CALLS; |
205 | | |
206 | 1.60k | if (inputs[selected] == IN_RAW) { |
207 | 693 | encoding_input_t input = { data + 1, size - 1 }; |
208 | 693 | if (!R_ToplevelExec(set_encoding_input, &input)) |
209 | 0 | return 0; |
210 | 907 | } else { |
211 | | /* Character inputs cannot carry a NUL. */ |
212 | 907 | if (memchr(data + 1, '\0', size - 1)) |
213 | 8 | return 0; |
214 | | |
215 | 899 | char buffer[FUZZ_MAX_INPUT]; |
216 | 899 | memcpy(buffer, data + 1, size - 1); |
217 | 899 | buffer[size - 1] = '\0'; |
218 | | |
219 | 899 | Rboolean ok; |
220 | 899 | switch (inputs[selected]) { |
221 | 126 | case IN_UTF8: |
222 | 126 | ok = fuzz_set_string_ce(x_utf8, buffer, CE_UTF8); |
223 | 126 | break; |
224 | 40 | case IN_LATIN1: |
225 | 40 | ok = fuzz_set_string_ce(x_latin1, buffer, CE_LATIN1); |
226 | 40 | break; |
227 | 733 | default: |
228 | 733 | ok = fuzz_set_string(x_str, buffer); |
229 | 733 | break; |
230 | 899 | } |
231 | 899 | if (!ok) |
232 | 0 | return 0; |
233 | 899 | } |
234 | | |
235 | 1.59k | fuzz_eval_silent(calls[selected], R_GlobalEnv); |
236 | 1.59k | return 0; |
237 | 1.60k | } |