Coverage Report

Created: 2026-09-14 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-oss-fuzz/harnesses/scan.c
Line
Count
Source
1
/*
2
 * libFuzzer harness for R's scan / delimited-text parser.
3
 *
4
 * Feeds fuzzed input to scan() as text with several type/separator
5
 * configurations, exercising the C-level parsing state machine in
6
 * src/main/scan.c: field separation, quoting, escapes, comment chars,
7
 * NA-string matching, and type coercion.  R's equivalent of a
8
 * csv.reader target.
9
 *
10
 * Adapted from r-afl's scan harness.
11
 */
12
13
#include <stdint.h>
14
#include <string.h>
15
16
#include "common.h"
17
18
2.21k
#define FUZZ_MAX_INPUT (1024 * 64)
19
11.0k
#define N_CALLS 4
20
21
static SEXP x_text;
22
static SEXP calls[N_CALLS];
23
24
int LLVMFuzzerInitialize(int *argc, char ***argv)
25
2
{
26
2
    fuzz_init_r();
27
28
2
    SEXP sym_scan = Rf_install("scan");
29
30
2
    SEXP str_empty, str_comma, str_tab, val_zero, val_true;
31
2
    Rf_protect(str_empty = Rf_mkString(""));
32
2
    Rf_protect(str_comma = Rf_mkString(","));
33
2
    Rf_protect(str_tab   = Rf_mkString("\t"));
34
2
    Rf_protect(val_zero  = Rf_ScalarReal(0.0));
35
2
    Rf_protect(val_true  = Rf_ScalarLogical(TRUE));
36
37
    /* Reusable text container -- each iteration swaps its CHARSXP. */
38
2
    Rf_protect(x_text = Rf_allocVector(STRSXP, 1));
39
40
    /* [0] scan(text=x, what="", quiet=TRUE) -- whitespace separated */
41
2
    Rf_protect(calls[0] = Rf_lang4(sym_scan, x_text, str_empty, val_true));
42
2
    SET_TAG(CDR(calls[0]),   Rf_install("text"));
43
2
    SET_TAG(CDDR(calls[0]),  Rf_install("what"));
44
2
    SET_TAG(CDDDR(calls[0]), Rf_install("quiet"));
45
46
    /* [1] scan(text=x, what=0, quiet=TRUE) -- numeric reading */
47
2
    Rf_protect(calls[1] = Rf_lang4(sym_scan, x_text, val_zero, val_true));
48
2
    SET_TAG(CDR(calls[1]),   Rf_install("text"));
49
2
    SET_TAG(CDDR(calls[1]),  Rf_install("what"));
50
2
    SET_TAG(CDDDR(calls[1]), Rf_install("quiet"));
51
52
    /* [2] scan(text=x, what="", sep=",", quiet=TRUE) -- CSV */
53
2
    Rf_protect(calls[2] = Rf_lang5(sym_scan, x_text, str_empty,
54
2
                                   val_true, str_comma));
55
2
    SET_TAG(CDR(calls[2]),        Rf_install("text"));
56
2
    SET_TAG(CDDR(calls[2]),       Rf_install("what"));
57
2
    SET_TAG(CDDDR(calls[2]),      Rf_install("quiet"));
58
2
    SET_TAG(CDR(CDDDR(calls[2])), Rf_install("sep"));
59
60
    /* [3] scan(text=x, what="", sep="\t", quiet=TRUE) -- TSV */
61
2
    Rf_protect(calls[3] = Rf_lang5(sym_scan, x_text, str_empty,
62
2
                                   val_true, str_tab));
63
2
    SET_TAG(CDR(calls[3]),        Rf_install("text"));
64
2
    SET_TAG(CDDR(calls[3]),       Rf_install("what"));
65
2
    SET_TAG(CDDDR(calls[3]),      Rf_install("quiet"));
66
2
    SET_TAG(CDR(CDDDR(calls[3])), Rf_install("sep"));
67
68
    /* Warmup: prime scan's type-dispatch and locale state. */
69
2
    {
70
2
        int error = 0;
71
2
        SET_STRING_ELT(x_text, 0, Rf_mkChar("1,2,3"));
72
10
        for (int i = 0; i < N_CALLS; i++) {
73
8
            R_tryEval(calls[i], R_GlobalEnv, &error);
74
8
            error = 0;
75
8
        }
76
2
    }
77
78
2
    return 0;
79
2
}
80
81
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
82
2.21k
{
83
2.21k
    if (size == 0 || size > FUZZ_MAX_INPUT)
84
3
        return 0;
85
86
2.21k
    char buf[FUZZ_MAX_INPUT + 1];
87
2.21k
    memcpy(buf, data, size);
88
2.21k
    buf[size] = '\0';
89
90
2.21k
    if (!fuzz_set_string(x_text, buf))
91
0
        return 0;
92
93
2.21k
    fuzz_eval_data_t ed = { .env = R_GlobalEnv };
94
11.0k
    for (int i = 0; i < N_CALLS; i++) {
95
8.86k
        ed.call = calls[i];
96
8.86k
        R_ToplevelExec(fuzz_do_eval, &ed);
97
8.86k
    }
98
99
2.21k
    return 0;
100
2.21k
}