source: zzuf/trunk/src/lib-stream.c @ 1791

Last change on this file since 1791 was 1791, checked in by Sam Hocevar, 13 years ago
  • Added -p flag to cherry pick file descriptors that get fuzzed.
  • Property svn:keywords set to Id
File size: 14.7 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: lib-stream.c 1791 2007-07-06 13:40:55Z sam $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  load-stream.c: loaded stream functions
17 */
18
19#include "config.h"
20
21#define _GNU_SOURCE /* for getline() and getdelim() */
22
23#if defined HAVE_STDINT_H
24#   include <stdint.h>
25#elif defined HAVE_INTTYPES_H
26#   include <inttypes.h>
27#endif
28#include <stdlib.h>
29
30#include <stdio.h>
31#include <sys/types.h>
32#if defined HAVE___SREFILL && defined HAVE_UNISTD_H
33#   include <unistd.h> /* Needed for __srefill’s lseek() call */
34#endif
35
36#include "libzzuf.h"
37#include "lib-load.h"
38#include "debug.h"
39#include "fuzz.h"
40#include "fd.h"
41
42#if defined HAVE___SREFILL
43int NEW(__srefill)(FILE *fp);
44#endif
45
46/* Library functions that we divert */
47static FILE *  (*ORIG(fopen))    (const char *path, const char *mode);
48#if defined HAVE_FOPEN64
49static FILE *  (*ORIG(fopen64))  (const char *path, const char *mode);
50#endif
51static FILE *  (*ORIG(freopen))  (const char *path, const char *mode,
52                                  FILE *stream);
53static int     (*ORIG(fseek))    (FILE *stream, long offset, int whence);
54#if defined HAVE_FSEEKO
55static int     (*ORIG(fseeko))   (FILE *stream, off_t offset, int whence);
56#endif
57static void    (*ORIG(rewind))   (FILE *stream);
58static size_t  (*ORIG(fread))    (void *ptr, size_t size, size_t nmemb,
59                                  FILE *stream);
60static int     (*ORIG(getc))     (FILE *stream);
61static int     (*ORIG(fgetc))    (FILE *stream);
62#if defined HAVE__IO_GETC
63static int     (*ORIG(_IO_getc)) (FILE *stream);
64#endif
65static char *  (*ORIG(fgets))    (char *s, int size, FILE *stream);
66static int     (*ORIG(ungetc))   (int c, FILE *stream);
67static int     (*ORIG(fclose))   (FILE *fp);
68
69/* Additional GNUisms */
70#if defined HAVE_GETLINE
71static ssize_t (*ORIG(getline))    (char **lineptr, size_t *n, FILE *stream);
72#endif
73#if defined HAVE_GETDELIM
74static ssize_t (*ORIG(getdelim))   (char **lineptr, size_t *n, int delim,
75                                    FILE *stream);
76#endif
77#if defined HAVE___GETDELIM
78static ssize_t (*ORIG(__getdelim)) (char **lineptr, size_t *n, int delim,
79                                    FILE *stream);
80#endif
81
82/* Additional BSDisms */
83#if defined HAVE_FGETLN
84static char *  (*ORIG(fgetln))    (FILE *stream, size_t *len);
85#endif
86#if defined HAVE___SREFILL
87int            (*ORIG(__srefill)) (FILE *fp);
88#endif
89
90/* Our function wrappers */
91#define FOPEN(fn) \
92    do \
93    { \
94        LOADSYM(fn); \
95        if(!_zz_ready) \
96            return ORIG(fn)(path, mode); \
97        _zz_lock(-1); \
98        ret = ORIG(fn)(path, mode); \
99        _zz_unlock(-1); \
100        if(ret && _zz_mustwatch(path)) \
101        { \
102            int fd = fileno(ret); \
103            _zz_register(fd); \
104            debug("%s(\"%s\", \"%s\") = [%i]", __func__, path, mode, fd); \
105        } \
106    } while(0)
107
108FILE *NEW(fopen)(const char *path, const char *mode)
109{
110    FILE *ret; FOPEN(fopen); return ret;
111}
112
113#if defined HAVE_FOPEN64
114FILE *NEW(fopen64)(const char *path, const char *mode)
115{
116    FILE *ret; FOPEN(fopen64); return ret;
117}
118#endif
119
120FILE *NEW(freopen)(const char *path, const char *mode, FILE *stream)
121{
122    FILE *ret;
123    int fd0 = -1, fd1 = -1, disp = 0;
124
125    LOADSYM(freopen);
126    if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0))
127    {
128        _zz_unregister(fd0);
129        disp = 1;
130    }
131
132    _zz_lock(-1);
133    ret = ORIG(freopen)(path, mode, stream);
134    _zz_unlock(-1);
135
136    if(ret && _zz_mustwatch(path))
137    {
138        fd1 = fileno(ret);
139        _zz_register(fd1);
140        disp = 1;
141    }
142
143    if(disp)
144        debug("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__,
145              path, mode, fd0, fd1);
146
147    return ret;
148}
149
150#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
151#   define FSEEK_FUZZ(fn2)
152#else
153#   define FSEEK_FUZZ(fn2) \
154        if(ret == 0) \
155        { \
156            /* FIXME: check what happens when fseek()ing a pipe */ \
157            switch(whence) \
158            { \
159                case SEEK_END: \
160                    offset = fn2(stream); \
161                    /* fall through */ \
162                case SEEK_SET: \
163                    _zz_setpos(fd, offset); \
164                    break; \
165                case SEEK_CUR: \
166                    _zz_addpos(fd, offset); \
167                    break; \
168            } \
169        }
170#endif
171
172#define FSEEK(fn, fn2) \
173    do \
174    { \
175        int fd; \
176        LOADSYM(fn); \
177        fd = fileno(stream); \
178        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
179            return ORIG(fn)(stream, offset, whence); \
180        _zz_lock(fd); \
181        ret = ORIG(fn)(stream, offset, whence); \
182        _zz_unlock(fd); \
183        debug("%s([%i], %lli, %i) = %i", __func__, \
184              fd, (long long int)offset, whence, ret); \
185        FSEEK_FUZZ(fn2) \
186    } while(0)
187
188int NEW(fseek)(FILE *stream, long offset, int whence)
189{
190    int ret; FSEEK(fseek, ftell); return ret;
191}
192
193#if defined HAVE_FSEEKO
194int NEW(fseeko)(FILE *stream, off_t offset, int whence)
195{
196    int ret; FSEEK(fseeko, ftello); return ret;
197}
198#endif
199
200void NEW(rewind)(FILE *stream)
201{
202    int fd;
203
204    LOADSYM(rewind);
205    fd = fileno(stream);
206    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
207    {
208        ORIG(rewind)(stream);
209        return;
210    }
211
212    _zz_lock(fd);
213    ORIG(rewind)(stream);
214    _zz_unlock(fd);
215    debug("%s([%i])", __func__, fd);
216
217#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
218#else
219    /* FIXME: check what happens when rewind()ing a pipe */
220    _zz_setpos(fd, 0);
221#endif
222}
223
224size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
225{
226    int64_t pos;
227#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
228#else
229    int64_t newpos;
230#endif
231    size_t ret;
232    int fd;
233
234    LOADSYM(fread);
235    fd = fileno(stream);
236    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
237        return ORIG(fread)(ptr, size, nmemb, stream);
238
239    pos = ftell(stream);
240    _zz_lock(fd);
241    ret = ORIG(fread)(ptr, size, nmemb, stream);
242    _zz_unlock(fd);
243
244#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
245#else
246    newpos = ftell(stream);
247    /* XXX: the number of bytes read is not ret * size, because
248     * a partial read may have advanced the stream pointer. However,
249     * when reading from a pipe ftell() will return 0, and ret * size
250     * is then better than nothing. */
251    if(newpos <= 0)
252    {
253        pos = _zz_getpos(fd);
254        newpos = pos + ret * size;
255    }
256    if(newpos != pos)
257    {
258        char *b = ptr;
259
260        _zz_fuzz(fd, ptr, newpos - pos);
261        _zz_setpos(fd, newpos);
262
263        if(newpos >= pos + 4)
264            debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, ptr,
265                  (long int)size, (long int)nmemb, fd, (long int)ret,
266                  b[0], b[1], b[2], b[3]);
267        else
268            debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr,
269                  (long int)size, (long int)nmemb, fd, (long int)ret, b[0]);
270    }
271    else
272#endif
273        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr,
274              (long int)size, (long int)nmemb, fd, (long int)ret);
275
276    return ret;
277}
278
279#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
280#   define FGETC_FUZZ
281#else
282#   define FGETC_FUZZ \
283        if(ret != EOF) \
284        { \
285            uint8_t ch = ret; \
286            _zz_fuzz(fd, &ch, 1); \
287            _zz_addpos(fd, 1); \
288            ret = ch; \
289        }
290#endif
291
292#define FGETC(fn) \
293    do { \
294        int fd; \
295        LOADSYM(fn); \
296        fd = fileno(stream); \
297        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
298            return ORIG(fn)(stream); \
299        _zz_lock(fd); \
300        ret = ORIG(fn)(stream); \
301        _zz_unlock(fd); \
302        FGETC_FUZZ \
303        if(ret == EOF) \
304            debug("%s([%i]) = EOF", __func__, fd); \
305        else \
306            debug("%s([%i]) = '%c'", __func__, fd, ret); \
307    } while(0)
308
309#undef getc /* can be a macro; we don’t want that */
310int NEW(getc)(FILE *stream)
311{
312    int ret; FGETC(getc); return ret;
313}
314
315int NEW(fgetc)(FILE *stream)
316{
317    int ret; FGETC(fgetc); return ret;
318}
319
320#if defined HAVE__IO_GETC
321int NEW(_IO_getc)(FILE *stream)
322{
323    int ret; FGETC(_IO_getc); return ret;
324}
325#endif
326
327char *NEW(fgets)(char *s, int size, FILE *stream)
328{
329    char *ret = s;
330    int fd;
331
332    LOADSYM(fgets);
333    LOADSYM(fgetc);
334    fd = fileno(stream);
335    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
336        return ORIG(fgets)(s, size, stream);
337
338#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
339    _zz_lock(fd);
340    ret = ORIG(fgets)(s, size, stream);
341    _zz_unlock(fd);
342#else
343    if(size <= 0)
344        ret = NULL;
345    else if(size == 1)
346        s[0] = '\0';
347    else
348    {
349        int i;
350
351        for(i = 0; i < size - 1; i++)
352        {
353            int ch;
354
355            _zz_lock(fd);
356            ch = ORIG(fgetc)(stream);
357            _zz_unlock(fd);
358
359            if(ch == EOF)
360            {
361                s[i] = '\0';
362                if(!i)
363                    ret = NULL;
364                break;
365            }
366            s[i] = (char)(unsigned char)ch;
367            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
368            _zz_addpos(fd, 1);
369            if(s[i] == '\n')
370            {
371                s[i + 1] = '\0';
372                break;
373            }
374        }
375    }
376#endif
377
378    debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret);
379    return ret;
380}
381
382int NEW(ungetc)(int c, FILE *stream)
383{
384    int ret, fd;
385
386    LOADSYM(ungetc);
387    fd = fileno(stream);
388    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
389        return ORIG(ungetc)(c, stream);
390
391    _zz_lock(fd);
392    ret = ORIG(ungetc)(c, stream);
393    _zz_unlock(fd);
394
395    if(ret != EOF)
396    {
397        struct fuzz *fuzz = _zz_getfuzz(fd);
398        fuzz->uflag = 1;
399        fuzz->upos = _zz_getpos(fd) - 1;
400        fuzz->uchar = c;
401#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
402#else
403        _zz_addpos(fd, -1);
404#endif
405    }
406
407    if(ret == EOF)
408        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
409    else
410        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
411
412    return ret;
413}
414
415int NEW(fclose)(FILE *fp)
416{
417    int ret, fd;
418
419    LOADSYM(fclose);
420    fd = fileno(fp);
421    if(!_zz_ready || !_zz_iswatched(fd))
422        return ORIG(fclose)(fp);
423
424    _zz_lock(fd);
425    ret = ORIG(fclose)(fp);
426    _zz_unlock(fd);
427    debug("%s([%i]) = %i", __func__, fd, ret);
428    _zz_unregister(fd);
429
430    return ret;
431}
432
433#define GETDELIM(fn, delim, need_delim) \
434    do { \
435        char *line; \
436        ssize_t done, size; \
437        int fd, finished = 0; \
438        LOADSYM(fn); \
439        LOADSYM(getdelim); \
440        LOADSYM(fgetc); \
441        fd = fileno(stream); \
442        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
443            return ORIG(getdelim)(lineptr, n, delim, stream); \
444        line = *lineptr; \
445        size = line ? *n : 0; \
446        ret = done = finished = 0; \
447        for(;;) \
448        { \
449            int ch; \
450            if(done >= size) /* highly inefficient but I don't care */ \
451                line = realloc(line, size = done + 1); \
452            if(finished) \
453            { \
454                line[done] = '\0'; \
455                *n = size; \
456                *lineptr = line; \
457                break; \
458            } \
459            _zz_lock(fd); \
460            ch = ORIG(fgetc)(stream); \
461            _zz_unlock(fd); \
462            if(ch == EOF) \
463            { \
464                finished = 1; \
465                ret = done; \
466            } \
467            else \
468            { \
469                unsigned char c = ch; \
470                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
471                line[done++] = c; \
472                _zz_addpos(fd, 1); \
473                if(c == delim) \
474                { \
475                    finished = 1; \
476                    ret = done; \
477                } \
478            } \
479        } \
480        if(need_delim) \
481            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
482                  lineptr, n, delim, fd, (long int)ret); \
483        else \
484            debug("%s(%p, %p, [%i]) = %li", __func__, \
485                  lineptr, n, fd, (long int)ret); \
486        return ret; \
487    } while(0)
488
489#if defined HAVE_GETLINE
490ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
491{
492    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
493}
494#endif
495
496#if defined HAVE_GETDELIM
497ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
498{
499    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
500}
501#endif
502
503#if defined HAVE___GETDELIM
504ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
505{
506    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
507}
508#endif
509
510#if defined HAVE_FGETLN
511char *NEW(fgetln)(FILE *stream, size_t *len)
512{
513    char *ret;
514#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
515#else
516    struct fuzz *fuzz;
517    size_t i, size;
518#endif
519    int fd;
520
521    LOADSYM(fgetln);
522    LOADSYM(fgetc);
523    fd = fileno(stream);
524    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
525        return ORIG(fgetln)(stream, len);
526
527#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
528    _zz_lock(fd);
529    ret = ORIG(fgetln)(stream, len);
530    _zz_unlock(fd);
531#else
532    fuzz = _zz_getfuzz(fd);
533
534    for(i = size = 0; ; /* i is incremented below */)
535    {
536        int ch;
537
538        _zz_lock(fd);
539        ch = ORIG(fgetc)(stream);
540        _zz_unlock(fd);
541
542        if(ch == EOF)
543            break;
544
545        if(i >= size)
546            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
547
548        fuzz->tmp[i] = (char)(unsigned char)ch;
549        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
550        _zz_addpos(fd, 1);
551
552        if(fuzz->tmp[i++] == '\n')
553            break;
554    }
555
556    *len = i;
557    ret = fuzz->tmp;
558#endif
559
560    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
561    return ret;
562}
563#endif
564
565#if defined HAVE___SREFILL
566int NEW(__srefill)(FILE *fp)
567{
568    off_t newpos;
569    int ret, fd, tmp;
570
571    LOADSYM(__srefill);
572    fd = fileno(fp);
573    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
574        return ORIG(__srefill)(fp);
575
576    _zz_lock(fd);
577    ret = ORIG(__srefill)(fp);
578    newpos = lseek(fd, 0, SEEK_CUR);
579    _zz_unlock(fd);
580    if(ret != EOF)
581    {
582        if(newpos != -1)
583            _zz_setpos(fd, newpos - fp->_r);
584        _zz_fuzz(fd, fp->_p, fp->_r);
585        _zz_addpos(fd, fp->_r);
586    }
587
588    if(!_zz_islocked(fd))
589        debug("%s([%i]) = %i", __func__, fd, ret);
590
591    return ret;
592}
593#endif
594
Note: See TracBrowser for help on using the repository browser.