Coverage Report

Created: 2025-07-18 06:11

/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