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

Last change on this file since 1595 was 1595, checked in by Sam Hocevar, 14 years ago
  • Always define getc(), but undefine the getc macro beforehands just in case.
  • Implemented _IO_getc().
  • Property svn:keywords set to Id
File size: 11.9 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 1595 2007-01-07 10:34:45Z 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/* Library functions that we divert */
40static FILE *  (*fopen_orig)    (const char *path, const char *mode);
41#ifdef HAVE_FOPEN64
42static FILE *  (*fopen64_orig)  (const char *path, const char *mode);
43#endif
44static int     (*fseek_orig)    (FILE *stream, long offset, int whence);
45#ifdef HAVE_FSEEKO
46static int     (*fseeko_orig)   (FILE *stream, off_t offset, int whence);
47#endif
48static void    (*rewind_orig)   (FILE *stream);
49static size_t  (*fread_orig)    (void *ptr, size_t size, size_t nmemb,
50                                 FILE *stream);
51static int     (*getc_orig)     (FILE *stream);
52static int     (*fgetc_orig)    (FILE *stream);
53static int     (*_IO_getc_orig) (FILE *stream);
54static char *  (*fgets_orig)    (char *s, int size, FILE *stream);
55static int     (*ungetc_orig)   (int c, FILE *stream);
56static int     (*fclose_orig)   (FILE *fp);
57
58/* Additional GNUisms */
59#ifdef HAVE_GETLINE
60static ssize_t (*getline_orig)    (char **lineptr, size_t *n, FILE *stream);
61#endif
62#ifdef HAVE_GETDELIM
63static ssize_t (*getdelim_orig)   (char **lineptr, size_t *n, int delim,
64                                   FILE *stream);
65#endif
66#ifdef HAVE___GETDELIM
67static ssize_t (*__getdelim_orig) (char **lineptr, size_t *n, int delim,
68                                   FILE *stream);
69#endif
70
71/* Additional BSDisms */
72#ifdef HAVE_FGETLN
73static char *  (*fgetln_orig) (FILE *stream, size_t *len);
74#endif
75
76void _zz_load_stream(void)
77{
78    LOADSYM(fopen);
79#ifdef HAVE_FOPEN64
80    LOADSYM(fopen64);
81#endif
82    LOADSYM(fseek);
83#ifdef HAVE_FSEEKO
84    LOADSYM(fseeko);
85#endif
86    LOADSYM(rewind);
87    LOADSYM(fread);
88    LOADSYM(getc);
89    LOADSYM(fgetc);
90#ifdef HAVE__IO_GETC
91    LOADSYM(_IO_getc);
92#endif
93    LOADSYM(fgets);
94    LOADSYM(ungetc);
95    LOADSYM(fclose);
96#ifdef HAVE_GETLINE
97    LOADSYM(getline);
98#endif
99#ifdef HAVE_GETDELIM
100    LOADSYM(getdelim);
101#endif
102#ifdef HAVE___GETDELIM
103    LOADSYM(__getdelim);
104#endif
105#ifdef HAVE_FGETLN
106    LOADSYM(fgetln);
107#endif
108}
109
110/* Our function wrappers */
111#define FOPEN(fn) \
112    do \
113    { \
114        if(!_zz_ready) \
115        { \
116            LOADSYM(fn); \
117            return ORIG(fn)(path, mode); \
118        } \
119        _zz_disabled = 1; \
120        ret = ORIG(fn)(path, mode); \
121        _zz_disabled = 0; \
122        if(ret && _zz_mustwatch(path)) \
123        { \
124            int fd = fileno(ret); \
125            _zz_register(fd); \
126            debug(STR(fn) "(\"%s\", \"%s\") = [%i]", path, mode, fd); \
127        } \
128    } while(0)
129
130FILE *fopen(const char *path, const char *mode)
131{
132    FILE *ret; FOPEN(fopen); return ret;
133}
134
135#ifdef HAVE_FOPEN64
136FILE *fopen64(const char *path, const char *mode)
137{
138    FILE *ret; FOPEN(fopen64); return ret;
139}
140#endif
141
142#define FSEEK(fn, fn2) \
143    do \
144    { \
145        int fd; \
146        if(!_zz_ready) \
147            LOADSYM(fn); \
148        fd = fileno(stream); \
149        if(!_zz_ready || !_zz_iswatched(fd)) \
150            return ORIG(fn)(stream, offset, whence); \
151        _zz_disabled = 1; \
152        ret = ORIG(fn)(stream, offset, whence); \
153        _zz_disabled = 0; \
154        debug(STR(fn)"([%i], %lli, %i) = %i", \
155              fd, (long long int)offset, whence, ret); \
156        if(ret == 0) \
157        { \
158            /* FIXME: check what happens when fseek()ing a pipe */ \
159            switch(whence) \
160            { \
161                case SEEK_END: \
162                    offset = fn2(stream); \
163                    /* fall through */ \
164                case SEEK_SET: \
165                    _zz_setpos(fd, offset); \
166                    break; \
167                case SEEK_CUR: \
168                    _zz_addpos(fd, offset); \
169                    break; \
170            } \
171        } \
172    } while(0)
173
174int fseek(FILE *stream, long offset, int whence)
175{
176    int ret; FSEEK(fseek, ftell); return ret;
177}
178
179#ifdef HAVE_FSEEKO
180int fseeko(FILE *stream, off_t offset, int whence)
181{
182    int ret; FSEEK(fseeko, ftello); return ret;
183}
184#endif
185
186void rewind(FILE *stream)
187{
188    int fd;
189
190    if(!_zz_ready)
191        LOADSYM(rewind);
192    fd = fileno(stream);
193    if(!_zz_ready || !_zz_iswatched(fd))
194    {
195        rewind_orig(stream);
196        return;
197    }
198
199    _zz_disabled = 1;
200    rewind_orig(stream);
201    _zz_disabled = 0;
202    debug("rewind([%i])", fd);
203
204    /* FIXME: check what happens when rewind()ing a pipe */
205    _zz_setpos(fd, 0);
206}
207
208size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
209{
210    long int pos, newpos;
211    size_t ret;
212    int fd;
213
214    if(!_zz_ready)
215        LOADSYM(fread);
216    fd = fileno(stream);
217    if(!_zz_ready || !_zz_iswatched(fd))
218        return fread_orig(ptr, size, nmemb, stream);
219
220    pos = ftell(stream);
221    _zz_disabled = 1;
222    ret = fread_orig(ptr, size, nmemb, stream);
223    _zz_disabled = 0;
224    debug("fread(%p, %li, %li, [%i]) = %li",
225          ptr, (long int)size, (long int)nmemb, fd, (long int)ret);
226
227    /* XXX: the number of bytes read is not ret * size, because
228     * a partial read may have advanced the stream pointer. However,
229     * when reading from a pipe ftell() will return 0, and ret * size
230     * is then better than nothing. */
231    newpos = ftell(stream);
232    if(newpos <= 0)
233        newpos = ret * size;
234    if(newpos != pos)
235    {
236        _zz_fuzz(fd, ptr, newpos - pos);
237        _zz_setpos(fd, newpos);
238    }
239
240    return ret;
241}
242
243#define FGETC(fn) \
244    do { \
245        int fd; \
246        if(!_zz_ready) \
247            LOADSYM(fn); \
248        fd = fileno(stream); \
249        if(!_zz_ready || !_zz_iswatched(fd)) \
250            return ORIG(fn)(stream); \
251        _zz_disabled = 1; \
252        ret = ORIG(fn)(stream); \
253        _zz_disabled = 0; \
254        if(ret != EOF) \
255        { \
256            uint8_t ch = ret; \
257            _zz_fuzz(fd, &ch, 1); \
258            _zz_addpos(fd, 1); \
259            ret = ch; \
260        } \
261        if(ret >= 0x20 && ret <= 0x7f) \
262            debug(STR(fn)"([%i]) = 0x%02x '%c'", fd, ret, (char)ret); \
263        else \
264            debug(STR(fn)"([%i]) = 0x%02x", fd, ret); \
265    } while(0)
266
267#undef getc /* can be a macro; we don’t want that */
268int getc(FILE *stream)
269{
270    int ret; FGETC(getc); return ret;
271}
272
273int fgetc(FILE *stream)
274{
275    int ret; FGETC(fgetc); return ret;
276}
277
278#ifdef HAVE__IO_GETC
279int _IO_getc(FILE *stream)
280{
281    int ret; FGETC(_IO_getc); return ret;
282}
283#endif
284
285char *fgets(char *s, int size, FILE *stream)
286{
287    char *ret = s;
288    int i, fd;
289
290    if(!_zz_ready)
291        LOADSYM(fgets);
292    fd = fileno(stream);
293    if(!_zz_ready || !_zz_iswatched(fd))
294        return fgets_orig(s, size, stream);
295
296    if(size <= 0)
297        ret = NULL;
298    else if(size == 1)
299        s[0] = '\0';
300    else
301    {
302        for(i = 0; i < size - 1; i++)
303        {
304            int ch;
305
306            _zz_disabled = 1;
307            ch = fgetc_orig(stream);
308            _zz_disabled = 0;
309
310            if(ch == EOF)
311            {
312                s[i] = '\0';
313                if(!i)
314                    ret = NULL;
315                break;
316            }
317            s[i] = (char)(unsigned char)ch;
318            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
319            _zz_addpos(fd, 1);
320            if(s[i] == '\n')
321            {
322                s[i + 1] = '\0';
323                break;
324            }
325        }
326    }
327
328    debug("fgets(%p, %i, [%i]) = %p", s, size, fd, ret);
329    return ret;
330}
331
332int ungetc(int c, FILE *stream)
333{
334    unsigned char ch = c;
335    int ret, fd;
336
337    if(!_zz_ready)
338        LOADSYM(ungetc);
339    fd = fileno(stream);
340    if(!_zz_ready || !_zz_iswatched(fd))
341        return ungetc_orig(c, stream);
342
343    _zz_addpos(fd, -1);
344    _zz_fuzz(fd, &ch, 1);
345    ret = ungetc_orig((int)ch, stream);
346    if(ret >= 0)
347        ret = c;
348    else
349        _zz_addpos(fd, 1); /* revert what we did */
350    if(ret >= 0x20 && ret <= 0x7f)
351        debug("ungetc(0x%02x, [%i]) = 0x%02x '%c'", c, fd, ret, ret);
352    else
353        debug("ungetc(0x%02x, [%i]) = 0x%02x", c, fd, ret);
354    return ret;
355}
356
357int fclose(FILE *fp)
358{
359    int ret, fd;
360
361    if(!_zz_ready)
362        LOADSYM(fclose);
363    fd = fileno(fp);
364    if(!_zz_ready || !_zz_iswatched(fd))
365        return fclose_orig(fp);
366
367    _zz_disabled = 1;
368    ret = fclose_orig(fp);
369    _zz_disabled = 0;
370    debug("fclose([%i]) = %i", fd, ret);
371    _zz_unregister(fd);
372
373    return ret;
374}
375
376#define GETDELIM(fn, delim, need_delim) \
377    do { \
378        char *line; \
379        ssize_t done, size; \
380        int fd, finished = 0; \
381        if(!_zz_ready) \
382            LOADSYM(fn); \
383        fd = fileno(stream); \
384        if(!_zz_ready || !_zz_iswatched(fd)) \
385            return getdelim_orig(lineptr, n, delim, stream); \
386        line = *lineptr; \
387        size = line ? *n : 0; \
388        ret = done = finished = 0; \
389        for(;;) \
390        { \
391            int ch; \
392            if(done >= size) /* highly inefficient but I don't care */ \
393                line = realloc(line, size = done + 1); \
394            if(finished) \
395            { \
396                line[done] = '\0'; \
397                *n = size; \
398                *lineptr = line; \
399                break; \
400            } \
401            _zz_disabled = 1; \
402            ch = fgetc_orig(stream); \
403            _zz_disabled = 0; \
404            if(ch == EOF) \
405            { \
406                finished = 1; \
407                ret = done; \
408            } \
409            else \
410            { \
411                unsigned char c = ch; \
412                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
413                line[done++] = c; \
414                _zz_addpos(fd, 1); \
415                if(c == delim) \
416                { \
417                    finished = 1; \
418                    ret = done; \
419                } \
420            } \
421        } \
422        if(need_delim) \
423            debug(STR(fn) "(%p, %p, 0x%02x, [%i]) = %li", \
424                  lineptr, n, delim, fd, (long int)ret); \
425        else \
426            debug(STR(fn) "(%p, %p, [%i]) = %li", \
427                  lineptr, n, fd, (long int)ret); \
428        return ret; \
429    } while(0)
430
431#ifdef HAVE_GETLINE
432ssize_t getline(char **lineptr, size_t *n, FILE *stream)
433{
434    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
435}
436#endif
437
438#ifdef HAVE_GETDELIM
439ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
440{
441    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
442}
443#endif
444
445#ifdef HAVE___GETDELIM
446ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
447{
448    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
449}
450#endif
451
452#ifdef HAVE_FGETLN
453char *fgetln(FILE *stream, size_t *len)
454{
455    struct fuzz *fuzz;
456    size_t i, size;
457    int fd;
458
459    if(!_zz_ready)
460        LOADSYM(fgetln);
461    fd = fileno(stream);
462    if(!_zz_ready || !_zz_iswatched(fd))
463        return fgetln_orig(stream, len);
464
465    fuzz = _zz_getfuzz(fd);
466
467    for(i = size = 0; ; /* i is incremented below */)
468    {
469        int ch;
470
471        _zz_disabled = 1;
472        ch = fgetc_orig(stream);
473        _zz_disabled = 0;
474
475        if(ch == EOF)
476            break;
477
478        if(i >= size)
479            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
480
481        fuzz->tmp[i] = (char)(unsigned char)ch;
482        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
483        _zz_addpos(fd, 1);
484
485        if(fuzz->tmp[i++] == '\n')
486            break;
487    }
488
489    *len = i;
490
491    debug("fgetln([%i], &%li) = %p", fd, (long int)*len, fuzz->tmp);
492    return fuzz->tmp;
493}
494#endif
495
Note: See TracBrowser for help on using the repository browser.