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

Last change on this file since 2334 was 2334, checked in by sam, 6 years ago
  • Implement getchar() and getchar_unlocked().
  • Property svn:keywords set to Id
File size: 17.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$
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
30#include <stdio.h>
31#include <sys/types.h>
32#if defined HAVE___SREFILL && defined HAVE_UNISTD_H
33#   include <unistd.h> /* Needed for __srefill’s lseek() call */
34#endif
35
36#include "libzzuf.h"
37#include "lib-load.h"
38#include "debug.h"
39#include "fuzz.h"
40#include "fd.h"
41
42#if defined HAVE___SREFILL
43int NEW(__srefill)(FILE *fp);
44#endif
45
46/* Library functions that we divert */
47static FILE *  (*ORIG(fopen))    (const char *path, const char *mode);
48#if defined HAVE_FOPEN64
49static FILE *  (*ORIG(fopen64))  (const char *path, const char *mode);
50#endif
51static FILE *  (*ORIG(freopen))  (const char *path, const char *mode,
52                                  FILE *stream);
53static int     (*ORIG(fseek))    (FILE *stream, long offset, int whence);
54#if defined HAVE_FSEEKO
55static int     (*ORIG(fseeko))   (FILE *stream, off_t offset, int whence);
56#endif
57static void    (*ORIG(rewind))   (FILE *stream);
58static size_t  (*ORIG(fread))    (void *ptr, size_t size, size_t nmemb,
59                                  FILE *stream);
60#if defined HAVE_FREAD_UNLOCKED
61static size_t  (*ORIG(fread_unlocked))  (void *ptr, size_t size, size_t nmemb,
62                                         FILE *stream);
63#endif
64static int     (*ORIG(getc))     (FILE *stream);
65static int     (*ORIG(getchar))  (void);
66static int     (*ORIG(fgetc))    (FILE *stream);
67#if defined HAVE__IO_GETC
68static int     (*ORIG(_IO_getc)) (FILE *stream);
69#endif
70#if defined HAVE_GETC_UNLOCKED
71static int     (*ORIG(getc_unlocked))    (FILE *stream);
72#endif
73#if defined HAVE_GETCHAR_UNLOCKED
74static int     (*ORIG(getchar_unlocked)) (void);
75#endif
76#if defined HAVE_FGETC_UNLOCKED
77static int     (*ORIG(fgetc_unlocked))   (FILE *stream);
78#endif
79static char *  (*ORIG(fgets))    (char *s, int size, FILE *stream);
80#if defined HAVE_FGETS_UNLOCKED
81static char *  (*ORIG(fgets_unlocked))   (char *s, int size, FILE *stream);
82#endif
83static int     (*ORIG(ungetc))   (int c, FILE *stream);
84static int     (*ORIG(fclose))   (FILE *fp);
85
86/* Additional GNUisms */
87#if defined HAVE_GETLINE
88static ssize_t (*ORIG(getline))    (char **lineptr, size_t *n, FILE *stream);
89#endif
90#if defined HAVE_GETDELIM
91static ssize_t (*ORIG(getdelim))   (char **lineptr, size_t *n, int delim,
92                                    FILE *stream);
93#endif
94#if defined HAVE___GETDELIM
95static ssize_t (*ORIG(__getdelim)) (char **lineptr, size_t *n, int delim,
96                                    FILE *stream);
97#endif
98
99/* Additional BSDisms */
100#if defined HAVE_FGETLN
101static char *  (*ORIG(fgetln))    (FILE *stream, size_t *len);
102#endif
103#if defined HAVE___SREFILL
104int            (*ORIG(__srefill)) (FILE *fp);
105#endif
106
107/* Our function wrappers */
108#define FOPEN(fn) \
109    do \
110    { \
111        LOADSYM(fn); \
112        if(!_zz_ready) \
113            return ORIG(fn)(path, mode); \
114        _zz_lock(-1); \
115        ret = ORIG(fn)(path, mode); \
116        _zz_unlock(-1); \
117        if(ret && _zz_mustwatch(path)) \
118        { \
119            int fd = fileno(ret); \
120            _zz_register(fd); \
121            debug("%s(\"%s\", \"%s\") = [%i]", __func__, path, mode, fd); \
122        } \
123    } while(0)
124
125FILE *NEW(fopen)(const char *path, const char *mode)
126{
127    FILE *ret; FOPEN(fopen); return ret;
128}
129
130#if defined HAVE_FOPEN64
131FILE *NEW(fopen64)(const char *path, const char *mode)
132{
133    FILE *ret; FOPEN(fopen64); return ret;
134}
135#endif
136
137FILE *NEW(freopen)(const char *path, const char *mode, FILE *stream)
138{
139    FILE *ret;
140    int fd0 = -1, fd1 = -1, disp = 0;
141
142    LOADSYM(freopen);
143    if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0))
144    {
145        _zz_unregister(fd0);
146        disp = 1;
147    }
148
149    _zz_lock(-1);
150    ret = ORIG(freopen)(path, mode, stream);
151    _zz_unlock(-1);
152
153    if(ret && _zz_mustwatch(path))
154    {
155        fd1 = fileno(ret);
156        _zz_register(fd1);
157        disp = 1;
158    }
159
160    if(disp)
161        debug("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__,
162              path, mode, fd0, fd1);
163
164    return ret;
165}
166
167#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
168#   define FSEEK_FUZZ(fn2)
169#else
170#   define FSEEK_FUZZ(fn2) \
171        if(ret == 0) \
172        { \
173            /* FIXME: check what happens when fseek()ing a pipe */ \
174            switch(whence) \
175            { \
176                case SEEK_END: \
177                    offset = fn2(stream); \
178                    /* fall through */ \
179                case SEEK_SET: \
180                    _zz_setpos(fd, offset); \
181                    break; \
182                case SEEK_CUR: \
183                    _zz_addpos(fd, offset); \
184                    break; \
185            } \
186        }
187#endif
188
189#define FSEEK(fn, fn2) \
190    do \
191    { \
192        int fd; \
193        LOADSYM(fn); \
194        fd = fileno(stream); \
195        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
196            return ORIG(fn)(stream, offset, whence); \
197        _zz_lock(fd); \
198        ret = ORIG(fn)(stream, offset, whence); \
199        _zz_unlock(fd); \
200        debug("%s([%i], %lli, %i) = %i", __func__, \
201              fd, (long long int)offset, whence, ret); \
202        FSEEK_FUZZ(fn2) \
203    } while(0)
204
205int NEW(fseek)(FILE *stream, long offset, int whence)
206{
207    int ret; FSEEK(fseek, ftell); return ret;
208}
209
210#if defined HAVE_FSEEKO
211int NEW(fseeko)(FILE *stream, off_t offset, int whence)
212{
213    int ret; FSEEK(fseeko, ftello); return ret;
214}
215#endif
216
217void NEW(rewind)(FILE *stream)
218{
219    int fd;
220
221    LOADSYM(rewind);
222    fd = fileno(stream);
223    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
224    {
225        ORIG(rewind)(stream);
226        return;
227    }
228
229    _zz_lock(fd);
230    ORIG(rewind)(stream);
231    _zz_unlock(fd);
232    debug("%s([%i])", __func__, fd);
233
234#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
235#else
236    /* FIXME: check what happens when rewind()ing a pipe */
237    _zz_setpos(fd, 0);
238#endif
239}
240
241#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
242#   define FREAD_FUZZ() \
243    do \
244    { \
245        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
246              (long int)size, (long int)nmemb, fd, (long int)ret); \
247    } while(0)
248#else
249#   define FREAD_FUZZ() \
250    do \
251    { \
252        int64_t newpos = ftell(stream); \
253        /* XXX: the number of bytes read is not ret * size, because \
254         * a partial read may have advanced the stream pointer. However, \
255         * when reading from a pipe ftell() will return 0, and ret * size \
256         * is then better than nothing. */ \
257        if(newpos <= 0) \
258        { \
259            pos = _zz_getpos(fd); \
260            newpos = pos + ret * size; \
261        } \
262        if(newpos != pos) \
263        { \
264            char *b = ptr; \
265            _zz_fuzz(fd, ptr, newpos - pos); \
266            _zz_setpos(fd, newpos); \
267            if(newpos >= pos + 4) \
268                debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, \
269                      ptr, (long int)size, (long int)nmemb, fd, \
270                      (long int)ret, b[0], b[1], b[2], b[3]); \
271            else \
272                debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr, \
273                      (long int)size, (long int)nmemb, fd, \
274                      (long int)ret, b[0]); \
275        } \
276        else \
277            debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
278                  (long int)size, (long int)nmemb, fd, (long int)ret); \
279    } while(0)
280#endif
281
282#define FREAD(fn) \
283    do \
284    { \
285        int64_t pos; \
286        int fd; \
287        LOADSYM(fn); \
288        fd = fileno(stream); \
289        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
290            return ORIG(fn)(ptr, size, nmemb, stream); \
291        pos = ftell(stream); \
292        _zz_lock(fd); \
293        ret = ORIG(fn)(ptr, size, nmemb, stream); \
294        _zz_unlock(fd); \
295        FREAD_FUZZ(); \
296    } while(0)
297
298size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
299{
300    size_t ret; FREAD(fread); return ret;
301}
302
303#if defined HAVE_FREAD_UNLOCKED
304#undef fread_unlocked /* can be a macro; we don’t want that */
305size_t NEW(fread_unlocked)(void *ptr, size_t size, size_t nmemb, FILE *stream)
306{
307    size_t ret; FREAD(fread_unlocked); return ret;
308}
309#endif
310
311#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
312#   define FGETC_FUZZ
313#else
314#   define FGETC_FUZZ \
315        if(ret != EOF) \
316        { \
317            uint8_t ch = ret; \
318            _zz_fuzz(fd, &ch, 1); \
319            _zz_addpos(fd, 1); \
320            ret = ch; \
321        }
322#endif
323
324#define FGETC(fn, s, arg) \
325    do { \
326        int fd; \
327        LOADSYM(fn); \
328        fd = fileno(s); \
329        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
330            return ORIG(fn)(arg); \
331        _zz_lock(fd); \
332        ret = ORIG(fn)(arg); \
333        _zz_unlock(fd); \
334        FGETC_FUZZ \
335        if(ret == EOF) \
336            debug("%s([%i]) = EOF", __func__, fd); \
337        else \
338            debug("%s([%i]) = '%c'", __func__, fd, ret); \
339    } while(0)
340
341#undef getc /* can be a macro; we don’t want that */
342int NEW(getc)(FILE *stream)
343{
344    int ret; FGETC(getc, stream, stream); return ret;
345}
346
347#undef getchar /* can be a macro; we don’t want that */
348int NEW(getchar)(void)
349{
350    int ret; FGETC(getchar, stdin, /* empty */); return ret;
351}
352
353int NEW(fgetc)(FILE *stream)
354{
355    int ret; FGETC(fgetc, stream, stream); return ret;
356}
357
358#if defined HAVE__IO_GETC
359int NEW(_IO_getc)(FILE *stream)
360{
361    int ret; FGETC(_IO_getc, stream, stream); return ret;
362}
363#endif
364
365#if defined HAVE_GETC_UNLOCKED
366#undef getc_unlocked /* can be a macro; we don’t want that */
367int NEW(getc_unlocked)(FILE *stream)
368{
369    int ret; FGETC(getc_unlocked, stream, stream); return ret;
370}
371#endif
372
373#if defined HAVE_GETCHAR_UNLOCKED
374#undef getchar_unlocked /* can be a macro; we don’t want that */
375int NEW(getchar_unlocked)(void)
376{
377    int ret; FGETC(getchar_unlocked, stdin, /* empty */); return ret;
378}
379#endif
380
381#if defined HAVE_FGETC_UNLOCKED
382#undef fgetc_unlocked /* can be a macro; we don’t want that */
383int NEW(fgetc_unlocked)(FILE *stream)
384{
385    int ret; FGETC(fgetc_unlocked, stream, stream); return ret;
386}
387#endif
388
389#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
390#   define FGETS_FUZZ(fn, fn2) \
391        _zz_lock(fd); \
392        ret = ORIG(fn)(s, size, stream); \
393        _zz_unlock(fd);
394#else
395#   define FGETS_FUZZ(fn, fn2) \
396        if(size <= 0) \
397            ret = NULL; \
398        else if(size == 1) \
399            s[0] = '\0'; \
400        else \
401        { \
402            int i; \
403            for(i = 0; i < size - 1; i++) \
404            { \
405                int ch; \
406                _zz_lock(fd); \
407                ch = ORIG(fn2)(stream); \
408                _zz_unlock(fd); \
409                if(ch == EOF) \
410                { \
411                    s[i] = '\0'; \
412                    if(!i) \
413                        ret = NULL; \
414                    break; \
415                } \
416                s[i] = (char)(unsigned char)ch; \
417                _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */ \
418                _zz_addpos(fd, 1); \
419                if(s[i] == '\n') \
420                { \
421                    s[i + 1] = '\0'; \
422                    break; \
423                } \
424            } \
425        }
426#endif
427
428#define FGETS(fn, fn2) \
429    do \
430    { \
431        int fd; \
432        ret = s; \
433        LOADSYM(fn); \
434        LOADSYM(fn2); \
435        fd = fileno(stream); \
436        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
437            return ORIG(fn)(s, size, stream); \
438        FGETS_FUZZ(fn, fn2) \
439        debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret); \
440    } while(0)
441
442char *NEW(fgets)(char *s, int size, FILE *stream)
443{
444    char *ret; FGETS(fgets, fgetc); return ret;
445}
446
447#if defined HAVE_FGETS_UNLOCKED
448char *NEW(fgets_unlocked)(char *s, int size, FILE *stream)
449{
450    char *ret; FGETS(fgets_unlocked, fgetc_unlocked); return ret;
451}
452#endif
453
454int NEW(ungetc)(int c, FILE *stream)
455{
456    int ret, fd;
457
458    LOADSYM(ungetc);
459    fd = fileno(stream);
460    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
461        return ORIG(ungetc)(c, stream);
462
463    _zz_lock(fd);
464    ret = ORIG(ungetc)(c, stream);
465    _zz_unlock(fd);
466
467    if(ret != EOF)
468    {
469        struct fuzz *fuzz = _zz_getfuzz(fd);
470        fuzz->uflag = 1;
471        fuzz->upos = _zz_getpos(fd) - 1;
472        fuzz->uchar = c;
473#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
474#else
475        _zz_addpos(fd, -1);
476#endif
477    }
478
479    if(ret == EOF)
480        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
481    else
482        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
483
484    return ret;
485}
486
487int NEW(fclose)(FILE *fp)
488{
489    int ret, fd;
490
491    LOADSYM(fclose);
492    fd = fileno(fp);
493    if(!_zz_ready || !_zz_iswatched(fd))
494        return ORIG(fclose)(fp);
495
496    _zz_lock(fd);
497    ret = ORIG(fclose)(fp);
498    _zz_unlock(fd);
499    debug("%s([%i]) = %i", __func__, fd, ret);
500    _zz_unregister(fd);
501
502    return ret;
503}
504
505#define GETDELIM(fn, delim, need_delim) \
506    do { \
507        char *line; \
508        ssize_t done, size; \
509        int fd, finished = 0; \
510        LOADSYM(fn); \
511        LOADSYM(getdelim); \
512        LOADSYM(fgetc); \
513        fd = fileno(stream); \
514        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
515            return ORIG(getdelim)(lineptr, n, delim, stream); \
516        line = *lineptr; \
517        size = line ? *n : 0; \
518        ret = done = finished = 0; \
519        for(;;) \
520        { \
521            int ch; \
522            if(done >= size) /* highly inefficient but I don't care */ \
523                line = realloc(line, size = done + 1); \
524            if(finished) \
525            { \
526                line[done] = '\0'; \
527                *n = size; \
528                *lineptr = line; \
529                break; \
530            } \
531            _zz_lock(fd); \
532            ch = ORIG(fgetc)(stream); \
533            _zz_unlock(fd); \
534            if(ch == EOF) \
535            { \
536                finished = 1; \
537                ret = done; \
538            } \
539            else \
540            { \
541                unsigned char c = ch; \
542                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
543                line[done++] = c; \
544                _zz_addpos(fd, 1); \
545                if(c == delim) \
546                { \
547                    finished = 1; \
548                    ret = done; \
549                } \
550            } \
551        } \
552        if(need_delim) \
553            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
554                  lineptr, n, delim, fd, (long int)ret); \
555        else \
556            debug("%s(%p, %p, [%i]) = %li", __func__, \
557                  lineptr, n, fd, (long int)ret); \
558        return ret; \
559    } while(0)
560
561#if defined HAVE_GETLINE
562ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
563{
564    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
565}
566#endif
567
568#if defined HAVE_GETDELIM
569ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
570{
571    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
572}
573#endif
574
575#if defined HAVE___GETDELIM
576ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
577{
578    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
579}
580#endif
581
582#if defined HAVE_FGETLN
583char *NEW(fgetln)(FILE *stream, size_t *len)
584{
585    char *ret;
586#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
587#else
588    struct fuzz *fuzz;
589    size_t i, size;
590#endif
591    int fd;
592
593    LOADSYM(fgetln);
594    LOADSYM(fgetc);
595    fd = fileno(stream);
596    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
597        return ORIG(fgetln)(stream, len);
598
599#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
600    _zz_lock(fd);
601    ret = ORIG(fgetln)(stream, len);
602    _zz_unlock(fd);
603#else
604    fuzz = _zz_getfuzz(fd);
605
606    for(i = size = 0; ; /* i is incremented below */)
607    {
608        int ch;
609
610        _zz_lock(fd);
611        ch = ORIG(fgetc)(stream);
612        _zz_unlock(fd);
613
614        if(ch == EOF)
615            break;
616
617        if(i >= size)
618            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
619
620        fuzz->tmp[i] = (char)(unsigned char)ch;
621        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
622        _zz_addpos(fd, 1);
623
624        if(fuzz->tmp[i++] == '\n')
625            break;
626    }
627
628    *len = i;
629    ret = fuzz->tmp;
630#endif
631
632    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
633    return ret;
634}
635#endif
636
637#if defined HAVE___SREFILL
638int NEW(__srefill)(FILE *fp)
639{
640    off_t newpos;
641    int ret, fd, tmp;
642
643    LOADSYM(__srefill);
644    fd = fileno(fp);
645    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
646        return ORIG(__srefill)(fp);
647
648    _zz_lock(fd);
649    ret = ORIG(__srefill)(fp);
650    newpos = lseek(fd, 0, SEEK_CUR);
651    _zz_unlock(fd);
652    if(ret != EOF)
653    {
654        if(newpos != -1)
655            _zz_setpos(fd, newpos - fp->_r);
656        _zz_fuzz(fd, fp->_p, fp->_r);
657        _zz_addpos(fd, fp->_r);
658    }
659
660    if(!_zz_islocked(fd))
661        debug("%s([%i]) = %i", __func__, fd, ret);
662
663    return ret;
664}
665#endif
666
Note: See TracBrowser for help on using the repository browser.