/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 | 53.8k | #define FUZZ_MAX_INPUT (1024 * 16) |
26 | 161k | #define FUZZ_HEADER 5 |
27 | 26.8k | #define N_CALLS 24 |
28 | | |
29 | | /* formatC flags; str_signif pastes these into the printf format. */ |
30 | 26.8k | #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 | 26.9k | { |
166 | 26.9k | if (size < FUZZ_HEADER + 1 || size > FUZZ_MAX_INPUT |
167 | 26.8k | || memchr(data + FUZZ_HEADER, '\0', size - FUZZ_HEADER)) |
168 | 97 | 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 | 26.8k | INTEGER(digits)[0] = 1 + data[1] % 22; |
173 | 26.8k | INTEGER(width)[0] = data[2] % 41; |
174 | 26.8k | INTEGER(nsmall)[0] = data[3] % 21; |
175 | 26.8k | SET_STRING_ELT(flag, 0, STRING_ELT(flags, data[4] % N_FLAGS)); |
176 | | |
177 | 26.8k | char buffer[FUZZ_MAX_INPUT]; |
178 | 26.8k | memcpy(buffer, data + FUZZ_HEADER, size - FUZZ_HEADER); |
179 | 26.8k | buffer[size - FUZZ_HEADER] = '\0'; |
180 | 26.8k | if (!fuzz_set_string(input_string, buffer)) |
181 | 0 | return 0; |
182 | | |
183 | 26.8k | fuzz_eval_silent(calls[data[0] % N_CALLS], R_GlobalEnv); |
184 | 26.8k | return 0; |
185 | 26.8k | } |