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

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