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

Last change on this file since 1608 was 1608, checked in by Sam Hocevar, 14 years ago
  • Only use srefill on OpenBSD and FreeBSD.
  • Property svn:keywords set to Id
File size: 14.0 KB
Line 
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/*
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#include <dlfcn.h>
30
31#include <stdio.h>
32#include <sys/types.h>
33
34#include "libzzuf.h"
35#include "debug.h"
36#include "fuzz.h"
37#include "load.h"
38
39#if !defined __FreeBSD__ && !defined __OpenBSD__
40#   undef HAVE___SREFILL
41#endif
42
43#ifdef HAVE___SREFILL
44int __srefill(FILE *fp);
45#endif
46
47/* Library functions that we divert */
48static FILE *  (*fopen_orig)    (const char *path, const char *mode);
49#ifdef HAVE_FOPEN64
50static FILE *  (*fopen64_orig)  (const char *path, const char *mode);
51#endif
52static int     (*fseek_orig)    (FILE *stream, long offset, int whence);
53#ifdef HAVE_FSEEKO
54static int     (*fseeko_orig)   (FILE *stream, off_t offset, int whence);
55#endif
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);
61#ifdef HAVE__IO_GETC
62static int     (*_IO_getc_orig) (FILE *stream);
63#endif
64static char *  (*fgets_orig)    (char *s, int size, FILE *stream);
65static int     (*ungetc_orig)   (int c, FILE *stream);
66static int     (*fclose_orig)   (FILE *fp);
67
68/* Additional GNUisms */
69#ifdef HAVE_GETLINE
70static ssize_t (*getline_orig)    (char **lineptr, size_t *n, FILE *stream);
71#endif
72#ifdef HAVE_GETDELIM
73static ssize_t (*getdelim_orig)   (char **lineptr, size_t *n, int delim,
74                                   FILE *stream);
75#endif
76#ifdef HAVE___GETDELIM
77static ssize_t (*__getdelim_orig) (char **lineptr, size_t *n, int delim,
78                                   FILE *stream);
79#endif
80
81/* Additional BSDisms */
82#ifdef HAVE_FGETLN
83static char *  (*fgetln_orig)    (FILE *stream, size_t *len);
84#endif
85#ifdef HAVE___SREFILL
86int            (*__srefill_orig) (FILE *fp);
87#endif
88
89
90void _zz_load_stream(void)
91{
92    LOADSYM(fopen);
93#ifdef HAVE_FOPEN64
94    LOADSYM(fopen64);
95#endif
96    LOADSYM(fseek);
97#ifdef HAVE_FSEEKO
98    LOADSYM(fseeko);
99#endif
100    LOADSYM(rewind);
101    LOADSYM(fread);
102    LOADSYM(getc);
103    LOADSYM(fgetc);
104#ifdef HAVE__IO_GETC
105    LOADSYM(_IO_getc);
106#endif
107    LOADSYM(fgets);
108    LOADSYM(ungetc);
109    LOADSYM(fclose);
110#ifdef HAVE_GETLINE
111    LOADSYM(getline);
112#endif
113#ifdef HAVE_GETDELIM
114    LOADSYM(getdelim);
115#endif
116#ifdef HAVE___GETDELIM
117    LOADSYM(__getdelim);
118#endif
119#ifdef HAVE_FGETLN
120    LOADSYM(fgetln);
121#endif
122#ifdef HAVE___SREFILL
123    LOADSYM(__srefill);
124#endif
125}
126
127/* Our function wrappers */
128#define FOPEN(fn) \
129    do \
130    { \
131        if(!_zz_ready) \
132        { \
133            LOADSYM(fn); \
134            return ORIG(fn)(path, mode); \
135        } \
136        _zz_disabled = 1; \
137        ret = ORIG(fn)(path, mode); \
138        _zz_disabled = 0; \
139        if(ret && _zz_mustwatch(path)) \
140        { \
141            int fd = fileno(ret); \
142            _zz_register(fd); \
143            debug(STR(fn) "(\"%s\", \"%s\") = [%i]", path, mode, fd); \
144        } \
145    } while(0)
146
147FILE *fopen(const char *path, const char *mode)
148{
149    FILE *ret; FOPEN(fopen); return ret;
150}
151
152#ifdef HAVE_FOPEN64
153FILE *fopen64(const char *path, const char *mode)
154{
155    FILE *ret; FOPEN(fopen64); return ret;
156}
157#endif
158
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) \
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            } \
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) \
196    } while(0)
197
198int fseek(FILE *stream, long offset, int whence)
199{
200    int ret; FSEEK(fseek, ftell); return ret;
201}
202
203#ifdef HAVE_FSEEKO
204int fseeko(FILE *stream, off_t offset, int whence)
205{
206    int ret; FSEEK(fseeko, ftello); return ret;
207}
208#endif
209
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
228#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
229#else
230    /* FIXME: check what happens when rewind()ing a pipe */
231    _zz_setpos(fd, 0);
232#endif
233}
234
235size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
236{
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
242    size_t ret;
243    int fd;
244
245    if(!_zz_ready)
246        LOADSYM(fread);
247    fd = fileno(stream);
248    if(!_zz_ready || !_zz_iswatched(fd))
249        return fread_orig(ptr, size, nmemb, stream);
250
251    pos = ftell(stream);
252    _zz_disabled = 1;
253    ret = fread_orig(ptr, size, nmemb, stream);
254    _zz_disabled = 0;
255    debug("fread(%p, %li, %li, [%i]) = %li",
256          ptr, (long int)size, (long int)nmemb, fd, (long int)ret);
257
258#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
259#else
260    newpos = ftell(stream);
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)
268    {
269        _zz_fuzz(fd, ptr, newpos - pos);
270        _zz_setpos(fd, newpos);
271    }
272#endif
273
274    return ret;
275}
276
277#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
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
290#define FGETC(fn) \
291    do { \
292        int fd; \
293        if(!_zz_ready) \
294            LOADSYM(fn); \
295        fd = fileno(stream); \
296        if(!_zz_ready || !_zz_iswatched(fd)) \
297            return ORIG(fn)(stream); \
298        _zz_disabled = 1; \
299        ret = ORIG(fn)(stream); \
300        _zz_disabled = 0; \
301        FGETC_FUZZ \
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); \
306    } while(0)
307
308#undef getc /* can be a macro; we don’t want that */
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
319#ifdef HAVE__IO_GETC
320int _IO_getc(FILE *stream)
321{
322    int ret; FGETC(_IO_getc); return ret;
323}
324#endif
325
326char *fgets(char *s, int size, FILE *stream)
327{
328    char *ret = s;
329    int fd;
330
331    if(!_zz_ready)
332        LOADSYM(fgets);
333    fd = fileno(stream);
334    if(!_zz_ready || !_zz_iswatched(fd))
335        return fgets_orig(s, size, stream);
336
337#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
338    _zz_disabled = 1;
339    ret = fgets_orig(s, size, stream);
340    _zz_disabled = 0;
341#else
342    if(size <= 0)
343        ret = NULL;
344    else if(size == 1)
345        s[0] = '\0';
346    else
347    {
348        int i;
349
350        for(i = 0; i < size - 1; i++)
351        {
352            int ch;
353
354            _zz_disabled = 1;
355            ch = fgetc_orig(stream);
356            _zz_disabled = 0;
357
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;
366            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
367            _zz_addpos(fd, 1);
368            if(s[i] == '\n')
369            {
370                s[i + 1] = '\0';
371                break;
372            }
373        }
374    }
375#endif
376
377    debug("fgets(%p, %i, [%i]) = %p", s, size, fd, ret);
378    return ret;
379}
380
381int ungetc(int c, FILE *stream)
382{
383    unsigned char ch = c;
384    int ret, fd;
385
386    if(!_zz_ready)
387        LOADSYM(ungetc);
388    fd = fileno(stream);
389    if(!_zz_ready || !_zz_iswatched(fd))
390        return ungetc_orig(c, stream);
391
392#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
393#else
394    _zz_addpos(fd, -1);
395    _zz_fuzz(fd, &ch, 1);
396#endif
397    _zz_disabled = 1;
398    ret = ungetc_orig((int)ch, stream);
399    _zz_disabled = 0;
400
401    if(ret >= 0)
402        ret = c;
403#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
404#else
405    else
406        _zz_addpos(fd, 1); /* revert what we did */
407#endif
408
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);
413    return ret;
414}
415
416int fclose(FILE *fp)
417{
418    int ret, fd;
419
420    if(!_zz_ready)
421        LOADSYM(fclose);
422    fd = fileno(fp);
423    if(!_zz_ready || !_zz_iswatched(fd))
424        return fclose_orig(fp);
425
426    _zz_disabled = 1;
427    ret = fclose_orig(fp);
428    _zz_disabled = 0;
429    debug("fclose([%i]) = %i", fd, ret);
430    _zz_unregister(fd);
431
432    return ret;
433}
434
435#define GETDELIM(fn, delim, need_delim) \
436    do { \
437        char *line; \
438        ssize_t done, size; \
439        int fd, finished = 0; \
440        if(!_zz_ready) \
441            LOADSYM(fn); \
442        fd = fileno(stream); \
443        if(!_zz_ready || !_zz_iswatched(fd)) \
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            } \
460            _zz_disabled = 1; \
461            ch = fgetc_orig(stream); \
462            _zz_disabled = 0; \
463            if(ch == EOF) \
464            { \
465                finished = 1; \
466                ret = done; \
467            } \
468            else \
469            { \
470                unsigned char c = ch; \
471                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
472                line[done++] = c; \
473                _zz_addpos(fd, 1); \
474                if(c == delim) \
475                { \
476                    finished = 1; \
477                    ret = done; \
478                } \
479            } \
480        } \
481        if(need_delim) \
482            debug(STR(fn) "(%p, %p, 0x%02x, [%i]) = %li", \
483                  lineptr, n, delim, fd, (long int)ret); \
484        else \
485            debug(STR(fn) "(%p, %p, [%i]) = %li", \
486                  lineptr, n, fd, (long int)ret); \
487        return ret; \
488    } while(0)
489
490#ifdef HAVE_GETLINE
491ssize_t getline(char **lineptr, size_t *n, FILE *stream)
492{
493    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
494}
495#endif
496
497#ifdef HAVE_GETDELIM
498ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
499{
500    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
501}
502#endif
503
504#ifdef HAVE___GETDELIM
505ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
506{
507    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
508}
509#endif
510
511#ifdef HAVE_FGETLN
512char *fgetln(FILE *stream, size_t *len)
513{
514    char *ret;
515#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
516#else
517    struct fuzz *fuzz;
518    size_t i, size;
519#endif
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
528#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
529    _zz_disabled = 1;
530    ret = fgetln_orig(stream, len);
531    _zz_disabled = 0;
532#else
533    fuzz = _zz_getfuzz(fd);
534
535    for(i = size = 0; ; /* i is incremented below */)
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
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 */
551        _zz_addpos(fd, 1);
552
553        if(fuzz->tmp[i++] == '\n')
554            break;
555    }
556
557    *len = i;
558    ret = fuzz->tmp;
559#endif
560
561    debug("fgetln([%i], &%li) = %p", fd, (long int)*len, ret);
562    return ret;
563}
564#endif
565
566#ifdef HAVE___SREFILL
567int __srefill(FILE *fp)
568{
569    off_t oldpos, newpos;
570    int ret, fd;
571
572    if(!_zz_ready)
573        LOADSYM(__srefill);
574    fd = fileno(fp);
575    if(!_zz_ready || !_zz_iswatched(fd))
576        return __srefill_orig(fp);
577
578    oldpos = lseek(fd, 0, SEEK_CUR);
579    ret = __srefill_orig(fp);
580    newpos = lseek(fd, 0, SEEK_CUR);
581    if(ret != EOF)
582    {
583        if(newpos != -1)
584            _zz_setpos(fd, newpos - fp->_r);
585        _zz_fuzz(fd, fp->_p, fp->_r);
586        _zz_addpos(fd, fp->_r);
587    }
588
589    if(!_zz_disabled)
590        debug("__srefill([%i]) = %i", fd, ret);
591
592    return ret;
593}
594#endif
595
Note: See TracBrowser for help on using the repository browser.