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

Last change on this file since 1679 was 1679, checked in by Sam Hocevar, 16 years ago
  • Got rid of the *_load() functions. Now each diverted function is supposed to check that the *_orig() functions it calls are properly loaded.
  • 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 1679 2007-01-16 14:36:33Z 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/* Our function wrappers */
92#define FOPEN(fn) \
93    do \
94    { \
95        LOADSYM(fn); \
96        if(!_zz_ready) \
97            return ORIG(fn)(path, mode); \
98        _zz_disabled = 1; \
99        ret = ORIG(fn)(path, mode); \
100        _zz_disabled = 0; \
101        if(ret && _zz_mustwatch(path)) \
102        { \
103            int fd = fileno(ret); \
104            _zz_register(fd); \
105            debug(STR(fn) "(\"%s\", \"%s\") = [%i]", path, mode, fd); \
106        } \
107    } while(0)
108
109FILE *fopen(const char *path, const char *mode)
110{
111    FILE *ret; FOPEN(fopen); return ret;
112}
113
114#ifdef HAVE_FOPEN64
115FILE *fopen64(const char *path, const char *mode)
116{
117    FILE *ret; FOPEN(fopen64); return ret;
118}
119#endif
120
121FILE *freopen(const char *path, const char *mode, FILE *stream)
122{
123    FILE *ret;
124    int fd0 = -1, fd1 = -1, disp = 0;
125
126    LOADSYM(freopen);
127    if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0))
128    {
129        _zz_unregister(fd0);
130        disp = 1;
131    }
132
133    _zz_disabled = 1;
134    ret = freopen_orig(path, mode, stream);
135    _zz_disabled = 0;
136
137    if(ret && _zz_mustwatch(path))
138    {
139        fd1 = fileno(ret);
140        _zz_register(fd1);
141        disp = 1;
142    }
143
144    if(disp)
145        debug("freopen(\"%s\", \"%s\", [%i]) = [%i]", 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)) \
179            return ORIG(fn)(stream, offset, whence); \
180        _zz_disabled = 1; \
181        ret = ORIG(fn)(stream, offset, whence); \
182        _zz_disabled = 0; \
183        debug(STR(fn)"([%i], %lli, %i) = %i", \
184              fd, (long long int)offset, whence, ret); \
185        FSEEK_FUZZ(fn2) \
186    } while(0)
187
188int fseek(FILE *stream, long offset, int whence)
189{
190    int ret; FSEEK(fseek, ftell); return ret;
191}
192
193#ifdef HAVE_FSEEKO
194int fseeko(FILE *stream, off_t offset, int whence)
195{
196    int ret; FSEEK(fseeko, ftello); return ret;
197}
198#endif
199
200void rewind(FILE *stream)
201{
202    int fd;
203
204    LOADSYM(rewind);
205    fd = fileno(stream);
206    if(!_zz_ready || !_zz_iswatched(fd))
207    {
208        rewind_orig(stream);
209        return;
210    }
211
212    _zz_disabled = 1;
213    rewind_orig(stream);
214    _zz_disabled = 0;
215    debug("rewind([%i])", 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 fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
225{
226    long int pos;
227#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
228#else
229    long int 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))
237        return fread_orig(ptr, size, nmemb, stream);
238
239    pos = ftell(stream);
240    _zz_disabled = 1;
241    ret = fread_orig(ptr, size, nmemb, stream);
242    _zz_disabled = 0;
243    debug("fread(%p, %li, %li, [%i]) = %li",
244          ptr, (long int)size, (long int)nmemb, fd, (long int)ret);
245
246#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
247#else
248    newpos = ftell(stream);
249    /* XXX: the number of bytes read is not ret * size, because
250     * a partial read may have advanced the stream pointer. However,
251     * when reading from a pipe ftell() will return 0, and ret * size
252     * is then better than nothing. */
253    if(newpos <= 0)
254        newpos = ret * size;
255    if(newpos != pos)
256    {
257        _zz_fuzz(fd, ptr, newpos - pos);
258        _zz_setpos(fd, newpos);
259    }
260#endif
261
262    return ret;
263}
264
265#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
266#   define FGETC_FUZZ
267#else
268#   define FGETC_FUZZ \
269        if(ret != EOF) \
270        { \
271            uint8_t ch = ret; \
272            _zz_fuzz(fd, &ch, 1); \
273            _zz_addpos(fd, 1); \
274            ret = ch; \
275        }
276#endif
277
278#define FGETC(fn) \
279    do { \
280        int fd; \
281        LOADSYM(fn); \
282        fd = fileno(stream); \
283        if(!_zz_ready || !_zz_iswatched(fd)) \
284            return ORIG(fn)(stream); \
285        _zz_disabled = 1; \
286        ret = ORIG(fn)(stream); \
287        _zz_disabled = 0; \
288        FGETC_FUZZ \
289        if(ret >= 0x20 && ret <= 0x7f) \
290            debug(STR(fn)"([%i]) = 0x%02x '%c'", fd, ret, (char)ret); \
291        else \
292            debug(STR(fn)"([%i]) = 0x%02x", fd, ret); \
293    } while(0)
294
295#undef getc /* can be a macro; we don’t want that */
296int getc(FILE *stream)
297{
298    int ret; FGETC(getc); return ret;
299}
300
301int fgetc(FILE *stream)
302{
303    int ret; FGETC(fgetc); return ret;
304}
305
306#ifdef HAVE__IO_GETC
307int _IO_getc(FILE *stream)
308{
309    int ret; FGETC(_IO_getc); return ret;
310}
311#endif
312
313char *fgets(char *s, int size, FILE *stream)
314{
315    char *ret = s;
316    int fd;
317
318    LOADSYM(fgets);
319    LOADSYM(fgetc);
320    fd = fileno(stream);
321    if(!_zz_ready || !_zz_iswatched(fd))
322        return fgets_orig(s, size, stream);
323
324#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
325    _zz_disabled = 1;
326    ret = fgets_orig(s, size, stream);
327    _zz_disabled = 0;
328#else
329    if(size <= 0)
330        ret = NULL;
331    else if(size == 1)
332        s[0] = '\0';
333    else
334    {
335        int i;
336
337        for(i = 0; i < size - 1; i++)
338        {
339            int ch;
340
341            _zz_disabled = 1;
342            ch = fgetc_orig(stream);
343            _zz_disabled = 0;
344
345            if(ch == EOF)
346            {
347                s[i] = '\0';
348                if(!i)
349                    ret = NULL;
350                break;
351            }
352            s[i] = (char)(unsigned char)ch;
353            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
354            _zz_addpos(fd, 1);
355            if(s[i] == '\n')
356            {
357                s[i + 1] = '\0';
358                break;
359            }
360        }
361    }
362#endif
363
364    debug("fgets(%p, %i, [%i]) = %p", s, size, fd, ret);
365    return ret;
366}
367
368int ungetc(int c, FILE *stream)
369{
370    unsigned char ch = c;
371    int ret, fd;
372
373    LOADSYM(ungetc);
374    fd = fileno(stream);
375    if(!_zz_ready || !_zz_iswatched(fd))
376        return ungetc_orig(c, stream);
377
378#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
379#else
380    _zz_addpos(fd, -1);
381    _zz_fuzz(fd, &ch, 1);
382#endif
383    _zz_disabled = 1;
384    ret = ungetc_orig((int)ch, stream);
385    _zz_disabled = 0;
386
387    if(ret >= 0)
388        ret = c;
389#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
390#else
391    else
392        _zz_addpos(fd, 1); /* revert what we did */
393#endif
394
395    if(ret >= 0x20 && ret <= 0x7f)
396        debug("ungetc(0x%02x, [%i]) = 0x%02x '%c'", c, fd, ret, ret);
397    else
398        debug("ungetc(0x%02x, [%i]) = 0x%02x", c, fd, ret);
399    return ret;
400}
401
402int fclose(FILE *fp)
403{
404    int ret, fd;
405
406    LOADSYM(fclose);
407    fd = fileno(fp);
408    if(!_zz_ready || !_zz_iswatched(fd))
409        return fclose_orig(fp);
410
411    _zz_disabled = 1;
412    ret = fclose_orig(fp);
413    _zz_disabled = 0;
414    debug("fclose([%i]) = %i", fd, ret);
415    _zz_unregister(fd);
416
417    return ret;
418}
419
420#define GETDELIM(fn, delim, need_delim) \
421    do { \
422        char *line; \
423        ssize_t done, size; \
424        int fd, finished = 0; \
425        LOADSYM(fn); \
426        LOADSYM(getdelim); \
427        LOADSYM(fgetc); \
428        fd = fileno(stream); \
429        if(!_zz_ready || !_zz_iswatched(fd)) \
430            return getdelim_orig(lineptr, n, delim, stream); \
431        line = *lineptr; \
432        size = line ? *n : 0; \
433        ret = done = finished = 0; \
434        for(;;) \
435        { \
436            int ch; \
437            if(done >= size) /* highly inefficient but I don't care */ \
438                line = realloc(line, size = done + 1); \
439            if(finished) \
440            { \
441                line[done] = '\0'; \
442                *n = size; \
443                *lineptr = line; \
444                break; \
445            } \
446            _zz_disabled = 1; \
447            ch = fgetc_orig(stream); \
448            _zz_disabled = 0; \
449            if(ch == EOF) \
450            { \
451                finished = 1; \
452                ret = done; \
453            } \
454            else \
455            { \
456                unsigned char c = ch; \
457                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
458                line[done++] = c; \
459                _zz_addpos(fd, 1); \
460                if(c == delim) \
461                { \
462                    finished = 1; \
463                    ret = done; \
464                } \
465            } \
466        } \
467        if(need_delim) \
468            debug(STR(fn) "(%p, %p, 0x%02x, [%i]) = %li", \
469                  lineptr, n, delim, fd, (long int)ret); \
470        else \
471            debug(STR(fn) "(%p, %p, [%i]) = %li", \
472                  lineptr, n, fd, (long int)ret); \
473        return ret; \
474    } while(0)
475
476#ifdef HAVE_GETLINE
477ssize_t getline(char **lineptr, size_t *n, FILE *stream)
478{
479    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
480}
481#endif
482
483#ifdef HAVE_GETDELIM
484ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
485{
486    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
487}
488#endif
489
490#ifdef HAVE___GETDELIM
491ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
492{
493    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
494}
495#endif
496
497#ifdef HAVE_FGETLN
498char *fgetln(FILE *stream, size_t *len)
499{
500    char *ret;
501#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
502#else
503    struct fuzz *fuzz;
504    size_t i, size;
505#endif
506    int fd;
507
508    LOADSYM(fgetln);
509    LOADSYM(fgetc);
510    fd = fileno(stream);
511    if(!_zz_ready || !_zz_iswatched(fd))
512        return fgetln_orig(stream, len);
513
514#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
515    _zz_disabled = 1;
516    ret = fgetln_orig(stream, len);
517    _zz_disabled = 0;
518#else
519    fuzz = _zz_getfuzz(fd);
520
521    for(i = size = 0; ; /* i is incremented below */)
522    {
523        int ch;
524
525        _zz_disabled = 1;
526        ch = fgetc_orig(stream);
527        _zz_disabled = 0;
528
529        if(ch == EOF)
530            break;
531
532        if(i >= size)
533            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
534
535        fuzz->tmp[i] = (char)(unsigned char)ch;
536        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
537        _zz_addpos(fd, 1);
538
539        if(fuzz->tmp[i++] == '\n')
540            break;
541    }
542
543    *len = i;
544    ret = fuzz->tmp;
545#endif
546
547    debug("fgetln([%i], &%li) = %p", fd, (long int)*len, ret);
548    return ret;
549}
550#endif
551
552#ifdef HAVE___SREFILL
553int __srefill(FILE *fp)
554{
555    off_t newpos;
556    int ret, fd, tmp;
557
558    LOADSYM(__srefill);
559    fd = fileno(fp);
560    if(!_zz_ready || !_zz_iswatched(fd))
561        return __srefill_orig(fp);
562
563    tmp = _zz_disabled;
564    _zz_disabled = 1;
565    ret = __srefill_orig(fp);
566    newpos = lseek(fd, 0, SEEK_CUR);
567    _zz_disabled = tmp;
568    if(ret != EOF)
569    {
570        if(newpos != -1)
571            _zz_setpos(fd, newpos - fp->_r);
572        _zz_fuzz(fd, fp->_p, fp->_r);
573        _zz_addpos(fd, fp->_r);
574    }
575
576    if(!_zz_disabled)
577        debug("__srefill([%i]) = %i", fd, ret);
578
579    return ret;
580}
581#endif
582
Note: See TracBrowser for help on using the repository browser.