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

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