/src/wavpack/fuzzing/fuzzer.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <stddef.h> |
2 | | #include <stdint.h> |
3 | | #include <string.h> |
4 | | #include <stdlib.h> |
5 | | #include <stdio.h> |
6 | | |
7 | | #include "wavpack.h" |
8 | | |
9 | | #ifdef __cplusplus |
10 | | using namespace std; |
11 | | #endif |
12 | | |
13 | 273k | #define BUF_SAMPLES 1024 |
14 | | |
15 | | typedef struct { |
16 | | unsigned char ungetc_char, ungetc_flag; |
17 | | unsigned char *sptr, *dptr, *eptr; |
18 | | int64_t total_bytes_read; |
19 | | } WavpackRawContext; |
20 | | |
21 | | static int32_t raw_read_bytes (void *id, void *data, int32_t bcount) |
22 | 7.39M | { |
23 | 7.39M | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
24 | 7.39M | unsigned char *outptr = (unsigned char *) data; |
25 | | |
26 | 14.6M | while (bcount) { |
27 | 7.34M | if (rcxt->ungetc_flag) { |
28 | 7.83k | *outptr++ = rcxt->ungetc_char; |
29 | 7.83k | rcxt->ungetc_flag = 0; |
30 | 7.83k | bcount--; |
31 | 7.83k | } |
32 | 7.33M | else { |
33 | 7.33M | size_t bytes_to_copy = rcxt->eptr - rcxt->dptr; |
34 | | |
35 | 7.33M | if (!bytes_to_copy) |
36 | 42.1k | break; |
37 | | |
38 | 7.29M | if (bytes_to_copy > bcount) |
39 | 7.25M | bytes_to_copy = bcount; |
40 | | |
41 | 7.29M | memcpy (outptr, rcxt->dptr, bytes_to_copy); |
42 | 7.29M | rcxt->total_bytes_read += bytes_to_copy; |
43 | 7.29M | rcxt->dptr += bytes_to_copy; |
44 | 7.29M | outptr += bytes_to_copy; |
45 | 7.29M | bcount -= bytes_to_copy; |
46 | 7.29M | } |
47 | 7.34M | } |
48 | | |
49 | 7.39M | return (int32_t)(outptr - (unsigned char *) data); |
50 | 7.39M | } |
51 | | |
52 | | static int32_t raw_write_bytes (void *id, void *data, int32_t bcount) |
53 | 2.93k | { |
54 | 2.93k | return data ? bcount : 0; |
55 | 2.93k | } |
56 | | |
57 | | static int64_t raw_get_pos (void *id) |
58 | 274k | { |
59 | 274k | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
60 | 274k | return rcxt->dptr - rcxt->sptr; |
61 | 274k | } |
62 | | |
63 | | static int raw_set_pos_abs (void *id, int64_t pos) |
64 | 50.3k | { |
65 | 50.3k | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
66 | | |
67 | 50.3k | if (rcxt->sptr + pos < rcxt->sptr || rcxt->sptr + pos > rcxt->eptr) |
68 | 14 | return 1; |
69 | | |
70 | 50.3k | rcxt->dptr = rcxt->sptr + pos; |
71 | 50.3k | return 0; |
72 | 50.3k | } |
73 | | |
74 | | static int raw_set_pos_rel (void *id, int64_t delta, int mode) |
75 | 1.69M | { |
76 | 1.69M | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
77 | 1.69M | unsigned char *ref = NULL; |
78 | | |
79 | 1.69M | if (mode == SEEK_SET) |
80 | 0 | ref = rcxt->sptr; |
81 | 1.69M | else if (mode == SEEK_CUR) |
82 | 1.67M | ref = rcxt->dptr; |
83 | 17.0k | else if (mode == SEEK_END) |
84 | 17.0k | ref = rcxt->eptr; |
85 | | |
86 | 1.69M | if (ref + delta < rcxt->sptr || ref + delta > rcxt->eptr) |
87 | 26.4k | return 1; |
88 | | |
89 | 1.66M | rcxt->dptr = ref + delta; |
90 | 1.66M | return 0; |
91 | 1.69M | } |
92 | | |
93 | | static int raw_push_back_byte (void *id, int c) |
94 | 7.83k | { |
95 | 7.83k | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
96 | 7.83k | rcxt->ungetc_char = c; |
97 | 7.83k | rcxt->ungetc_flag = 1; |
98 | 7.83k | return c; |
99 | 7.83k | } |
100 | | |
101 | | static int64_t raw_get_length (void *id) |
102 | 17.2k | { |
103 | 17.2k | WavpackRawContext *rcxt = (WavpackRawContext *) id; |
104 | 17.2k | return rcxt->eptr - rcxt->sptr; |
105 | 17.2k | } |
106 | | |
107 | | static int raw_can_seek (void *id) |
108 | 20.5k | { |
109 | 20.5k | return 1; |
110 | 20.5k | } |
111 | | |
112 | | static int raw_close_stream (void *id) |
113 | 7.83k | { |
114 | 7.83k | return 0; |
115 | 7.83k | } |
116 | | |
117 | | static WavpackStreamReader64 raw_reader = { |
118 | | raw_read_bytes, raw_write_bytes, raw_get_pos, raw_set_pos_abs, raw_set_pos_rel, |
119 | | raw_push_back_byte, raw_get_length, raw_can_seek, NULL, raw_close_stream |
120 | | }; |
121 | | |
122 | | static long long debug_log_mask = -1; |
123 | | |
124 | | #ifdef __cplusplus |
125 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
126 | | #else |
127 | | int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
128 | | #endif |
129 | 7.83k | { |
130 | 7.83k | static long long times_called, opens, seeks, tag_writes, samples_decoded, text_tags, binary_tags; |
131 | 7.83k | int flags = OPEN_TAGS | OPEN_EDIT_TAGS | OPEN_WRAPPER | OPEN_DSD_AS_PCM | OPEN_NO_CHECKSUM | OPEN_NORMALIZE | |
132 | 7.83k | (4 << OPEN_THREADS_SHFT); // spin up 4 worker threads for better fuzz coverage |
133 | 7.83k | WavpackRawContext raw_wv; |
134 | 7.83k | WavpackContext *wpc; |
135 | 7.83k | char error [80]; |
136 | 7.83k | int num_chans, bps, mode, qmode; |
137 | 7.83k | int64_t total_samples; |
138 | 7.83k | int retval = 0; |
139 | | |
140 | 7.83k | times_called++; |
141 | | |
142 | 7.83k | WavpackGetLibraryVersionString (); |
143 | 7.83k | WavpackGetLibraryVersion (); |
144 | | |
145 | 7.83k | memset (&raw_wv, 0, sizeof (WavpackRawContext)); |
146 | 7.83k | raw_wv.dptr = raw_wv.sptr = (unsigned char *) data; |
147 | 7.83k | raw_wv.eptr = raw_wv.dptr + size; |
148 | 7.83k | wpc = WavpackOpenFileInputEx64 (&raw_reader, &raw_wv, NULL, error, flags, 15); |
149 | | |
150 | 7.83k | if (!wpc) { |
151 | 1.05k | retval = 1; |
152 | 1.05k | goto exit; |
153 | 1.05k | } |
154 | | |
155 | 6.77k | opens++; |
156 | 6.77k | num_chans = WavpackGetNumChannels (wpc); |
157 | 6.77k | total_samples = WavpackGetNumSamples64 (wpc); |
158 | 6.77k | bps = WavpackGetBytesPerSample (wpc); |
159 | 6.77k | qmode = WavpackGetQualifyMode (wpc); |
160 | 6.77k | mode = WavpackGetMode (wpc); |
161 | | |
162 | | // call some other APIs for coverage |
163 | 6.77k | WavpackGetErrorMessage (wpc); |
164 | 6.77k | WavpackGetSampleIndex64 (wpc); |
165 | 6.77k | WavpackGetSampleIndex (wpc); |
166 | 6.77k | WavpackGetNumSamples (wpc); |
167 | 6.77k | WavpackGetNumErrors (wpc); |
168 | 6.77k | WavpackLossyBlocks (wpc); |
169 | 6.77k | WavpackGetProgress (wpc); |
170 | 6.77k | WavpackGetRatio (wpc); |
171 | 6.77k | WavpackGetAverageBitrate (wpc, 1); |
172 | 6.77k | WavpackGetInstantBitrate (wpc); |
173 | 6.77k | WavpackGetNativeSampleRate (wpc); |
174 | 6.77k | WavpackGetSampleRate (wpc); |
175 | 6.77k | WavpackGetChannelMask (wpc); |
176 | 6.77k | WavpackGetFloatNormExp (wpc); |
177 | 6.77k | WavpackGetBitsPerSample (wpc); |
178 | 6.77k | WavpackGetBytesPerSample (wpc); |
179 | 6.77k | WavpackGetReducedChannels (wpc); |
180 | 6.77k | WavpackGetVersion (wpc); |
181 | 6.77k | WavpackGetFileFormat (wpc); |
182 | 6.77k | WavpackGetFileExtension (wpc); |
183 | | |
184 | 6.77k | if (WavpackGetWrapperBytes (wpc)) |
185 | 78 | WavpackGetWrapperData (wpc); |
186 | | |
187 | 6.77k | if (num_chans) { |
188 | 6.77k | unsigned char identities [num_chans + 1]; |
189 | 6.77k | WavpackGetChannelIdentities (wpc, identities); |
190 | | |
191 | 6.77k | if (WavpackGetChannelLayout (wpc, NULL) & 0xff) { |
192 | 31 | unsigned char reordering [WavpackGetChannelLayout (wpc, NULL) & 0xff]; |
193 | 31 | WavpackGetChannelLayout (wpc, reordering); |
194 | 31 | } |
195 | 6.77k | } |
196 | | |
197 | | // Get all the metadata tags (text & binary) |
198 | 6.77k | if (mode & MODE_VALID_TAG) { |
199 | 902 | int num_binary_items = WavpackGetNumBinaryTagItems (wpc); |
200 | 902 | int num_items = WavpackGetNumTagItems (wpc), i; |
201 | | |
202 | 5.52k | for (i = 0; i < num_items; ++i) { |
203 | 4.62k | int item_len, value_len, j; |
204 | 4.62k | char *item, *value; |
205 | | |
206 | 4.62k | item_len = WavpackGetTagItemIndexed (wpc, i, NULL, 0); |
207 | 4.62k | item = (char *) malloc (item_len + 1); |
208 | 4.62k | WavpackGetTagItemIndexed (wpc, i, item, item_len + 1); |
209 | 4.62k | value_len = WavpackGetTagItem (wpc, item, NULL, 0); |
210 | 4.62k | value = (char *) malloc (value_len + 1); |
211 | 4.62k | WavpackGetTagItem (wpc, item, value, value_len + 1); |
212 | 4.62k | text_tags++; |
213 | 4.62k | free (value); |
214 | 4.62k | free (item); |
215 | 4.62k | } |
216 | | |
217 | 11.2k | for (i = 0; i < num_binary_items; ++i) { |
218 | 10.3k | int item_len, value_len; |
219 | 10.3k | char *item, *value; |
220 | | |
221 | 10.3k | item_len = WavpackGetBinaryTagItemIndexed (wpc, i, NULL, 0); |
222 | 10.3k | item = (char *) malloc (item_len + 1); |
223 | 10.3k | WavpackGetBinaryTagItemIndexed (wpc, i, item, item_len + 1); |
224 | 10.3k | value_len = WavpackGetBinaryTagItem (wpc, item, NULL, 0); |
225 | 10.3k | value = (char *) malloc (value_len); |
226 | 10.3k | WavpackGetBinaryTagItem (wpc, item, value, value_len); |
227 | 10.3k | binary_tags++; |
228 | 10.3k | free (value); |
229 | 10.3k | free (item); |
230 | 10.3k | } |
231 | | |
232 | 902 | WavpackAppendTagItem (wpc, "Artist", "The Googlers", strlen ("The Googlers")); |
233 | 902 | WavpackAppendTagItem (wpc, "Title", "Fuzz Me All Night Long", strlen ("Fuzz Me All Night Long")); |
234 | 902 | WavpackAppendTagItem (wpc, "Album", "Meet The Googlers", strlen ("Meet The Googlers")); |
235 | 902 | WavpackAppendBinaryTagItem (wpc, "Cover Art (Front)", (const char *) data, size < 4096 ? size : 4096); |
236 | 902 | } |
237 | | |
238 | | // Decode all |
239 | 6.77k | if (num_chans && num_chans <= 256) { |
240 | 6.57k | int32_t decoded_samples [BUF_SAMPLES * num_chans]; |
241 | 6.57k | unsigned char md5sum [16]; |
242 | 6.57k | int unpack_result; |
243 | | |
244 | 266k | do { |
245 | 266k | unpack_result = WavpackUnpackSamples (wpc, decoded_samples, BUF_SAMPLES); |
246 | 266k | samples_decoded += unpack_result; |
247 | 266k | } while (unpack_result); |
248 | | |
249 | 6.57k | WavpackGetMD5Sum (wpc, md5sum); |
250 | 6.57k | } |
251 | | |
252 | | // Seek to 1/3 of the way in plus 1000 samples (definitely not a block boundary) |
253 | 6.77k | if (WavpackSeekSample64 (wpc, total_samples / 3 + 1000)) { |
254 | 1.20k | ++seeks; |
255 | | |
256 | | // if we're still okay, try to write out the modified tags |
257 | 1.20k | if (WavpackWriteTag (wpc)) |
258 | 1.20k | ++tag_writes; |
259 | 1.20k | } |
260 | | |
261 | 6.77k | WavpackCloseFile (wpc); |
262 | | |
263 | 7.83k | exit: |
264 | 7.83k | if (!(times_called & debug_log_mask)) |
265 | 0 | printf ("LLVMFuzzerTestOneInput(): %lld calls, %lld opens, %lld seeks, %lld tag writes, %lld samples, %lld text & %lld binary tags\n", |
266 | 0 | times_called, opens, seeks, tag_writes, samples_decoded, text_tags, binary_tags); |
267 | | |
268 | 7.83k | return retval; |
269 | 6.77k | } |
270 | | |
271 | | #ifdef STAND_ALONE_LENGTH // max file length for stand-alone testing (sans fuzz) |
272 | | |
273 | | int main (int argc, char **argv) |
274 | | { |
275 | | unsigned char *buffer = (unsigned char *) malloc (STAND_ALONE_LENGTH); |
276 | | int index; |
277 | | |
278 | | // debug_log_mask = 0; |
279 | | |
280 | | for (index = 1; index < argc; ++index) { |
281 | | const char *filename = argv [index]; |
282 | | FILE *infile = fopen (filename, "rb"); |
283 | | int bytes_read; |
284 | | |
285 | | if (!infile) { |
286 | | fprintf (stderr, "can't open file %s!\n", filename); |
287 | | continue; |
288 | | } |
289 | | |
290 | | bytes_read = fread (buffer, 1, STAND_ALONE_LENGTH, infile); |
291 | | printf ("read %d bytes from file %s\n", bytes_read, filename); |
292 | | |
293 | | if (bytes_read == STAND_ALONE_LENGTH) |
294 | | printf ("warning: at maximum length, perhaps truncated!\n"); |
295 | | |
296 | | fclose (infile); |
297 | | LLVMFuzzerTestOneInput(buffer, bytes_read); |
298 | | } |
299 | | |
300 | | free (buffer); |
301 | | |
302 | | return 0; |
303 | | } |
304 | | |
305 | | #endif |