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/io.c
Line
Count
Source
1
/*
2
 * Exercise readBin() / writeBin() / readChar() / writeChar() over raw
3
 * vectors and in-memory raw connections.
4
 *
5
 * The first input byte selects a slot.  The first N_BIN slots are the
6
 * original typed readBin round trips (read, write back, read again,
7
 * and abort if the two reads differ).  The rest wrap the input in R
8
 * closures that reach the string readers (readChar, readBin of
9
 * "character"), the rawConnection layer (seek, readLines, the
10
 * connection-backed readBin), and the writeChar encoder -- none of
11
 * which the round trips touched.
12
 */
13
14
#include <stdint.h>
15
#include <string.h>
16
17
#include "common.h"
18
19
1.46k
#define FUZZ_MAX_INPUT (1024 * 64)
20
4.12k
#define N_BIN 8
21
1.52k
#define N_WRAP 10
22
1.45k
#define N_CALLS (N_BIN + N_WRAP)
23
24
static SEXP read_calls[N_BIN];
25
static SEXP write_calls[N_BIN];
26
static SEXP wrap_calls[N_WRAP];
27
28
/* Connections are closed on exit from each closure: a read error must
29
 * not leak one of R's 128 connection slots across iterations. */
30
static const char *const wrap_sources[N_WRAP] = {
31
    "function(x) readChar(x, c(1L, 5L, 16L, 64L), useBytes = TRUE)",
32
    "function(x) readChar(x, c(1L, 5L, 16L, 64L))",
33
    "function(x) { con <- rawConnection(x); on.exit(close(con)); "
34
    "readChar(con, c(1L, 5L, 16L, 64L)) }",
35
    "function(x) { s <- readChar(x, c(4L, 8L, 16L), useBytes = TRUE); "
36
    "r <- writeChar(s, raw(), nchars = c(4L, 8L, 16L), eos = NULL); "
37
    "readChar(r, c(4L, 8L, 16L), useBytes = TRUE) }",
38
    "function(x) { con <- rawConnection(x); on.exit(close(con)); "
39
    "readBin(con, \"character\", n = 64L) }",
40
    "function(x) { con <- rawConnection(x); on.exit(close(con)); "
41
    "readBin(con, \"integer\", n = 16384L, size = 2L, signed = FALSE) }",
42
    "function(x) readBin(x, \"integer\", n = 16384L, size = 1L, signed = TRUE)",
43
    "function(x) { con <- rawConnection(x); on.exit(close(con)); seek(con, 3L); "
44
    "readBin(con, \"double\", n = 16384L, size = 4L, endian = \"big\") }",
45
    "function(x) { con <- rawConnection(x); on.exit(close(con)); readLines(con) }",
46
    "function(x) { con <- rawConnection(x); on.exit(close(con)); "
47
    "readBin(con, \"numeric\", n = 16384L, endian = \"swap\") }",
48
};
49
50
typedef struct {
51
    int selected;
52
    const uint8_t *data;
53
    size_t size;
54
} io_input_t;
55
56
static void run_io(void *data)
57
299
{
58
299
    io_input_t *input = data;
59
299
    int error = 0;
60
299
    SEXP raw, value, encoded, roundtrip;
61
299
    Rf_protect(raw = Rf_allocVector(RAWSXP, (R_xlen_t)input->size));
62
299
    memcpy(RAW(raw), input->data, input->size);
63
299
    SETCADR(read_calls[input->selected], raw);
64
65
299
    value = R_tryEvalSilent(read_calls[input->selected], R_GlobalEnv, &error);
66
299
    if (!error) {
67
299
        SETCADR(write_calls[input->selected], value);
68
299
        encoded = R_tryEvalSilent(write_calls[input->selected], R_GlobalEnv,
69
299
                                  &error);
70
299
    }
71
299
    if (!error) {
72
299
        SETCADR(read_calls[input->selected], encoded);
73
299
        roundtrip = R_tryEvalSilent(read_calls[input->selected], R_GlobalEnv,
74
299
                                    &error);
75
299
    }
76
299
    if (!error) {
77
299
        Rf_protect(roundtrip);
78
299
        if (!R_compute_identical(value, roundtrip, IDENT_USE_CLOENV))
79
0
            abort();
80
299
        Rf_unprotect(1);
81
299
    }
82
299
    Rf_unprotect(1);
83
299
}
84
85
int LLVMFuzzerInitialize(int *argc, char ***argv)
86
6
{
87
6
    (void)argc;
88
6
    (void)argv;
89
6
    fuzz_init_r();
90
91
6
    SEXP what[N_BIN], sizes[N_BIN], endian[N_BIN];
92
6
    Rcomplex zero;
93
6
    zero.r = 0.0;
94
6
    zero.i = 0.0;
95
6
    Rf_protect(what[0] = Rf_allocVector(RAWSXP, 0));
96
6
    Rf_protect(what[1] = Rf_ScalarInteger(0));
97
6
    Rf_protect(what[2] = Rf_ScalarInteger(0));
98
6
    Rf_protect(what[3] = Rf_ScalarReal(0));
99
6
    Rf_protect(what[4] = Rf_ScalarReal(0));
100
6
    Rf_protect(what[5] = Rf_ScalarComplex(zero));
101
6
    Rf_protect(what[6] = Rf_mkString(""));
102
6
    Rf_protect(what[7] = Rf_ScalarLogical(FALSE));
103
104
6
    int size_values[N_BIN] = { 1, 1, 4, 4, 8, 16, 1, 4 };
105
6
    const char *endian_values[N_BIN] = {
106
6
        "little", "big", "little", "big", "little", "big", "little",
107
6
        "little"
108
6
    };
109
6
    SEXP placeholder, count, output;
110
6
    Rf_protect(placeholder = Rf_allocVector(RAWSXP, 0));
111
6
    Rf_protect(count = Rf_ScalarInteger(16384));
112
6
    Rf_protect(output = Rf_allocVector(RAWSXP, 0));
113
6
    SEXP read_bin = Rf_install("readBin");
114
6
    SEXP write_bin = Rf_install("writeBin");
115
116
66
    for (int i = 0; i < N_BIN; i++) {
117
60
        Rf_protect(sizes[i] = Rf_ScalarInteger(size_values[i]));
118
60
        Rf_protect(endian[i] = Rf_mkString(endian_values[i]));
119
60
        Rf_protect(read_calls[i] = Rf_lang6(read_bin, placeholder, what[i], count,
120
60
                                            sizes[i], endian[i]));
121
60
        SET_TAG(CDR(CDR(CDDR(read_calls[i]))), Rf_install("size"));
122
60
        SET_TAG(CDR(CDR(CDR(CDDR(read_calls[i])))), Rf_install("endian"));
123
124
60
        Rf_protect(write_calls[i] = Rf_lang5(write_bin, R_NilValue, output,
125
60
                                             sizes[i], endian[i]));
126
60
        SET_TAG(CDDDR(write_calls[i]), Rf_install("size"));
127
60
        SET_TAG(CDR(CDDDR(write_calls[i])), Rf_install("endian"));
128
60
    }
129
130
70
    for (int i = 0; i < N_WRAP; i++) {
131
64
        SEXP wrapper;
132
64
        Rf_protect(wrapper = fuzz_make_wrapper(wrap_sources[i]));
133
64
        Rf_protect(wrap_calls[i] = Rf_lang2(wrapper, placeholder));
134
64
    }
135
6
    return 0;
136
6
}
137
138
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
139
1.46k
{
140
1.46k
    if (size < 2 || size > FUZZ_MAX_INPUT)
141
11
        return 0;
142
143
1.45k
    int selected = data[0] % N_CALLS;
144
1.45k
    if (selected < N_BIN) {
145
299
        io_input_t input = { selected, data + 1, size - 1 };
146
299
        R_ToplevelExec(run_io, &input);
147
299
        return 0;
148
299
    }
149
150
1.15k
    SEXP call = wrap_calls[selected - N_BIN];
151
1.15k
    if (!fuzz_set_raw_arg(call, data + 1, size - 1))
152
0
        return 0;
153
154
1.15k
    fuzz_eval_silent(call, R_GlobalEnv);
155
1.15k
    return 0;
156
1.15k
}