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

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