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/format.c
Line
Count
Source
1
/*
2
 * Exercise R's number and string formatting and the default print
3
 * methods.
4
 *
5
 * src/main/format.c (formatReal/formatInteger/formatComplex and their
6
 * width computation), paste.c (do_format, do_formatinfo), util.c
7
 * (do_formatC and str_signif, which build a C printf format from the
8
 * caller's digits/width/flag), printvector.c, printarray.c, print.c and
9
 * printutils.c are reached by nothing else: sprintf() fuzzes the format
10
 * string but not R's own formatting of arbitrary doubles under arbitrary
11
 * digits/width/nsmall, and nothing prints.
12
 *
13
 * Input layout: byte 0 selects the slot, bytes 1-4 feed the numeric
14
 * parameters (digits, width, nsmall, formatC flag), the rest is a string
15
 * s.  Each slot derives the numeric vector it needs by splitting s on
16
 * separators, so the same corpus serves the string and numeric paths.
17
 * Printing goes to a null sink opened once at startup.
18
 */
19
20
#include <stdint.h>
21
#include <string.h>
22
23
#include "common.h"
24
25
10.5k
#define FUZZ_MAX_INPUT (1024 * 16)
26
31.6k
#define FUZZ_HEADER 5
27
5.31k
#define N_CALLS 24
28
29
/* formatC flags; str_signif pastes these into the printf format. */
30
5.28k
#define N_FLAGS 8
31
static const char *const flag_values[N_FLAGS] = {
32
    "", "-", "0", "+", " ", "#", "0-", "+#0"
33
};
34
35
static SEXP calls[N_CALLS];
36
static SEXP input_string, digits, width, nsmall, flag, flags;
37
38
/* Every slot has the signature (s, d, w, ns, fl).  num(s) is inlined as
39
 * a strsplit so the slots stay independent of each other; 500 numbers is
40
 * plenty for the width scans in formatReal. */
41
#define NUM "n <- as.numeric(strsplit(s, '[ ,;\\t\\n]+', perl = TRUE)[[1]]); n <- n[seq_len(min(500L, length(n)))];"
42
43
static const char *const sources[N_CALLS] = {
44
    /* 0 */
45
    "function(s, d, w, ns, fl) { format(s); format(s, width = w, justify = 'right');"
46
    "  format(s, justify = 'centre'); format(c(s, NA), na.encode = FALSE) }",
47
    /* 1 */
48
    "function(s, d, w, ns, fl) {" NUM " format(n); format(n, digits = d);"
49
    "  format(n, width = w) }",
50
    /* 2 */
51
    "function(s, d, w, ns, fl) {" NUM " format(n, digits = d, nsmall = ns);"
52
    "  format(n, nsmall = ns, width = w); format(NA_real_, nsmall = ns) }",
53
    /* 3 */
54
    "function(s, d, w, ns, fl) {" NUM " format(n, scientific = TRUE);"
55
    "  format(n, scientific = FALSE, digits = d); format(n, scientific = w - 20L) }",
56
    /* 4 */
57
    "function(s, d, w, ns, fl) {" NUM " format(n, big.mark = ',', small.mark = ' ',"
58
    "  small.interval = 2L); prettyNum(n, big.mark = \"'\", decimal.mark = ',');"
59
    "  prettyNum(s, big.mark = ',') }",
60
    /* 5 */
61
    "function(s, d, w, ns, fl) {" NUM " formatC(n, digits = d, width = w,"
62
    "  format = 'f', flag = fl) }",
63
    /* 6 */
64
    "function(s, d, w, ns, fl) {" NUM " formatC(n, digits = d, width = w,"
65
    "  format = 'e', flag = fl) }",
66
    /* 7 */
67
    "function(s, d, w, ns, fl) {" NUM " formatC(n, digits = d, width = w,"
68
    "  format = 'g', flag = fl); formatC(n, format = 'G', digits = d) }",
69
    /* 8 */
70
    "function(s, d, w, ns, fl) {" NUM " formatC(n, digits = d, width = w,"
71
    "  format = 'fg', flag = fl); formatC(n, format = 'fg', digits = d, flag = '#') }",
72
    /* 9 */
73
    "function(s, d, w, ns, fl) {" NUM " i <- as.integer(n); formatC(i, width = w,"
74
    "  format = 'd', flag = fl); formatC(n, format = 'd', big.mark = ',');"
75
    "  formatC(i, mode = 'integer', width = -w) }",
76
    /* 10 */
77
    "function(s, d, w, ns, fl) { formatC(s, width = w, flag = fl);"
78
    "  formatC(s, width = -w, format = 's'); formatC(s, mode = 'character',"
79
    "  width = w, flag = '-') }",
80
    /* 11 */
81
    "function(s, d, w, ns, fl) {" NUM " i <- as.integer(n); format(i);"
82
    "  format(i, width = w); format(i, big.mark = ','); toString(i, width = w) }",
83
    /* 12 */
84
    "function(s, d, w, ns, fl) {" NUM " z <- complex(real = n, imaginary = rev(n));"
85
    "  format(z); format(z, digits = d, nsmall = ns); format(as.complex(n), width = w) }",
86
    /* 13 */
87
    "function(s, d, w, ns, fl) {" NUM " i <- as.integer(n); i[is.na(i)] <- 0L;"
88
    "  format(as.raw(i %% 256L)); format(as.logical(n)); format(is.na(n), width = w);"
89
    "  format(list(s, n, i)) }",
90
    /* 14 */
91
    "function(s, d, w, ns, fl) {" NUM " toString(n); toString(s, width = w);"
92
    "  format(n, trim = TRUE, drop0trailing = TRUE); format(n, zero.print = '.') }",
93
    /* 15 */
94
    "function(s, d, w, ns, fl) {" NUM " print(n); print(n, digits = max(1L, d));"
95
    "  print(as.integer(n)); print(complex(real = n, imaginary = -n)) }",
96
    /* 16 */
97
    "function(s, d, w, ns, fl) { print(s, quote = FALSE); print(s);"
98
    "  print(noquote(s)); print(s, max = w); print(c(a = s)) }",
99
    /* 17 */
100
    "function(s, d, w, ns, fl) {" NUM " m <- matrix(n, nrow = max(1L, w %% 7L));"
101
    "  print(m); print(m, digits = max(1L, d)); print(t(m), quote = FALSE);"
102
    "  print(array(n, c(2L, 2L, max(1L, length(n) %/% 4L)))) }",
103
    /* 18 */
104
    "function(s, d, w, ns, fl) {" NUM " print(list(s, n, list(n, s)));"
105
    "  print(structure(n, names = rep_len(s, length(n))));"
106
    "  print(structure(n, class = 'nosuchclass')); utils::str(list(s, n)) }",
107
    /* 19 */
108
    "function(s, d, w, ns, fl) {" NUM " old <- options(OutDec = ',', digits = max(1L, d),"
109
    "  scipen = w - 20L); on.exit(options(old)); format(n); print(n); formatC(n);"
110
    "  format(n, nsmall = ns) }",
111
    /* 20 */
112
    "function(s, d, w, ns, fl) {" NUM " print(summary(n)); print(summary(s));"
113
    "  print(data.frame(a = n, b = rep_len(s, length(n)))) }",
114
    /* 21 */
115
    "function(s, d, w, ns, fl) {" NUM " cat(n, s, sep = ' ', fill = max(1L, w));"
116
    "  cat(s, fill = TRUE); cat(n, sep = '\\n'); cat(s, n, sep = ', ') }",
117
    /* 22 */
118
    "function(s, d, w, ns, fl) {" NUM " round(n, d - 10L); signif(n, max(1L, d));"
119
    "  format(round(n, d - 10L), nsmall = ns); formatC(signif(n, max(1L, d)), format = 'fg') }",
120
    /* 23 */
121
    "function(s, d, w, ns, fl) {" NUM " format(n, digits = d, width = w, justify = 'left');"
122
    "  format(c(n, NA), na.encode = TRUE); format(as.factor(s)); format(s, width = w,"
123
    "  justify = 'left', na.encode = FALSE); format(NULL); format(NA) }",
124
};
125
126
int LLVMFuzzerInitialize(int *argc, char ***argv)
127
2
{
128
2
    (void)argc;
129
2
    (void)argv;
130
2
    fuzz_init_r();
131
132
    /* All printing goes to the null device; nothing reads it back. */
133
2
    {
134
2
        int error = 0;
135
2
        SEXP sink_call;
136
2
        Rf_protect(sink_call = Rf_lang2(Rf_install("sink"),
137
2
                                        Rf_lang1(Rf_install("nullfile"))));
138
2
        R_tryEval(sink_call, R_BaseEnv, &error);
139
2
        Rf_unprotect(1);
140
2
        if (error)
141
0
            abort();
142
2
    }
143
144
2
    Rf_protect(input_string = Rf_allocVector(STRSXP, 1));
145
2
    Rf_protect(digits = Rf_ScalarInteger(7));
146
2
    Rf_protect(width = Rf_ScalarInteger(0));
147
2
    Rf_protect(nsmall = Rf_ScalarInteger(0));
148
2
    Rf_protect(flag = Rf_allocVector(STRSXP, 1));
149
2
    Rf_protect(flags = Rf_allocVector(STRSXP, N_FLAGS));
150
18
    for (int i = 0; i < N_FLAGS; i++)
151
16
        SET_STRING_ELT(flags, i, Rf_mkChar(flag_values[i]));
152
2
    SET_STRING_ELT(flag, 0, STRING_ELT(flags, 0));
153
154
50
    for (int i = 0; i < N_CALLS; i++) {
155
48
        SEXP wrapper;
156
48
        Rf_protect(wrapper = fuzz_make_wrapper(sources[i]));
157
48
        Rf_protect(calls[i] = Rf_lang6(wrapper, input_string, digits, width,
158
48
                                       nsmall, flag));
159
48
    }
160
2
    SET_STRING_ELT(input_string, 0, Rf_mkChar("1.5 2 3"));
161
2
    return 0;
162
2
}
163
164
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
165
5.28k
{
166
5.28k
    if (size < FUZZ_HEADER + 1 || size > FUZZ_MAX_INPUT
167
5.27k
        || memchr(data + FUZZ_HEADER, '\0', size - FUZZ_HEADER))
168
20
        return 0;
169
170
    /* Ranges follow what R accepts: digits 1..22, nsmall 0..20, and a
171
     * width small enough that the padded output stays a few KB. */
172
5.26k
    INTEGER(digits)[0] = 1 + data[1] % 22;
173
5.26k
    INTEGER(width)[0] = data[2] % 41;
174
5.26k
    INTEGER(nsmall)[0] = data[3] % 21;
175
5.26k
    SET_STRING_ELT(flag, 0, STRING_ELT(flags, data[4] % N_FLAGS));
176
177
5.26k
    char buffer[FUZZ_MAX_INPUT];
178
5.26k
    memcpy(buffer, data + FUZZ_HEADER, size - FUZZ_HEADER);
179
5.26k
    buffer[size - FUZZ_HEADER] = '\0';
180
5.26k
    if (!fuzz_set_string(input_string, buffer))
181
0
        return 0;
182
183
5.26k
    fuzz_eval_silent(calls[data[0] % N_CALLS], R_GlobalEnv);
184
5.26k
    return 0;
185
5.26k
}