source: zzuf/trunk/src/load-stream.c @ 1608

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