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

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