Coverage Report

Created: 2023-09-25 07:00

/src/minigzip_fuzzer.c
Line
Count
Source (jump to first uncovered line)
1
/* minigzip.c -- simulate gzip using the zlib compression library
2
 * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly
3
 * For conditions of distribution and use, see copyright notice in zlib.h
4
 */
5
6
/*
7
 * minigzip is a minimal implementation of the gzip utility. This is
8
 * only an example of using zlib and isn't meant to replace the
9
 * full-featured gzip. No attempt is made to deal with file systems
10
 * limiting names to 14 or 8+3 characters, etc... Error checking is
11
 * very limited. So use minigzip only for testing; use gzip for the
12
 * real thing.
13
 */
14
15
/* @(#) $Id$ */
16
17
#include "zlib.h"
18
#include <stdio.h>
19
#include <assert.h>
20
#include <string.h>
21
#include <stdlib.h>
22
#include <inttypes.h>
23
24
#ifdef USE_MMAP
25
#  include <sys/types.h>
26
#  include <sys/mman.h>
27
#  include <sys/stat.h>
28
#endif
29
30
#ifndef UNALIGNED_OK
31
#  include <malloc.h>
32
#endif
33
34
#if defined(WIN32) || defined(__CYGWIN__)
35
#  include <fcntl.h>
36
#  include <io.h>
37
#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
38
#else
39
#  define SET_BINARY_MODE(file)
40
#endif
41
42
#if defined(_MSC_VER) && _MSC_VER < 1900
43
#  define snprintf _snprintf
44
#endif
45
46
#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
47
#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
48
  extern int unlink (const char *);
49
#endif
50
#endif
51
52
#ifndef GZ_SUFFIX
53
22.4k
#  define GZ_SUFFIX ".gz"
54
#endif
55
11.2k
#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
56
57
#define BUFLEN      16384        /* read buffer size */
58
#define BUFLENW     (BUFLEN * 3) /* write buffer size */
59
#define MAX_NAME_LEN 1024
60
61
#ifdef Z_SOLO
62
/* for Z_SOLO, create simplified gz* functions using deflate and inflate */
63
64
#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
65
#  include <unistd.h>       /* for unlink() */
66
#endif
67
68
void *myalloc (void *, unsigned, unsigned);
69
void myfree (void *, void *);
70
71
void *myalloc(void *q, unsigned n, unsigned m)
72
{
73
    (void)q;
74
#ifndef UNALIGNED_OK
75
    return memalign(16, n * m);
76
#else
77
    return calloc(n, m);
78
#endif
79
}
80
81
void myfree(void *q, void *p)
82
{
83
    (void)q;
84
    free(p);
85
}
86
87
typedef struct gzFile_s {
88
    FILE *file;
89
    int write;
90
    int err;
91
    const char *msg;
92
    z_stream strm;
93
    unsigned char *buf;
94
} *gzFile;
95
96
gzFile gzopen(const char *, const char *);
97
gzFile gzdopen(int, const char *);
98
gzFile gz_open (const char *, int, const char *);
99
100
gzFile gzopen(const char *path, const char *mode)
101
{
102
    return gz_open(path, -1, mode);
103
}
104
105
gzFile gzdopen(int fd, const char *mode)
106
{
107
    return gz_open(NULL, fd, mode);
108
}
109
110
gzFile gz_open(const char *path, int fd, const char *mode)
111
{
112
    gzFile gz;
113
    int ret;
114
    int level = Z_DEFAULT_COMPRESSION;
115
    const char *plevel = mode;
116
117
    gz = malloc(sizeof(struct gzFile_s));
118
    if (gz == NULL)
119
        return NULL;
120
    gz->write = strchr(mode, 'w') != NULL;
121
    gz->strm.zalloc = myalloc;
122
    gz->strm.zfree = myfree;
123
    gz->strm.opaque = NULL;
124
    gz->buf = malloc(gz->write ? BUFLENW : BUFLEN);
125
126
    if (gz->buf == NULL) {
127
        free(gz);
128
        return NULL;
129
    }
130
131
    while (*plevel) {
132
        if (*plevel >= '0' && *plevel <= '9') {
133
           level = *plevel - '0';
134
           break;
135
        }
136
        plevel++;
137
    }
138
    if (gz->write)
139
        ret = deflateInit2(&(gz->strm), level, 8, 15 + 16, 8, 0);
140
    else {
141
        gz->strm.next_in = NULL;
142
        gz->strm.avail_in = 0;
143
        ret = inflateInit2(&(gz->strm), 15 + 16);
144
    }
145
    if (ret != Z_OK) {
146
        free(gz);
147
        return NULL;
148
    }
149
    gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
150
                              fopen(path, gz->write ? "wb" : "rb");
151
    if (gz->file == NULL) {
152
        gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
153
        free(gz);
154
        return NULL;
155
    }
156
    gz->err = 0;
157
    gz->msg = "";
158
    return gz;
159
}
160
161
int gzwrite(gzFile, const void *, unsigned);
162
163
int gzwrite(gzFile gz, const void *buf, unsigned len)
164
{
165
    z_stream *strm;
166
167
    if (gz == NULL || !gz->write)
168
        return 0;
169
    strm = &(gz->strm);
170
    strm->next_in = (void *)buf;
171
    strm->avail_in = len;
172
    do {
173
        strm->next_out = gz->buf;
174
        strm->avail_out = BUFLENW;
175
        (void)deflate(strm, Z_NO_FLUSH);
176
        fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
177
    } while (strm->avail_out == 0);
178
    return len;
179
}
180
181
int gzread(gzFile, void *, unsigned);
182
183
int gzread(gzFile gz, void *buf, unsigned len)
184
{
185
    z_stream *strm;
186
187
    if (gz == NULL || gz->write || gz->err)
188
        return 0;
189
    strm = &(gz->strm);
190
    strm->next_out = buf;
191
    strm->avail_out = len;
192
    do {
193
        if (strm->avail_in == 0)
194
        {
195
            strm->next_in = gz->buf;
196
            strm->avail_in = (uint32_t)fread(gz->buf, 1, BUFLEN, gz->file);
197
        }
198
        if (strm->avail_in > 0)
199
        {
200
            int ret = inflate(strm, Z_NO_FLUSH);
201
            if (ret == Z_DATA_ERROR) {
202
                gz->err = ret;
203
                gz->msg = strm->msg;
204
                return 0;
205
            }
206
            else if (ret == Z_STREAM_END)
207
                inflateReset(strm);
208
        }
209
        else
210
            break;
211
    } while (strm->avail_out);
212
    return len - strm->avail_out;
213
}
214
215
int gzclose(gzFile);
216
217
int gzclose(gzFile gz)
218
{
219
    z_stream *strm;
220
221
    if (gz == NULL)
222
        return Z_STREAM_ERROR;
223
    strm = &(gz->strm);
224
    if (gz->write) {
225
        strm->next_in = NULL;
226
        strm->avail_in = 0;
227
        do {
228
            strm->next_out = gz->buf;
229
            strm->avail_out = BUFLENW;
230
            (void)deflate(strm, Z_FINISH);
231
            fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
232
        } while (strm->avail_out == 0);
233
        deflateEnd(strm);
234
    }
235
    else
236
        inflateEnd(strm);
237
    free(gz->buf);
238
    fclose(gz->file);
239
    free(gz);
240
    return Z_OK;
241
}
242
243
const char *gzerror(gzFile, int *);
244
245
const char *gzerror(gzFile gz, int *err)
246
{
247
    *err = gz->err;
248
    return gz->msg;
249
}
250
251
#endif
252
253
static char *prog;
254
255
int error            (const char *msg);
256
int gz_compress      (FILE   *in, gzFile out);
257
#ifdef USE_MMAP
258
int  gz_compress_mmap (FILE   *in, gzFile out);
259
#endif
260
void gz_uncompress    (gzFile in, FILE   *out);
261
int file_compress    (char  *file, char *mode);
262
int file_uncompress  (char  *file);
263
int  main             (int argc, char *argv[]);
264
265
/* ===========================================================================
266
 * Display error message and return
267
 */
268
int error(const char *msg)
269
0
{
270
0
    fprintf(stderr, "%s: %s\n", prog, msg);
271
0
    return 0;
272
0
}
273
274
/* ===========================================================================
275
 * Compress input to output then close both files.
276
 */
277
278
int gz_compress(FILE   *in, gzFile out)
279
3.74k
{
280
3.74k
    char buf[BUFLEN];
281
3.74k
    int len;
282
3.74k
    int err;
283
284
#ifdef USE_MMAP
285
    /* Try first compressing with mmap. If mmap fails (minigzip used in a
286
     * pipe), use the normal fread loop.
287
     */
288
    if (gz_compress_mmap(in, out) == Z_OK) return;
289
#endif
290
    /* Clear out the contents of buf before reading from the file to avoid
291
       MemorySanitizer: use-of-uninitialized-value warnings. */
292
3.74k
    memset(buf, 0, sizeof(buf));
293
19.7k
    for (;;) {
294
19.7k
        len = (int)fread(buf, 1, sizeof(buf), in);
295
19.7k
        if (ferror(in)) {
296
0
            perror("fread");
297
0
            return 0;
298
0
        }
299
19.7k
        if (len == 0) break;
300
301
16.0k
        if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
302
16.0k
    }
303
3.74k
    fclose(in);
304
3.74k
    if (gzclose(out) != Z_OK) error("failed gzclose");
305
3.74k
    return 0;
306
3.74k
}
307
308
#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
309
310
/* Try compressing the input file at once using mmap. Return Z_OK if
311
 * if success, Z_ERRNO otherwise.
312
 */
313
int gz_compress_mmap(FILE   *in, gzFile out)
314
{
315
    int len;
316
    int err;
317
    int ifd = fileno(in);
318
    caddr_t buf;    /* mmap'ed buffer for the entire input file */
319
    off_t buf_len;  /* length of the input file */
320
    struct stat sb;
321
322
    /* Determine the size of the file, needed for mmap: */
323
    if (fstat(ifd, &sb) < 0) return Z_ERRNO;
324
    buf_len = sb.st_size;
325
    if (buf_len <= 0) return Z_ERRNO;
326
327
    /* Now do the actual mmap: */
328
    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
329
    if (buf == (caddr_t)(-1)) return Z_ERRNO;
330
331
    /* Compress the whole file at once: */
332
    len = gzwrite(out, (char *)buf, (unsigned)buf_len);
333
334
    if (len != (int)buf_len) error(gzerror(out, &err));
335
336
    munmap(buf, buf_len);
337
    fclose(in);
338
    if (gzclose(out) != Z_OK) error("failed gzclose");
339
    return Z_OK;
340
}
341
#endif /* USE_MMAP */
342
343
/* ===========================================================================
344
 * Uncompress input to output then close both files.
345
 */
346
void gz_uncompress(gzFile in, FILE   *out)
347
3.74k
{
348
3.74k
    char buf[BUFLENW];
349
3.74k
    int len;
350
3.74k
    int err;
351
352
11.1k
    for (;;) {
353
11.1k
        len = gzread(in, buf, sizeof(buf));
354
11.1k
        if (len < 0) error (gzerror(in, &err));
355
11.1k
        if (len == 0) break;
356
357
7.38k
        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
358
0
            error("failed fwrite");
359
0
        }
360
7.38k
    }
361
3.74k
    if (fclose(out)) error("failed fclose");
362
363
3.74k
    if (gzclose(in) != Z_OK) error("failed gzclose");
364
3.74k
}
365
366
367
/* ===========================================================================
368
 * Compress the given file: create a corresponding .gz file and remove the
369
 * original.
370
 */
371
int file_compress(char  *file, char  *mode)
372
3.74k
{
373
3.74k
    char outfile[MAX_NAME_LEN];
374
3.74k
    FILE  *in;
375
3.74k
    gzFile out;
376
377
3.74k
    if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
378
0
        fprintf(stderr, "%s: filename too long\n", prog);
379
0
        return 0;
380
0
    }
381
382
3.74k
    snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
383
384
3.74k
    in = fopen(file, "rb");
385
3.74k
    if (in == NULL) {
386
0
        perror(file);
387
0
        return 0;
388
0
    }
389
3.74k
    out = gzopen(outfile, mode);
390
3.74k
    if (out == NULL) {
391
0
        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
392
0
        return 0;
393
0
    }
394
3.74k
    gz_compress(in, out);
395
396
3.74k
    unlink(file);
397
3.74k
    return 0;
398
3.74k
}
399
400
401
/* ===========================================================================
402
 * Uncompress the given file and remove the original.
403
 */
404
int file_uncompress(char  *file)
405
3.74k
{
406
3.74k
    char buf[MAX_NAME_LEN];
407
3.74k
    char *infile, *outfile;
408
3.74k
    FILE  *out;
409
3.74k
    gzFile in;
410
3.74k
    size_t len = strlen(file);
411
412
3.74k
    if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
413
0
        fprintf(stderr, "%s: filename too long\n", prog);
414
0
        return 0;
415
0
    }
416
417
3.74k
    snprintf(buf, sizeof(buf), "%s", file);
418
419
3.74k
    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
420
3.74k
        infile = file;
421
3.74k
        outfile = buf;
422
3.74k
        outfile[len-3] = '\0';
423
3.74k
    } else {
424
0
        outfile = file;
425
0
        infile = buf;
426
0
        snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
427
0
    }
428
3.74k
    in = gzopen(infile, "rb");
429
3.74k
    if (in == NULL) {
430
0
        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
431
0
        return 0;
432
0
    }
433
3.74k
    out = fopen(outfile, "wb");
434
3.74k
    if (out == NULL) {
435
0
        perror(file);
436
0
        return 0;
437
0
    }
438
439
3.74k
    gz_uncompress(in, out);
440
441
3.74k
    unlink(infile);
442
3.74k
    return 0;
443
3.74k
}
444
445
3.74k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) {
446
3.74k
  char *inFileName = "/tmp/minigzip_fuzzer.out";
447
3.74k
  char *outFileName = "/tmp/minigzip_fuzzer.out.gz";
448
3.74k
  char outmode[20];
449
3.74k
  FILE *in;
450
3.74k
  char buf[BUFLEN];
451
3.74k
  uint32_t offset = 0;
452
453
  /* Discard inputs larger than 1Mb. */
454
3.74k
  static size_t kMaxSize = 1024 * 1024;
455
3.74k
  if (dataLen < 1 || dataLen > kMaxSize)
456
0
    return 0;
457
458
3.74k
  in = fopen(inFileName, "w");
459
3.74k
  if (fwrite(data, 1, (unsigned)dataLen, in) != dataLen)
460
0
    error("failed fwrite");
461
3.74k
  if (fclose(in))
462
0
    error("failed fclose");
463
464
3.74k
  memset(outmode, 0, sizeof(outmode));
465
3.74k
  snprintf(outmode, sizeof(outmode), "%s", "wb");
466
467
  /* Compression level: [0..9]. */
468
3.74k
  outmode[2] = data[0] % 10;
469
470
3.74k
  switch (data[0] % 4) {
471
0
  default:
472
1.12k
  case 0:
473
1.12k
    outmode[3] = 0;
474
1.12k
    break;
475
729
  case 1:
476
    /* compress with Z_FILTERED */
477
729
    outmode[3] = 'f';
478
729
    break;
479
959
  case 2:
480
    /* compress with Z_HUFFMAN_ONLY */
481
959
    outmode[3] = 'h';
482
959
    break;
483
932
  case 3:
484
    /* compress with Z_RLE */
485
932
    outmode[3] = 'R';
486
932
    break;
487
3.74k
  }
488
489
3.74k
  file_compress(inFileName, outmode);
490
3.74k
  file_uncompress(outFileName);
491
492
  /* Check that the uncompressed file matches the input data. */
493
3.74k
  in = fopen(inFileName, "rb");
494
3.74k
  if (in == NULL) {
495
0
    perror(inFileName);
496
0
    return 0;
497
0
  }
498
499
3.74k
  memset(buf, 0, sizeof(buf));
500
19.7k
  for (;;) {
501
19.7k
    int len = (int)fread(buf, 1, sizeof(buf), in);
502
19.7k
    if (ferror(in)) {
503
0
      perror("fread");
504
0
      return 0;
505
0
    }
506
19.7k
    if (len == 0)
507
3.74k
      break;
508
16.0k
    assert(0 == memcmp(data + offset, buf, len));
509
16.0k
    offset += len;
510
16.0k
  }
511
512
3.74k
  if (fclose(in))
513
0
    error("failed fclose");
514
515
  /* This function must return 0. */
516
3.74k
  return 0;
517
3.74k
}