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

Last change on this file since 4013 was 4013, checked in by Sam Hocevar, 11 years ago

More fread() behaviour improvements, with documentation.

  • Property svn:keywords set to Id
File size: 26.5 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: lib-stream.c 4013 2009-11-23 01:35:23Z 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/* Needed for getline() and getdelim() */
22#define _GNU_SOURCE
23/* Needed for getc_unlocked() on OpenSolaris */
24#define __EXTENSIONS__
25
26/* Define if stdio operations use *only* the refill mechanism */
27#if defined HAVE___SREFILL
28#   define REFILL_ONLY_STDIO
29#endif
30
31#if defined HAVE_STDINT_H
32#   include <stdint.h>
33#elif defined HAVE_INTTYPES_H
34#   include <inttypes.h>
35#endif
36#include <stdlib.h>
37
38#include <stdio.h>
39#include <sys/types.h>
40#if defined HAVE_UNISTD_H
41#   include <unistd.h> /* Needed for __srefill’s lseek() call */
42#endif
43
44#include "libzzuf.h"
45#include "lib-load.h"
46#include "debug.h"
47#include "fuzz.h"
48#include "fd.h"
49
50#if defined HAVE___SREFILL
51int NEW(__srefill)(FILE *fp);
52#endif
53
54#if defined HAVE___FILBUF
55int NEW(__filbuf)(FILE *fp);
56#endif
57
58#if defined HAVE___SRGET && !defined HAVE___SREFILL
59int NEW(__srget)(FILE *fp);
60#endif
61
62#if defined HAVE___UFLOW
63int NEW(__uflow)(FILE *fp);
64#endif
65
66/* Library functions that we divert */
67static FILE *  (*ORIG(fopen))    (const char *path, const char *mode);
68#if defined HAVE_FOPEN64
69static FILE *  (*ORIG(fopen64))  (const char *path, const char *mode);
70#endif
71#if defined HAVE___FOPEN64
72static FILE *  (*ORIG(__fopen64))(const char *path, const char *mode);
73#endif
74static FILE *  (*ORIG(freopen))  (const char *path, const char *mode,
75                                  FILE *stream);
76#if defined HAVE_FREOPEN64
77static FILE *  (*ORIG(freopen64))(const char *path, const char *mode,
78                                  FILE *stream);
79#endif
80#if defined HAVE___FREOPEN64
81static FILE *  (*ORIG(__freopen64)) (const char *path, const char *mode,
82                                     FILE *stream);
83#endif
84static int     (*ORIG(fseek))    (FILE *stream, long offset, int whence);
85#if defined HAVE_FSEEKO
86static int     (*ORIG(fseeko))   (FILE *stream, off_t offset, int whence);
87#endif
88#if defined HAVE_FSEEKO64
89static int     (*ORIG(fseeko64)) (FILE *stream, off_t offset, int whence);
90#endif
91#if defined HAVE___FSEEKO64
92static int     (*ORIG(__fseeko64)) (FILE *stream, off_t offset, int whence);
93#endif
94#if defined HAVE_FSETPOS64
95static int     (*ORIG(fsetpos64))(FILE *stream, const fpos64_t *pos);
96#endif
97#if defined HAVE___FSETPOS64
98static int     (*ORIG(__fsetpos64)) (FILE *stream, const fpos64_t *pos);
99#endif
100static void    (*ORIG(rewind))   (FILE *stream);
101static size_t  (*ORIG(fread))    (void *ptr, size_t size, size_t nmemb,
102                                  FILE *stream);
103#if defined HAVE_FREAD_UNLOCKED
104static size_t  (*ORIG(fread_unlocked))  (void *ptr, size_t size, size_t nmemb,
105                                         FILE *stream);
106#endif
107static int     (*ORIG(getc))     (FILE *stream);
108static int     (*ORIG(getchar))  (void);
109static int     (*ORIG(fgetc))    (FILE *stream);
110#if defined HAVE__IO_GETC
111static int     (*ORIG(_IO_getc)) (FILE *stream);
112#endif
113#if defined HAVE_GETC_UNLOCKED
114static int     (*ORIG(getc_unlocked))    (FILE *stream);
115#endif
116#if defined HAVE_GETCHAR_UNLOCKED
117static int     (*ORIG(getchar_unlocked)) (void);
118#endif
119#if defined HAVE_FGETC_UNLOCKED
120static int     (*ORIG(fgetc_unlocked))   (FILE *stream);
121#endif
122static char *  (*ORIG(fgets))    (char *s, int size, FILE *stream);
123#if defined HAVE_FGETS_UNLOCKED
124static char *  (*ORIG(fgets_unlocked))   (char *s, int size, FILE *stream);
125#endif
126static int     (*ORIG(ungetc))   (int c, FILE *stream);
127static int     (*ORIG(fclose))   (FILE *fp);
128
129/* Additional GNUisms */
130#if defined HAVE_GETLINE
131static ssize_t (*ORIG(getline))    (char **lineptr, size_t *n, FILE *stream);
132#endif
133#if defined HAVE_GETDELIM
134static ssize_t (*ORIG(getdelim))   (char **lineptr, size_t *n, int delim,
135                                    FILE *stream);
136#endif
137#if defined HAVE___GETDELIM
138static ssize_t (*ORIG(__getdelim)) (char **lineptr, size_t *n, int delim,
139                                    FILE *stream);
140#endif
141#if defined HAVE___UFLOW
142static int     (*ORIG(__uflow))    (FILE *fp);
143#endif
144
145/* Additional BSDisms */
146#if defined HAVE_FGETLN
147static char *  (*ORIG(fgetln))    (FILE *stream, size_t *len);
148#endif
149#if defined HAVE___SREFILL
150int            (*ORIG(__srefill)) (FILE *fp);
151#endif
152#if defined HAVE___SRGET && !defined HAVE___SREFILL
153int            (*ORIG(__srget))   (FILE *fp);
154#endif
155
156/* Additional HP-UXisms */
157#if defined HAVE___FILBUF
158int            (*ORIG(__filbuf))  (FILE *fp);
159#endif
160
161/* Helper functions for refill-like functions */
162static inline uint8_t *get_stream_ptr(FILE *stream)
163{
164#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
165    return (uint8_t *)stream->FILE_PTR;
166#else
167    return NULL;
168#endif
169}
170
171static inline int get_stream_cnt(FILE *stream)
172{
173#if defined HAVE_GLIBC_FP
174    return (int)((uint8_t *)stream->FILE_CNT - (uint8_t *)stream->FILE_PTR);
175#elif defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
176    return stream->FILE_CNT;
177#else
178    return 0;
179#endif
180}
181
182#define DEBUG_STREAM(prefix, fp) \
183    debug2(prefix "stream([%i], %p, %i)", fileno(fp), \
184           get_stream_ptr(fp), get_stream_cnt(fp));
185
186/*
187 * fopen, fopen64 etc.
188 */
189
190#if defined REFILL_ONLY_STDIO /* Fuzz fp if we have __srefill() */
191#   define FOPEN_FUZZ() \
192    _zz_fuzz(fd, get_stream_ptr(ret), get_stream_cnt(ret))
193#else
194#   define FOPEN_FUZZ()
195#endif
196
197#define FOPEN(myfopen) \
198    do \
199    { \
200        LOADSYM(myfopen); \
201        if(!_zz_ready) \
202            return ORIG(myfopen)(path, mode); \
203        _zz_lock(-1); \
204        ret = ORIG(myfopen)(path, mode); \
205        _zz_unlock(-1); \
206        if(ret && _zz_mustwatch(path)) \
207        { \
208            int fd = fileno(ret); \
209            _zz_register(fd); \
210            debug("%s(\"%s\", \"%s\") = [%i]", __func__, path, mode, fd); \
211            DEBUG_STREAM("new", ret); \
212            FOPEN_FUZZ(); \
213        } \
214    } while(0)
215
216FILE *NEW(fopen)(const char *path, const char *mode)
217{
218    FILE *ret; FOPEN(fopen); return ret;
219}
220
221#if defined HAVE_FOPEN64
222FILE *NEW(fopen64)(const char *path, const char *mode)
223{
224    FILE *ret; FOPEN(fopen64); return ret;
225}
226#endif
227
228#if defined HAVE___FOPEN64
229FILE *NEW(__fopen64)(const char *path, const char *mode)
230{
231    FILE *ret; FOPEN(__fopen64); return ret;
232}
233#endif
234
235/*
236 * freopen, freopen64 etc.
237 */
238
239#define FREOPEN(myfreopen) \
240    do \
241    { \
242        int fd0 = -1, fd1 = -1, disp = 0; \
243        LOADSYM(myfreopen); \
244        if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0)) \
245        { \
246            _zz_unregister(fd0); \
247            disp = 1; \
248        } \
249        _zz_lock(-1); \
250        ret = ORIG(myfreopen)(path, mode, stream); \
251        _zz_unlock(-1); \
252        if(ret && _zz_mustwatch(path)) \
253        { \
254            fd1 = fileno(ret); \
255            _zz_register(fd1); \
256            disp = 1; \
257        } \
258        if(disp) \
259            debug("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__, \
260                  path, mode, fd0, fd1); \
261    } while(0)
262
263FILE *NEW(freopen)(const char *path, const char *mode, FILE *stream)
264{
265    FILE *ret; FREOPEN(freopen); return ret;
266}
267
268#if defined HAVE_FREOPEN64
269FILE *NEW(freopen64)(const char *path, const char *mode, FILE *stream)
270{
271    FILE *ret; FREOPEN(freopen64); return ret;
272}
273#endif
274
275#if defined HAVE___FREOPEN64
276FILE *NEW(__freopen64)(const char *path, const char *mode, FILE *stream)
277{
278    FILE *ret; FREOPEN(__freopen64); return ret;
279}
280#endif
281
282/*
283 * fseek, fseeko etc.
284 */
285
286#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
287#   define FSEEK_FUZZ(myftell)
288#else
289#   define FSEEK_FUZZ(myftell) \
290        if(ret == 0) \
291        { \
292            /* FIXME: check what happens when fseek()ing a pipe */ \
293            switch(whence) \
294            { \
295                case SEEK_END: \
296                    offset = myftell(stream); \
297                    /* fall through */ \
298                case SEEK_SET: \
299                    _zz_setpos(fd, offset); \
300                    break; \
301                case SEEK_CUR: \
302                    _zz_addpos(fd, offset); \
303                    break; \
304            } \
305        }
306#endif
307
308#define FSEEK(myfseek, myftell) \
309    do \
310    { \
311        int fd; \
312        LOADSYM(myfseek); \
313        fd = fileno(stream); \
314        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
315            return ORIG(myfseek)(stream, offset, whence); \
316        DEBUG_STREAM("old", stream); \
317        _zz_lock(fd); \
318        ret = ORIG(myfseek)(stream, offset, whence); \
319        _zz_unlock(fd); \
320        debug("%s([%i], %lli, %i) = %i", __func__, \
321              fd, (long long int)offset, whence, ret); \
322        FSEEK_FUZZ(myftell) \
323        DEBUG_STREAM("new", stream); \
324    } while(0)
325
326int NEW(fseek)(FILE *stream, long offset, int whence)
327{
328    int ret; FSEEK(fseek, ftell); return ret;
329}
330
331#if defined HAVE_FSEEKO
332int NEW(fseeko)(FILE *stream, off_t offset, int whence)
333{
334    int ret; FSEEK(fseeko, ftello); return ret;
335}
336#endif
337
338#if defined HAVE_FSEEKO64
339int NEW(fseeko64)(FILE *stream, off64_t offset, int whence)
340{
341    int ret; FSEEK(fseeko64, ftello64); return ret;
342}
343#endif
344
345#if defined HAVE___FSEEKO64
346int NEW(__fseeko64)(FILE *stream, off64_t offset, int whence)
347{
348    int ret; FSEEK(__fseeko64, ftello); return ret;
349}
350#endif
351
352/*
353 * fsetpos64, __fsetpos64
354 */
355
356#define FSETPOS(myfsetpos) \
357    do \
358    { \
359        int fd; \
360        LOADSYM(myfsetpos); \
361        fd = fileno(stream); \
362        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
363            return ORIG(myfsetpos)(stream, pos); \
364        DEBUG_STREAM("old", stream); \
365        _zz_lock(fd); \
366        ret = ORIG(myfsetpos)(stream, pos); \
367        _zz_unlock(fd); \
368        debug("%s([%i], %lli) = %i", __func__, \
369              fd, (long long int)FPOS_CAST(*pos), ret); \
370        _zz_setpos(fd, (int64_t)FPOS_CAST(*pos)); \
371        DEBUG_STREAM("new", stream); \
372    } \
373    while(0)
374
375#if defined HAVE_FSETPOS64
376int NEW(fsetpos64)(FILE *stream, const fpos64_t *pos)
377{
378    int ret; FSETPOS(fsetpos64); return ret;
379}
380#endif
381
382#if defined HAVE___FSETPOS64
383int NEW(__fsetpos64)(FILE *stream, const fpos64_t *pos)
384{
385    int ret; FSETPOS(__fsetpos64); return ret;
386}
387#endif
388
389/*
390 * rewind
391 */
392
393void NEW(rewind)(FILE *stream)
394{
395    int fd;
396
397    LOADSYM(rewind);
398    fd = fileno(stream);
399    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
400    {
401        ORIG(rewind)(stream);
402        return;
403    }
404
405    _zz_lock(fd);
406    ORIG(rewind)(stream);
407    _zz_unlock(fd);
408    debug("%s([%i])", __func__, fd);
409
410#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
411#else
412    /* FIXME: check what happens when rewind()ing a pipe */
413    _zz_setpos(fd, 0);
414#endif
415}
416
417/*
418 * fread, fread_unlocked
419 */
420
421/* Compute how many bytes from the stream were already fuzzed by __filbuf,
422 * __srget or __uflow, and store it in already_fuzzed. If these functions
423 * are not available, do nothing. */
424#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
425#   define FREAD_PREFUZZ(fd, oldpos) \
426    do \
427    { \
428        int64_t tmp = _zz_getpos(fd); \
429        _zz_setpos(fd, oldpos); \
430        already_fuzzed = _zz_getfuzzed(fd); \
431        _zz_setpos(fd, tmp); \
432    } \
433    while(0)
434#else
435#   define FREAD_PREFUZZ(fd, oldpos) do {} while(0)
436#endif
437
438/* Fuzz the data returned by fread(). If a __fillbuf mechanism already
439 * fuzzed some of our data, we skip the relevant amount of bytes. If we
440 * have __srefill, we just do nothing because that function is the only
441 * one that actually fuzzes things. */
442#if defined REFILL_ONLY_STDIO
443#   define FREAD_FUZZ(fd, oldpos) \
444    do \
445    { \
446        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
447              (long int)size, (long int)nmemb, fd, (long int)ret); \
448    } while(0)
449#else
450#   define FREAD_FUZZ(fd, oldpos) \
451    do \
452    { \
453        int64_t newpos = ftell(stream); \
454        /* XXX: the number of bytes read is not ret * size, because \
455         * a partial read may have advanced the stream pointer. However, \
456         * when reading from a pipe ftell() will return 0, and ret * size \
457         * is then better than nothing. */ \
458        if(newpos <= 0) \
459        { \
460            oldpos = _zz_getpos(fd); \
461            newpos = oldpos + ret * size; \
462        } \
463        if(newpos != oldpos) \
464        { \
465            char *b = ptr; \
466            /* Skip bytes that were already fuzzed by __filbuf or __srget */ \
467            if(newpos > oldpos + already_fuzzed) \
468            { \
469                _zz_setpos(fd, oldpos + already_fuzzed); \
470                _zz_fuzz(fd, ptr, newpos - oldpos - already_fuzzed); \
471                /* FIXME: we need to fuzz the extra bytes that may have been \
472                 * read by the fread call we just made, or subsequent calls \
473                 * to getc_unlocked may miss them. */ \
474                _zz_setpos(fd, newpos); \
475                _zz_fuzz(fd, get_stream_ptr(stream), get_stream_cnt(stream)); \
476                _zz_setfuzzed(fd, get_stream_cnt(stream)); \
477                /* FIXME: *AND* we need to fuzz the buffer before the current \
478                 * position, in case fseek() causes us to rewind. */ \
479            } \
480            _zz_setpos(fd, newpos); \
481            if(newpos >= oldpos + 4) \
482                debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, \
483                      ptr, (long int)size, (long int)nmemb, fd, \
484                      (long int)ret, b[0], b[1], b[2], b[3]); \
485            else \
486                debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr, \
487                      (long int)size, (long int)nmemb, fd, \
488                      (long int)ret, b[0]); \
489        } \
490        else \
491            debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
492                  (long int)size, (long int)nmemb, fd, (long int)ret); \
493    } while(0)
494#endif
495
496#define FREAD(myfread) \
497    do \
498    { \
499        int64_t oldpos; \
500        int fd, already_fuzzed = 0; \
501        LOADSYM(myfread); \
502        fd = fileno(stream); \
503        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
504            return ORIG(myfread)(ptr, size, nmemb, stream); \
505        DEBUG_STREAM("old", stream); \
506        oldpos = ftell(stream); \
507        _zz_lock(fd); \
508        ret = ORIG(myfread)(ptr, size, nmemb, stream); \
509        _zz_unlock(fd); \
510        FREAD_PREFUZZ(fd, oldpos); \
511        FREAD_FUZZ(fd, oldpos); \
512        DEBUG_STREAM("new", stream); \
513    } while(0)
514
515size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
516{
517    size_t ret; FREAD(fread); return ret;
518}
519
520#if defined HAVE_FREAD_UNLOCKED
521#undef fread_unlocked /* can be a macro; we don’t want that */
522size_t NEW(fread_unlocked)(void *ptr, size_t size, size_t nmemb, FILE *stream)
523{
524    size_t ret; FREAD(fread_unlocked); return ret;
525}
526#endif
527
528/*
529 * getc, getchar, fgetc etc.
530 */
531
532#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
533#   define FGETC_PREFUZZ already_fuzzed = _zz_getfuzzed(fd);
534#else
535#   define FGETC_PREFUZZ
536#endif
537
538#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
539#   define FGETC_FUZZ
540#else
541#   define FGETC_FUZZ \
542        if(ret != EOF) \
543        { \
544            uint8_t ch = ret; \
545            if(already_fuzzed <= 0) \
546               _zz_fuzz(fd, &ch, 1); \
547            _zz_addpos(fd, 1); \
548            ret = ch; \
549        }
550#endif
551
552#define FGETC(myfgetc, s, arg) \
553    do { \
554        int fd, already_fuzzed = 0; \
555        LOADSYM(myfgetc); \
556        fd = fileno(s); \
557        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
558            return ORIG(myfgetc)(arg); \
559        DEBUG_STREAM("old", s); \
560        _zz_lock(fd); \
561        ret = ORIG(myfgetc)(arg); \
562        _zz_unlock(fd); \
563        FGETC_PREFUZZ \
564        FGETC_FUZZ \
565        if(ret == EOF) \
566            debug("%s([%i]) = EOF", __func__, fd); \
567        else \
568            debug("%s([%i]) = '%c'", __func__, fd, ret); \
569        DEBUG_STREAM("new", s); \
570    } while(0)
571
572#undef getc /* can be a macro; we don’t want that */
573int NEW(getc)(FILE *stream)
574{
575    int ret; FGETC(getc, stream, stream); return ret;
576}
577
578#undef getchar /* can be a macro; we don’t want that */
579int NEW(getchar)(void)
580{
581    int ret; FGETC(getchar, stdin, /* empty */); return ret;
582}
583
584int NEW(fgetc)(FILE *stream)
585{
586    int ret; FGETC(fgetc, stream, stream); return ret;
587}
588
589#if defined HAVE__IO_GETC
590int NEW(_IO_getc)(FILE *stream)
591{
592    int ret; FGETC(_IO_getc, stream, stream); return ret;
593}
594#endif
595
596#if defined HAVE_GETC_UNLOCKED
597#undef getc_unlocked /* can be a macro; we don’t want that */
598int NEW(getc_unlocked)(FILE *stream)
599{
600    int ret; FGETC(getc_unlocked, stream, stream); return ret;
601}
602#endif
603
604#if defined HAVE_GETCHAR_UNLOCKED
605#undef getchar_unlocked /* can be a macro; we don’t want that */
606int NEW(getchar_unlocked)(void)
607{
608    int ret; FGETC(getchar_unlocked, stdin, /* empty */); return ret;
609}
610#endif
611
612#if defined HAVE_FGETC_UNLOCKED
613#undef fgetc_unlocked /* can be a macro; we don’t want that */
614int NEW(fgetc_unlocked)(FILE *stream)
615{
616    int ret; FGETC(fgetc_unlocked, stream, stream); return ret;
617}
618#endif
619
620/*
621 * fgets, fgets_unlocked
622 */
623
624#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
625#   define FGETS_FUZZ(myfgets, myfgetc) \
626        _zz_lock(fd); \
627        ret = ORIG(myfgets)(s, size, stream); \
628        _zz_unlock(fd);
629#else
630#   define FGETS_FUZZ(myfgets, myfgetc) \
631        if(size <= 0) \
632            ret = NULL; \
633        else if(size == 1) \
634            s[0] = '\0'; \
635        else \
636        { \
637            int i; \
638            for(i = 0; i < size - 1; i++) \
639            { \
640                int ch; \
641                _zz_lock(fd); \
642                ch = ORIG(myfgetc)(stream); \
643                _zz_unlock(fd); \
644                if(ch == EOF) \
645                { \
646                    s[i] = '\0'; \
647                    if(!i) \
648                        ret = NULL; \
649                    break; \
650                } \
651                s[i] = (char)(unsigned char)ch; \
652                _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */ \
653                _zz_addpos(fd, 1); \
654                if(s[i] == '\n') \
655                { \
656                    s[i + 1] = '\0'; \
657                    break; \
658                } \
659            } \
660        }
661#endif
662
663#define FGETS(myfgets, myfgetc) \
664    do \
665    { \
666        int fd; \
667        ret = s; \
668        LOADSYM(myfgets); \
669        LOADSYM(myfgetc); \
670        fd = fileno(stream); \
671        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
672            return ORIG(myfgets)(s, size, stream); \
673        DEBUG_STREAM("old", s); \
674        FGETS_FUZZ(myfgets, myfgetc) \
675        debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret); \
676        DEBUG_STREAM("new", s); \
677    } while(0)
678
679char *NEW(fgets)(char *s, int size, FILE *stream)
680{
681    char *ret; FGETS(fgets, fgetc); return ret;
682}
683
684#if defined HAVE_FGETS_UNLOCKED
685char *NEW(fgets_unlocked)(char *s, int size, FILE *stream)
686{
687    char *ret; FGETS(fgets_unlocked, fgetc_unlocked); return ret;
688}
689#endif
690
691/*
692 * ungetc
693 */
694
695int NEW(ungetc)(int c, FILE *stream)
696{
697    int ret, fd;
698
699    LOADSYM(ungetc);
700    fd = fileno(stream);
701    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
702        return ORIG(ungetc)(c, stream);
703
704    DEBUG_STREAM("old", stream);
705    _zz_lock(fd);
706    ret = ORIG(ungetc)(c, stream);
707    _zz_unlock(fd);
708
709    if(ret != EOF)
710    {
711        struct fuzz *fuzz = _zz_getfuzz(fd);
712        fuzz->uflag = 1;
713        fuzz->upos = _zz_getpos(fd) - 1;
714        fuzz->uchar = c;
715#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
716#else
717        _zz_addpos(fd, -1);
718#endif
719    }
720
721    if(ret == EOF)
722        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
723    else
724        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
725    DEBUG_STREAM("new", stream);
726    return ret;
727}
728
729/*
730 * fclose
731 */
732
733int NEW(fclose)(FILE *fp)
734{
735    int ret, fd;
736
737    LOADSYM(fclose);
738    fd = fileno(fp);
739    if(!_zz_ready || !_zz_iswatched(fd))
740        return ORIG(fclose)(fp);
741
742    DEBUG_STREAM("old", fp);
743    _zz_lock(fd);
744    ret = ORIG(fclose)(fp);
745    _zz_unlock(fd);
746    debug("%s([%i]) = %i", __func__, fd, ret);
747    _zz_unregister(fd);
748
749    return ret;
750}
751
752/*
753 * getline, getdelim etc.
754 */
755
756#define GETDELIM(mygetdelim, delim, need_delim) \
757    do { \
758        char *line; \
759        ssize_t done, size; \
760        int fd, already_fuzzed = 0, finished = 0; \
761        LOADSYM(mygetdelim); \
762        LOADSYM(getdelim); \
763        LOADSYM(fgetc); \
764        fd = fileno(stream); \
765        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
766        { \
767            ret = ORIG(getdelim)(lineptr, n, delim, stream); \
768            break; \
769        } \
770        DEBUG_STREAM("old", stream); \
771        line = *lineptr; \
772        size = line ? *n : 0; \
773        ret = done = finished = 0; \
774        for(;;) \
775        { \
776            int ch; \
777            if(done >= size) /* highly inefficient but I don't care */ \
778                line = realloc(line, size = done + 1); \
779            if(finished) \
780            { \
781                line[done] = '\0'; \
782                *n = size; \
783                *lineptr = line; \
784                break; \
785            } \
786            _zz_lock(fd); \
787            ch = ORIG(fgetc)(stream); \
788            _zz_unlock(fd); \
789            if(ch == EOF) \
790            { \
791                finished = 1; \
792                ret = done; \
793            } \
794            else \
795            { \
796                unsigned char c = ch; \
797                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
798                line[done++] = c; \
799                _zz_addpos(fd, 1); \
800                if(c == delim) \
801                { \
802                    finished = 1; \
803                    ret = done; \
804                } \
805            } \
806        } \
807        if(need_delim) \
808            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
809                  lineptr, n, delim, fd, (long int)ret); \
810        else \
811            debug("%s(%p, %p, [%i]) = %li", __func__, \
812                  lineptr, n, fd, (long int)ret); \
813        DEBUG_STREAM("new", stream); \
814        break; \
815    } while(0)
816
817#if defined HAVE_GETLINE
818ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
819{
820    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
821}
822#endif
823
824#if defined HAVE_GETDELIM
825ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
826{
827    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
828}
829#endif
830
831#if defined HAVE___GETDELIM
832ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
833{
834    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
835}
836#endif
837
838/*
839 * fgetln
840 */
841
842#if defined HAVE_FGETLN
843char *NEW(fgetln)(FILE *stream, size_t *len)
844{
845    char *ret;
846#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
847#else
848    struct fuzz *fuzz;
849    size_t i, size;
850#endif
851    int fd;
852
853    LOADSYM(fgetln);
854    LOADSYM(fgetc);
855    fd = fileno(stream);
856    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
857        return ORIG(fgetln)(stream, len);
858
859    DEBUG_STREAM("old", stream);
860#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
861    _zz_lock(fd);
862    ret = ORIG(fgetln)(stream, len);
863    _zz_unlock(fd);
864#else
865    fuzz = _zz_getfuzz(fd);
866
867    for(i = size = 0; ; /* i is incremented below */)
868    {
869        int ch;
870
871        _zz_lock(fd);
872        ch = ORIG(fgetc)(stream);
873        _zz_unlock(fd);
874
875        if(ch == EOF)
876            break;
877
878        if(i >= size)
879            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
880
881        fuzz->tmp[i] = (char)(unsigned char)ch;
882        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
883        _zz_addpos(fd, 1);
884
885        if(fuzz->tmp[i++] == '\n')
886            break;
887    }
888
889    *len = i;
890    ret = fuzz->tmp;
891#endif
892
893    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
894    DEBUG_STREAM("new", stream);
895    return ret;
896}
897#endif
898
899/*
900 * __srefill, __filbuf, __srget, __uflow
901 */
902
903#if defined HAVE___SREFILL || defined HAVE___FILBUF || defined HAVE___SRGET
904#   define REFILL_RETURNS_INT 1
905#elif defined HAVE___UFLOW
906#   define REFILL_RETURNS_INT 0
907#endif
908
909#define REFILL(myrefill, fn_advances) \
910    do \
911    { \
912        int64_t pos; \
913        off_t newpos; \
914        int fd; \
915        LOADSYM(myrefill); \
916        fd = fileno(fp); \
917        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
918            return ORIG(myrefill)(fp); \
919        DEBUG_STREAM("old", fp); \
920        pos = _zz_getpos(fd); \
921        _zz_lock(fd); \
922        ret = ORIG(myrefill)(fp); \
923        newpos = lseek(fd, 0, SEEK_CUR); \
924        _zz_unlock(fd); \
925        if(ret != EOF) \
926        { \
927            int already_fuzzed = 0; \
928            if(fn_advances) \
929            { \
930                uint8_t ch = (uint8_t)(unsigned int)ret; \
931                if(newpos != -1) \
932                    _zz_setpos(fd, newpos - get_stream_cnt(fp) - 1); \
933                already_fuzzed = _zz_getfuzzed(fd); \
934                _zz_fuzz(fd, &ch, 1); \
935                ret = get_stream_ptr(fp)[-1] = ch; \
936                _zz_setfuzzed(fd, get_stream_cnt(fp) + 1); \
937                _zz_addpos(fd, 1); \
938            } \
939            else \
940            { \
941                _zz_setfuzzed(fd, get_stream_cnt(fp)); \
942                if(newpos != -1) \
943                    _zz_setpos(fd, newpos - get_stream_cnt(fp)); \
944            } \
945            if(get_stream_cnt(fp) > already_fuzzed) \
946            { \
947                _zz_addpos(fd, already_fuzzed); \
948                _zz_fuzz(fd, get_stream_ptr(fp), \
949                             get_stream_cnt(fp) - already_fuzzed); \
950            } \
951            _zz_addpos(fd, get_stream_cnt(fp) - already_fuzzed); \
952        } \
953        _zz_setpos(fd, pos); /* FIXME: do we always need to do this? */ \
954        if (REFILL_RETURNS_INT) \
955            debug("%s([%i]) = %i", __func__, fd, ret); \
956        else if (ret == EOF) \
957            debug("%s([%i]) = EOF", __func__, fd, ret); \
958        else \
959            debug("%s([%i]) = '%c'", __func__, fd, ret); \
960        DEBUG_STREAM("new", fp); \
961    } \
962    while(0)
963
964#if defined HAVE___SREFILL
965int NEW(__srefill)(FILE *fp)
966{
967    int ret; REFILL(__srefill, 0); return ret;
968}
969#endif
970
971#if defined HAVE___SRGET && !defined HAVE___SREFILL
972int NEW(__srget)(FILE *fp)
973{
974    int ret; REFILL(__srget, 1); return ret;
975}
976#endif
977
978#if defined HAVE___FILBUF
979int NEW(__filbuf)(FILE *fp)
980{
981    int ret; REFILL(__filbuf, 1); return ret;
982}
983#endif
984
985#if defined HAVE___UFLOW
986int NEW(__uflow)(FILE *fp)
987{
988    int ret; REFILL(__uflow, 1); return ret;
989}
990#endif
991
Note: See TracBrowser for help on using the repository browser.