Coverage Report

Created: 2026-09-14 06:56

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
5.96k
#define FUZZ_MAX_INPUT (1024 * 64)
19
38.9k
#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
4
{
26
4
    fuzz_init_r();
27
28
4
    SEXP sym_scan = Rf_install("scan");
29
30
4
    SEXP str_empty, str_comma, str_tab, val_zero, val_true;
31
4
    Rf_protect(str_empty = Rf_mkString(""));
32
4
    Rf_protect(str_comma = Rf_mkString(","));
33
4
    Rf_protect(str_tab   = Rf_mkString("\t"));
34
4
    Rf_protect(val_zero  = Rf_ScalarReal(0.0));
35
4
    Rf_protect(val_true  = Rf_ScalarLogical(TRUE));
36
37
    /* Reusable text container -- each iteration swaps its CHARSXP. */
38
4
    Rf_protect(x_text = Rf_allocVector(STRSXP, 1));
39
40
    /* [0] scan(text=x, what="", quiet=TRUE) -- whitespace separated */
41
4
    Rf_protect(calls[0] = Rf_lang4(sym_scan, x_text, str_empty, val_true));
42
4
    SET_TAG(CDR(calls[0]),   Rf_install("text"));
43
4
    SET_TAG(CDDR(calls[0]),  Rf_install("what"));
44
4
    SET_TAG(CDDDR(calls[0]), Rf_install("quiet"));
45
46
    /* [1] scan(text=x, what=0, quiet=TRUE) -- numeric reading */
47
4
    Rf_protect(calls[1] = Rf_lang4(sym_scan, x_text, val_zero, val_true));
48
4
    SET_TAG(CDR(calls[1]),   Rf_install("text"));
49
4
    SET_TAG(CDDR(calls[1]),  Rf_install("what"));
50
4
    SET_TAG(CDDDR(calls[1]), Rf_install("quiet"));
51
52
    /* [2] scan(text=x, what="", sep=",", quiet=TRUE) -- CSV */
53
4
    Rf_protect(calls[2] = Rf_lang5(sym_scan, x_text, str_empty,
54
4
                                   val_true, str_comma));
55
4
    SET_TAG(CDR(calls[2]),        Rf_install("text"));
56
4
    SET_TAG(CDDR(calls[2]),       Rf_install("what"));
57
4
    SET_TAG(CDDDR(calls[2]),      Rf_install("quiet"));
58
4
    SET_TAG(CDR(CDDDR(calls[2])), Rf_install("sep"));
59
60
    /* [3] scan(text=x, what="", sep="\t", quiet=TRUE) -- TSV */
61
4
    Rf_protect(calls[3] = Rf_lang5(sym_scan, x_text, str_empty,
62
4
                                   val_true, str_tab));
63
4
    SET_TAG(CDR(calls[3]),        Rf_install("text"));
64
4
    SET_TAG(CDDR(calls[3]),       Rf_install("what"));
65
4
    SET_TAG(CDDDR(calls[3]),      Rf_install("quiet"));
66
4
    SET_TAG(CDR(CDDDR(calls[3])), Rf_install("sep"));
67
68
    /* Warmup: prime scan's type-dispatch and locale state. */
69
4
    {
70
4
        int error = 0;
71
4
        SET_STRING_ELT(x_text, 0, Rf_mkChar("1,2,3"));
72
20
        for (int i = 0; i < N_CALLS; i++) {
73
16
            R_tryEval(calls[i], R_GlobalEnv, &error);
74
16
            error = 0;
75
16
        }
76
4
    }
77
78
4
    return 0;
79
4
}
80
81
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
82
5.96k
{
83
5.96k
    if (size == 0 || size > FUZZ_MAX_INPUT)
84
17
        return 0;
85
86
5.94k
    char buf[FUZZ_MAX_INPUT + 1];
87
5.94k
    memcpy(buf, data, size);
88
5.94k
    buf[size] = '\0';
89
90
5.94k
    if (!fuzz_set_string(x_text, buf))
91
0
        return 0;
92
93
5.94k
    fuzz_eval_data_t ed = { .env = R_GlobalEnv };
94
38.9k
    for (int i = 0; i < N_CALLS; i++) {
95
32.9k
        ed.call = calls[i];
96
32.9k
        R_ToplevelExec(fuzz_do_eval, &ed);
97
32.9k
    }
98
99
5.94k
    return 0;
100
5.94k
}