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

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