Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.89k
#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.93k
#define NEED_X      0x01
33
2.89k
#define NEED_XU     0x02
34
2.85k
#define NEED_XL     0x04
35
2.87k
#define NEED_AUX    0x08
36
2.87k
#define NEED_AUXU   0x10
37
2.87k
#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
4
{
59
4
    (void)argc;
60
4
    (void)argv;
61
4
    fuzz_init_r();
62
63
4
    SEXP bytes, chars, width, true_value, one, end, empty, quote, none;
64
4
    Rf_protect(input_string = Rf_allocVector(STRSXP, 1));
65
4
    Rf_protect(input_utf8 = Rf_allocVector(STRSXP, 1));
66
4
    Rf_protect(input_latin1 = Rf_allocVector(STRSXP, 1));
67
4
    Rf_protect(aux_string = Rf_allocVector(STRSXP, 1));
68
4
    Rf_protect(aux_utf8 = Rf_allocVector(STRSXP, 1));
69
4
    Rf_protect(bytes = Rf_mkString("bytes"));
70
4
    Rf_protect(chars = Rf_mkString("chars"));
71
4
    Rf_protect(width = Rf_mkString("width"));
72
4
    Rf_protect(true_value = Rf_ScalarLogical(TRUE));
73
4
    Rf_protect(one = Rf_ScalarInteger(1));
74
4
    Rf_protect(end = Rf_ScalarInteger(32));
75
4
    Rf_protect(empty = Rf_mkString(""));
76
4
    Rf_protect(quote = Rf_mkString("\""));
77
4
    Rf_protect(none = Rf_mkString("none"));
78
79
4
    SEXP nchar = Rf_install("nchar");
80
4
    Rf_protect(calls[0] = Rf_lang3(nchar, input_string, bytes));
81
4
    SET_TAG(CDDR(calls[0]), Rf_install("type"));
82
4
    Rf_protect(calls[1] = Rf_lang3(nchar, input_string, chars));
83
4
    SET_TAG(CDDR(calls[1]), Rf_install("type"));
84
4
    Rf_protect(calls[2] = Rf_lang3(nchar, input_string, width));
85
4
    SET_TAG(CDDR(calls[2]), Rf_install("type"));
86
4
    Rf_protect(calls[3] = Rf_lang4(Rf_install("substr"), input_string, one, end));
87
4
    Rf_protect(calls[4] = Rf_lang4(Rf_install("substring"), input_string, one, end));
88
4
    Rf_protect(calls[5] = Rf_lang4(Rf_install("strsplit"), input_string, empty,
89
4
                                   true_value));
90
4
    SET_TAG(CDDDR(calls[5]), Rf_install("fixed"));
91
4
    Rf_protect(calls[6] = Rf_lang5(Rf_install("encodeString"), input_string,
92
4
                                   quote, none, true_value));
93
4
    SET_TAG(CDDR(calls[6]), Rf_install("quote"));
94
4
    SET_TAG(CDDDR(calls[6]), Rf_install("justify"));
95
4
    SET_TAG(CDR(CDDDR(calls[6])), Rf_install("na.encode"));
96
4
    Rf_protect(calls[7] = Rf_lang3(Rf_install("make.names"), input_string,
97
4
                                   true_value));
98
4
    SET_TAG(CDDR(calls[7]), Rf_install("unique"));
99
100
4
    SEXP to_raw;
101
4
    Rf_protect(to_raw = Rf_lang2(Rf_install("charToRaw"), input_string));
102
4
    Rf_protect(calls[8] = Rf_lang3(Rf_install("rawToChar"), to_raw, true_value));
103
4
    SET_TAG(CDDR(calls[8]), Rf_install("multiple"));
104
34
    for (int i = 0; i < N_FIXED_CALLS; i++)
105
30
        needs[i] = NEED_X;
106
107
4
    SEXP strsplit = Rf_install("strsplit");
108
4
    SEXP strtoi = Rf_install("strtoi");
109
4
    SEXP toupper = Rf_install("toupper");
110
4
    SEXP chartr = Rf_install("chartr");
111
4
    SEXP utf8ToInt = Rf_install("utf8ToInt");
112
4
    SEXP intToUtf8 = Rf_install("intToUtf8");
113
4
    SEXP two, three, four, ten, sixteen, thirtysix, scale;
114
4
    Rf_protect(two = Rf_ScalarInteger(2));
115
4
    Rf_protect(three = Rf_ScalarInteger(3));
116
4
    Rf_protect(four = Rf_ScalarInteger(4));
117
4
    Rf_protect(ten = Rf_ScalarInteger(10));
118
4
    Rf_protect(sixteen = Rf_ScalarInteger(16));
119
4
    Rf_protect(thirtysix = Rf_ScalarInteger(36));
120
4
    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
4
    SEXP upper_aux, upper_aux_utf8, codepoints, scaled_bytes;
125
4
    Rf_protect(upper_aux = Rf_lang2(toupper, aux_string));
126
4
    Rf_protect(upper_aux_utf8 = Rf_lang2(toupper, aux_utf8));
127
4
    Rf_protect(codepoints = Rf_lang2(utf8ToInt, input_string));
128
4
    Rf_protect(scaled_bytes = Rf_lang2(Rf_install("as.integer"), to_raw));
129
4
    Rf_protect(scaled_bytes = Rf_lang3(Rf_install("*"), scaled_bytes, scale));
130
131
4
    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
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_string,
138
4
                                                 aux_string, true_value,
139
4
                                                 true_value),
140
4
                                        3, "perl"), 4, "useBytes"));
141
4
    needs[k++] = NEED_X | NEED_AUX | NEED_REGEX;
142
4
    Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_string, aux_string,
143
4
                                          true_value), 3, "perl"));
144
4
    needs[k++] = NEED_X | NEED_AUX | NEED_REGEX;
145
4
    Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_string, aux_string,
146
4
                                          true_value), 3, "fixed"));
147
4
    needs[k++] = NEED_X | NEED_AUX;
148
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_string,
149
4
                                                 aux_string, true_value,
150
4
                                                 true_value),
151
4
                                        3, "fixed"), 4, "useBytes"));
152
4
    needs[k++] = NEED_X | NEED_AUX;
153
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang5(strsplit, input_utf8,
154
4
                                                 aux_utf8, true_value,
155
4
                                                 true_value),
156
4
                                        3, "perl"), 4, "useBytes"));
157
4
    needs[k++] = NEED_XU | NEED_AUXU | NEED_REGEX;
158
4
    Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_utf8, aux_utf8,
159
4
                                          true_value), 3, "perl"));
160
4
    needs[k++] = NEED_XU | NEED_AUXU | NEED_REGEX;
161
4
    Rf_protect(calls[k] = tagged(Rf_lang4(strsplit, input_utf8, aux_utf8,
162
4
                                          true_value), 3, "fixed"));
163
4
    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
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_utf8, chars,
168
4
                                                 true_value), 2, "type"),
169
4
                                 3, "allowNA"));
170
4
    needs[k++] = NEED_XU;
171
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_utf8, width,
172
4
                                                 true_value), 2, "type"),
173
4
                                 3, "allowNA"));
174
4
    needs[k++] = NEED_XU;
175
4
    Rf_protect(calls[k] = tagged(tagged(Rf_lang4(nchar, input_latin1, chars,
176
4
                                                 true_value), 2, "type"),
177
4
                                 3, "allowNA"));
178
4
    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
4
    Rf_protect(calls[k] = Rf_lang4(chartr, aux_string, upper_aux, input_string));
184
4
    needs[k++] = NEED_X | NEED_AUX;
185
4
    Rf_protect(calls[k] = Rf_lang4(chartr, aux_utf8, upper_aux_utf8, input_utf8));
186
4
    needs[k++] = NEED_XU | NEED_AUXU;
187
188
    /* strtoi in the bases with distinct digit handling */
189
4
    Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, ten));
190
4
    needs[k++] = NEED_X;
191
4
    Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, sixteen));
192
4
    needs[k++] = NEED_X;
193
4
    Rf_protect(calls[k] = Rf_lang3(strtoi, input_string, thirtysix));
194
4
    needs[k++] = NEED_X;
195
196
4
    Rf_protect(calls[k] = Rf_lang3(Rf_install("strtrim"), input_utf8, ten));
197
4
    needs[k++] = NEED_XU;
198
4
    Rf_protect(calls[k] = Rf_lang2(toupper, input_utf8));
199
4
    needs[k++] = NEED_XU;
200
4
    Rf_protect(calls[k] = Rf_lang2(Rf_install("tolower"), input_latin1));
201
4
    needs[k++] = NEED_XL;
202
4
    Rf_protect(calls[k] = tagged(Rf_lang3(Rf_install("abbreviate"), input_string,
203
4
                                          four), 2, "minlength"));
204
4
    needs[k++] = NEED_X;
205
206
    /* substr(x, 2, 10) <- aux */
207
4
    Rf_protect(calls[k] = Rf_lang5(Rf_install("substr<-"), input_utf8, two, ten,
208
4
                                   aux_utf8));
209
4
    needs[k++] = NEED_XU | NEED_AUXU;
210
211
4
    Rf_protect(calls[k] = Rf_lang3(Rf_install("strrep"), input_string, three));
212
4
    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
4
    calls[k] = codepoints;
218
4
    needs[k++] = NEED_X;
219
4
    Rf_protect(calls[k] = tagged(Rf_lang3(intToUtf8, codepoints, true_value),
220
4
                                 2, "multiple"));
221
4
    needs[k++] = NEED_X;
222
4
    Rf_protect(calls[k] = Rf_lang2(intToUtf8, scaled_bytes));
223
4
    needs[k++] = NEED_X;
224
225
4
    Rf_protect(calls[k] = Rf_lang4(Rf_install("substr"), input_utf8, two, end));
226
4
    needs[k++] = NEED_XU;
227
228
4
    if (k != N_CALLS)
229
0
        abort();
230
231
4
    SET_STRING_ELT(input_string, 0, Rf_mkChar("fuzz"));
232
4
    SET_STRING_ELT(input_utf8, 0, Rf_mkCharCE("fuzz", CE_UTF8));
233
4
    SET_STRING_ELT(input_latin1, 0, Rf_mkCharCE("fuzz", CE_LATIN1));
234
4
    SET_STRING_ELT(aux_string, 0, Rf_mkChar(DEFAULT_AUX));
235
4
    SET_STRING_ELT(aux_utf8, 0, Rf_mkCharCE(DEFAULT_AUX, CE_UTF8));
236
4
    return 0;
237
4
}
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
}