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

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