source: zzuf/trunk/src/lib-stream.c @ 1687

Last change on this file since 1687 was 1687, checked in by Sam Hocevar, 14 years ago
  • Use func wherever possible.
  • Property svn:keywords set to Id
File size: 14.1 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: lib-stream.c 1687 2007-01-17 15:21:09Z 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 "lib-load.h"
39#include "debug.h"
40#include "fuzz.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("%s(\"%s\", \"%s\") = [%i]", __func__, 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("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__,
146              path, mode, fd0, fd1);
147
148    return ret;
149}
150
151#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
152#   define FSEEK_FUZZ(fn2)
153#else
154#   define FSEEK_FUZZ(fn2) \
155        if(ret == 0) \
156        { \
157            /* FIXME: check what happens when fseek()ing a pipe */ \
158            switch(whence) \
159            { \
160                case SEEK_END: \
161                    offset = fn2(stream); \
162                    /* fall through */ \
163                case SEEK_SET: \
164                    _zz_setpos(fd, offset); \
165                    break; \
166                case SEEK_CUR: \
167                    _zz_addpos(fd, offset); \
168                    break; \
169            } \
170        }
171#endif
172
173#define FSEEK(fn, fn2) \
174    do \
175    { \
176        int fd; \
177        LOADSYM(fn); \
178        fd = fileno(stream); \
179        if(!_zz_ready || !_zz_iswatched(fd)) \
180            return ORIG(fn)(stream, offset, whence); \
181        _zz_disabled = 1; \
182        ret = ORIG(fn)(stream, offset, whence); \
183        _zz_disabled = 0; \
184        debug("%s([%i], %lli, %i) = %i", __func__, \
185              fd, (long long int)offset, whence, ret); \
186        FSEEK_FUZZ(fn2) \
187    } while(0)
188
189int fseek(FILE *stream, long offset, int whence)
190{
191    int ret; FSEEK(fseek, ftell); return ret;
192}
193
194#ifdef HAVE_FSEEKO
195int fseeko(FILE *stream, off_t offset, int whence)
196{
197    int ret; FSEEK(fseeko, ftello); return ret;
198}
199#endif
200
201void rewind(FILE *stream)
202{
203    int fd;
204
205    LOADSYM(rewind);
206    fd = fileno(stream);
207    if(!_zz_ready || !_zz_iswatched(fd))
208    {
209        rewind_orig(stream);
210        return;
211    }
212
213    _zz_disabled = 1;
214    rewind_orig(stream);
215    _zz_disabled = 0;
216    debug("%s([%i])", __func__, fd);
217
218#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
219#else
220    /* FIXME: check what happens when rewind()ing a pipe */
221    _zz_setpos(fd, 0);
222#endif
223}
224
225size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
226{
227    long int pos;
228#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
229#else
230    long int newpos;
231#endif
232    size_t ret;
233    int fd;
234
235    LOADSYM(fread);
236    fd = fileno(stream);
237    if(!_zz_ready || !_zz_iswatched(fd))
238        return fread_orig(ptr, size, nmemb, stream);
239
240    pos = ftell(stream);
241    _zz_disabled = 1;
242    ret = fread_orig(ptr, size, nmemb, stream);
243    _zz_disabled = 0;
244    debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr,
245          (long int)size, (long int)nmemb, fd, (long int)ret);
246
247#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
248#else
249    newpos = ftell(stream);
250    /* XXX: the number of bytes read is not ret * size, because
251     * a partial read may have advanced the stream pointer. However,
252     * when reading from a pipe ftell() will return 0, and ret * size
253     * is then better than nothing. */
254    if(newpos <= 0)
255        newpos = ret * size;
256    if(newpos != pos)
257    {
258        _zz_fuzz(fd, ptr, newpos - pos);
259        _zz_setpos(fd, newpos);
260    }
261#endif
262
263    return ret;
264}
265
266#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
267#   define FGETC_FUZZ
268#else
269#   define FGETC_FUZZ \
270        if(ret != EOF) \
271        { \
272            uint8_t ch = ret; \
273            _zz_fuzz(fd, &ch, 1); \
274            _zz_addpos(fd, 1); \
275            ret = ch; \
276        }
277#endif
278
279#define FGETC(fn) \
280    do { \
281        int fd; \
282        LOADSYM(fn); \
283        fd = fileno(stream); \
284        if(!_zz_ready || !_zz_iswatched(fd)) \
285            return ORIG(fn)(stream); \
286        _zz_disabled = 1; \
287        ret = ORIG(fn)(stream); \
288        _zz_disabled = 0; \
289        FGETC_FUZZ \
290        if(ret >= 0x20 && ret <= 0x7f) \
291            debug("%s([%i]) = 0x%02x '%c'", __func__, fd, ret, (char)ret); \
292        else \
293            debug("%s([%i]) = 0x%02x", __func__, fd, ret); \
294    } while(0)
295
296#undef getc /* can be a macro; we don’t want that */
297int getc(FILE *stream)
298{
299    int ret; FGETC(getc); return ret;
300}
301
302int fgetc(FILE *stream)
303{
304    int ret; FGETC(fgetc); return ret;
305}
306
307#ifdef HAVE__IO_GETC
308int _IO_getc(FILE *stream)
309{
310    int ret; FGETC(_IO_getc); return ret;
311}
312#endif
313
314char *fgets(char *s, int size, FILE *stream)
315{
316    char *ret = s;
317    int fd;
318
319    LOADSYM(fgets);
320    LOADSYM(fgetc);
321    fd = fileno(stream);
322    if(!_zz_ready || !_zz_iswatched(fd))
323        return fgets_orig(s, size, stream);
324
325#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
326    _zz_disabled = 1;
327    ret = fgets_orig(s, size, stream);
328    _zz_disabled = 0;
329#else
330    if(size <= 0)
331        ret = NULL;
332    else if(size == 1)
333        s[0] = '\0';
334    else
335    {
336        int i;
337
338        for(i = 0; i < size - 1; i++)
339        {
340            int ch;
341
342            _zz_disabled = 1;
343            ch = fgetc_orig(stream);
344            _zz_disabled = 0;
345
346            if(ch == EOF)
347            {
348                s[i] = '\0';
349                if(!i)
350                    ret = NULL;
351                break;
352            }
353            s[i] = (char)(unsigned char)ch;
354            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
355            _zz_addpos(fd, 1);
356            if(s[i] == '\n')
357            {
358                s[i + 1] = '\0';
359                break;
360            }
361        }
362    }
363#endif
364
365    debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret);
366    return ret;
367}
368
369int ungetc(int c, FILE *stream)
370{
371    unsigned char ch = c;
372    int ret, fd;
373
374    LOADSYM(ungetc);
375    fd = fileno(stream);
376    if(!_zz_ready || !_zz_iswatched(fd))
377        return ungetc_orig(c, stream);
378
379#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
380#else
381    _zz_addpos(fd, -1);
382    _zz_fuzz(fd, &ch, 1);
383#endif
384    _zz_disabled = 1;
385    ret = ungetc_orig((int)ch, stream);
386    _zz_disabled = 0;
387
388    if(ret >= 0)
389        ret = c;
390#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
391#else
392    else
393        _zz_addpos(fd, 1); /* revert what we did */
394#endif
395
396    if(ret >= 0x20 && ret <= 0x7f)
397        debug("%s(0x%02x, [%i]) = 0x%02x '%c'", __func__, c, fd, ret, ret);
398    else
399        debug("%s(0x%02x, [%i]) = 0x%02x", __func__, c, fd, ret);
400    return ret;
401}
402
403int fclose(FILE *fp)
404{
405    int ret, fd;
406
407    LOADSYM(fclose);
408    fd = fileno(fp);
409    if(!_zz_ready || !_zz_iswatched(fd))
410        return fclose_orig(fp);
411
412    _zz_disabled = 1;
413    ret = fclose_orig(fp);
414    _zz_disabled = 0;
415    debug("%s([%i]) = %i", __func__, fd, ret);
416    _zz_unregister(fd);
417
418    return ret;
419}
420
421#define GETDELIM(fn, delim, need_delim) \
422    do { \
423        char *line; \
424        ssize_t done, size; \
425        int fd, finished = 0; \
426        LOADSYM(fn); \
427        LOADSYM(getdelim); \
428        LOADSYM(fgetc); \
429        fd = fileno(stream); \
430        if(!_zz_ready || !_zz_iswatched(fd)) \
431            return getdelim_orig(lineptr, n, delim, stream); \
432        line = *lineptr; \
433        size = line ? *n : 0; \
434        ret = done = finished = 0; \
435        for(;;) \
436        { \
437            int ch; \
438            if(done >= size) /* highly inefficient but I don't care */ \
439                line = realloc(line, size = done + 1); \
440            if(finished) \
441            { \
442                line[done] = '\0'; \
443                *n = size; \
444                *lineptr = line; \
445                break; \
446            } \
447            _zz_disabled = 1; \
448            ch = fgetc_orig(stream); \
449            _zz_disabled = 0; \
450            if(ch == EOF) \
451            { \
452                finished = 1; \
453                ret = done; \
454            } \
455            else \
456            { \
457                unsigned char c = ch; \
458                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
459                line[done++] = c; \
460                _zz_addpos(fd, 1); \
461                if(c == delim) \
462                { \
463                    finished = 1; \
464                    ret = done; \
465                } \
466            } \
467        } \
468        if(need_delim) \
469            debug("%s(%p, %p, 0x%02x, [%i]) = %li", __func__, \
470                  lineptr, n, delim, fd, (long int)ret); \
471        else \
472            debug("%s(%p, %p, [%i]) = %li", __func__, \
473                  lineptr, n, fd, (long int)ret); \
474        return ret; \
475    } while(0)
476
477#ifdef HAVE_GETLINE
478ssize_t getline(char **lineptr, size_t *n, FILE *stream)
479{
480    ssize_t ret; GETDELIM(getline, '\n', 0); 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___GETDELIM
492ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
493{
494    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
495}
496#endif
497
498#ifdef HAVE_FGETLN
499char *fgetln(FILE *stream, size_t *len)
500{
501    char *ret;
502#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
503#else
504    struct fuzz *fuzz;
505    size_t i, size;
506#endif
507    int fd;
508
509    LOADSYM(fgetln);
510    LOADSYM(fgetc);
511    fd = fileno(stream);
512    if(!_zz_ready || !_zz_iswatched(fd))
513        return fgetln_orig(stream, len);
514
515#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
516    _zz_disabled = 1;
517    ret = fgetln_orig(stream, len);
518    _zz_disabled = 0;
519#else
520    fuzz = _zz_getfuzz(fd);
521
522    for(i = size = 0; ; /* i is incremented below */)
523    {
524        int ch;
525
526        _zz_disabled = 1;
527        ch = fgetc_orig(stream);
528        _zz_disabled = 0;
529
530        if(ch == EOF)
531            break;
532
533        if(i >= size)
534            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
535
536        fuzz->tmp[i] = (char)(unsigned char)ch;
537        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
538        _zz_addpos(fd, 1);
539
540        if(fuzz->tmp[i++] == '\n')
541            break;
542    }
543
544    *len = i;
545    ret = fuzz->tmp;
546#endif
547
548    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
549    return ret;
550}
551#endif
552
553#ifdef HAVE___SREFILL
554int __srefill(FILE *fp)
555{
556    off_t newpos;
557    int ret, fd, tmp;
558
559    LOADSYM(__srefill);
560    fd = fileno(fp);
561    if(!_zz_ready || !_zz_iswatched(fd))
562        return __srefill_orig(fp);
563
564    tmp = _zz_disabled;
565    _zz_disabled = 1;
566    ret = __srefill_orig(fp);
567    newpos = lseek(fd, 0, SEEK_CUR);
568    _zz_disabled = tmp;
569    if(ret != EOF)
570    {
571        if(newpos != -1)
572            _zz_setpos(fd, newpos - fp->_r);
573        _zz_fuzz(fd, fp->_p, fp->_r);
574        _zz_addpos(fd, fp->_r);
575    }
576
577    if(!_zz_disabled)
578        debug("%s([%i]) = %i", __func__, fd, ret);
579
580    return ret;
581}
582#endif
583
Note: See TracBrowser for help on using the repository browser.