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

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

Document the srefill strategy in the source code.

  • Property svn:keywords set to Id
File size: 25.3 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 4012 2009-11-23 00:27: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/* 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 */
162#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
163static inline uint8_t *get_stream_ptr(FILE *stream)
164{
165    return (uint8_t *)stream->FILE_PTR;
166}
167
168static inline int get_stream_cnt(FILE *stream)
169{
170#   if defined HAVE_GLIBC_FP
171    return (int)((uint8_t *)stream->FILE_CNT - (uint8_t *)stream->FILE_PTR);
172#   else
173    return stream->FILE_CNT;
174#   endif
175}
176#endif
177
178/* Our function wrappers */
179#if defined REFILL_ONLY_STDIO /* Fuzz fp if we have __srefill() */
180#   define FOPEN_FUZZ() \
181    _zz_fuzz(fd, get_stream_ptr(ret), get_stream_cnt(ret))
182#else
183#   define FOPEN_FUZZ()
184#endif
185
186#define BEGIN_STREAM(fp) \
187    debug2("oldstream([%i], %p, %i)", fileno(fp), \
188           get_stream_ptr(fp), get_stream_cnt(fp));
189
190#define END_STREAM(fp) \
191    debug2("newstream([%i], %p, %i)", fileno(fp), \
192           get_stream_ptr(fp), get_stream_cnt(fp));
193
194#define FOPEN(fn) \
195    do \
196    { \
197        LOADSYM(fn); \
198        if(!_zz_ready) \
199            return ORIG(fn)(path, mode); \
200        _zz_lock(-1); \
201        ret = ORIG(fn)(path, mode); \
202        _zz_unlock(-1); \
203        if(ret && _zz_mustwatch(path)) \
204        { \
205            int fd = fileno(ret); \
206            _zz_register(fd); \
207            debug("%s(\"%s\", \"%s\") = [%i]", __func__, path, mode, fd); \
208            END_STREAM(ret); \
209            FOPEN_FUZZ(); \
210        } \
211    } while(0)
212
213FILE *NEW(fopen)(const char *path, const char *mode)
214{
215    FILE *ret; FOPEN(fopen); return ret;
216}
217
218#if defined HAVE_FOPEN64
219FILE *NEW(fopen64)(const char *path, const char *mode)
220{
221    FILE *ret; FOPEN(fopen64); return ret;
222}
223#endif
224
225#if defined HAVE___FOPEN64
226FILE *NEW(__fopen64)(const char *path, const char *mode)
227{
228    FILE *ret; FOPEN(__fopen64); return ret;
229}
230#endif
231
232#define FREOPEN(fn) \
233    do \
234    { \
235        int fd0 = -1, fd1 = -1, disp = 0; \
236        LOADSYM(fn); \
237        if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0)) \
238        { \
239            _zz_unregister(fd0); \
240            disp = 1; \
241        } \
242        _zz_lock(-1); \
243        ret = ORIG(fn)(path, mode, stream); \
244        _zz_unlock(-1); \
245        if(ret && _zz_mustwatch(path)) \
246        { \
247            fd1 = fileno(ret); \
248            _zz_register(fd1); \
249            disp = 1; \
250        } \
251        if(disp) \
252            debug("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__, \
253                  path, mode, fd0, fd1); \
254    } while(0)
255
256FILE *NEW(freopen)(const char *path, const char *mode, FILE *stream)
257{
258    FILE *ret; FREOPEN(freopen); return ret;
259}
260
261#if defined HAVE_FREOPEN64
262FILE *NEW(freopen64)(const char *path, const char *mode, FILE *stream)
263{
264    FILE *ret; FREOPEN(freopen64); return ret;
265}
266#endif
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 REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
276#   define FSEEK_FUZZ(fn2)
277#else
278#   define FSEEK_FUZZ(fn2) \
279        if(ret == 0) \
280        { \
281            /* FIXME: check what happens when fseek()ing a pipe */ \
282            switch(whence) \
283            { \
284                case SEEK_END: \
285                    offset = fn2(stream); \
286                    /* fall through */ \
287                case SEEK_SET: \
288                    _zz_setpos(fd, offset); \
289                    break; \
290                case SEEK_CUR: \
291                    _zz_addpos(fd, offset); \
292                    break; \
293            } \
294        }
295#endif
296
297#define FSEEK(fn, fn2) \
298    do \
299    { \
300        int fd; \
301        LOADSYM(fn); \
302        fd = fileno(stream); \
303        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
304            return ORIG(fn)(stream, offset, whence); \
305        BEGIN_STREAM(stream); \
306        _zz_lock(fd); \
307        ret = ORIG(fn)(stream, offset, whence); \
308        _zz_unlock(fd); \
309        debug("%s([%i], %lli, %i) = %i", __func__, \
310              fd, (long long int)offset, whence, ret); \
311        FSEEK_FUZZ(fn2) \
312        END_STREAM(stream); \
313    } while(0)
314
315int NEW(fseek)(FILE *stream, long offset, int whence)
316{
317    int ret; FSEEK(fseek, ftell); return ret;
318}
319
320#if defined HAVE_FSEEKO
321int NEW(fseeko)(FILE *stream, off_t offset, int whence)
322{
323    int ret; FSEEK(fseeko, ftello); return ret;
324}
325#endif
326
327#if defined HAVE_FSEEKO64
328int NEW(fseeko64)(FILE *stream, off64_t offset, int whence)
329{
330    int ret; FSEEK(fseeko64, ftello64); return ret;
331}
332#endif
333
334#if defined HAVE___FSEEKO64
335int NEW(__fseeko64)(FILE *stream, off64_t offset, int whence)
336{
337    int ret; FSEEK(__fseeko64, ftello); return ret;
338}
339#endif
340
341#define FSETPOS(fn) \
342    do \
343    { \
344        int fd; \
345        LOADSYM(fn); \
346        fd = fileno(stream); \
347        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
348            return ORIG(fn)(stream, pos); \
349        BEGIN_STREAM(stream); \
350        _zz_lock(fd); \
351        ret = ORIG(fn)(stream, pos); \
352        _zz_unlock(fd); \
353        debug("%s([%i], %lli) = %i", __func__, \
354              fd, (long long int)FPOS_CAST(*pos), ret); \
355        _zz_setpos(fd, (int64_t)FPOS_CAST(*pos)); \
356        END_STREAM(stream); \
357    } \
358    while(0)
359
360#if defined HAVE_FSETPOS64
361int NEW(fsetpos64)(FILE *stream, const fpos64_t *pos)
362{
363    int ret; FSETPOS(fsetpos64); return ret;
364}
365#endif
366
367#if defined HAVE___FSETPOS64
368int NEW(__fsetpos64)(FILE *stream, const fpos64_t *pos)
369{
370    int ret; FSETPOS(__fsetpos64); return ret;
371}
372#endif
373
374void NEW(rewind)(FILE *stream)
375{
376    int fd;
377
378    LOADSYM(rewind);
379    fd = fileno(stream);
380    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
381    {
382        ORIG(rewind)(stream);
383        return;
384    }
385
386    _zz_lock(fd);
387    ORIG(rewind)(stream);
388    _zz_unlock(fd);
389    debug("%s([%i])", __func__, fd);
390
391#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
392#else
393    /* FIXME: check what happens when rewind()ing a pipe */
394    _zz_setpos(fd, 0);
395#endif
396}
397
398/* Compute how many bytes from the stream were already fuzzed by __filbuf,
399 * __srget or __uflow, and store it in already_fuzzed. If these functions
400 * are not available, do nothing. */
401#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
402#   define FREAD_PREFUZZ(fd, pos) \
403    do \
404    { \
405        int64_t tmp = _zz_getpos(fd); \
406        _zz_setpos(fd, pos); \
407        already_fuzzed = _zz_getfuzzed(fd); \
408        _zz_setpos(fd, tmp); \
409    } \
410    while(0)
411#else
412#   define FREAD_PREFUZZ(fd, pos) do {} while(0)
413#endif
414
415/* Fuzz the data returned by fread(). If a __fillbuf mechanism already
416 * fuzzed some of our data, we skip the relevant amount of bytes. If we
417 * have __srefill, we just do nothing because that function is the only
418 * one that actually fuzzes things. */
419#if defined REFILL_ONLY_STDIO
420#   define FREAD_FUZZ(fd, pos) \
421    do \
422    { \
423        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
424              (long int)size, (long int)nmemb, fd, (long int)ret); \
425    } while(0)
426#else
427#   define FREAD_FUZZ(fd, pos) \
428    do \
429    { \
430        int64_t newpos = ftell(stream); \
431        /* XXX: the number of bytes read is not ret * size, because \
432         * a partial read may have advanced the stream pointer. However, \
433         * when reading from a pipe ftell() will return 0, and ret * size \
434         * is then better than nothing. */ \
435        if(newpos <= 0) \
436        { \
437            pos = _zz_getpos(fd); \
438            newpos = pos + ret * size; \
439        } \
440        if(newpos != pos) \
441        { \
442            char *b = ptr; \
443            /* Skip bytes that were already fuzzed by __filbuf or __srget */ \
444            if(newpos > pos + already_fuzzed) \
445            { \
446                _zz_setpos(fd, pos + already_fuzzed); \
447                _zz_fuzz(fd, ptr, newpos - pos - already_fuzzed); \
448            } \
449            /* FIXME: we need to fuzz the extra bytes that may have been \
450             * read by the fread call we just made, or subsequent calls \
451             * to getc_unlocked may miss them. */ \
452            _zz_setpos(fd, newpos); \
453            if(newpos >= pos + 4) \
454                debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, \
455                      ptr, (long int)size, (long int)nmemb, fd, \
456                      (long int)ret, b[0], b[1], b[2], b[3]); \
457            else \
458                debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr, \
459                      (long int)size, (long int)nmemb, fd, \
460                      (long int)ret, b[0]); \
461        } \
462        else \
463            debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
464                  (long int)size, (long int)nmemb, fd, (long int)ret); \
465    } while(0)
466#endif
467
468#define FREAD(fn) \
469    do \
470    { \
471        int64_t pos; \
472        int fd, already_fuzzed = 0; \
473        LOADSYM(fn); \
474        fd = fileno(stream); \
475        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
476            return ORIG(fn)(ptr, size, nmemb, stream); \
477        BEGIN_STREAM(stream); \
478        pos = ftell(stream); \
479        _zz_lock(fd); \
480        ret = ORIG(fn)(ptr, size, nmemb, stream); \
481        _zz_unlock(fd); \
482        FREAD_PREFUZZ(fd, pos); \
483        FREAD_FUZZ(fd, pos); \
484        END_STREAM(stream); \
485    } while(0)
486
487size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
488{
489    size_t ret; FREAD(fread); return ret;
490}
491
492#if defined HAVE_FREAD_UNLOCKED
493#undef fread_unlocked /* can be a macro; we don’t want that */
494size_t NEW(fread_unlocked)(void *ptr, size_t size, size_t nmemb, FILE *stream)
495{
496    size_t ret; FREAD(fread_unlocked); return ret;
497}
498#endif
499
500#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
501#   define FGETC_PREFUZZ already_fuzzed = _zz_getfuzzed(fd);
502#else
503#   define FGETC_PREFUZZ
504#endif
505
506#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
507#   define FGETC_FUZZ
508#else
509#   define FGETC_FUZZ \
510        if(ret != EOF) \
511        { \
512            uint8_t ch = ret; \
513            if(already_fuzzed <= 0) \
514               _zz_fuzz(fd, &ch, 1); \
515            _zz_addpos(fd, 1); \
516            ret = ch; \
517        }
518#endif
519
520#define FGETC(fn, s, arg) \
521    do { \
522        int fd, already_fuzzed = 0; \
523        LOADSYM(fn); \
524        fd = fileno(s); \
525        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
526            return ORIG(fn)(arg); \
527        BEGIN_STREAM(s); \
528        _zz_lock(fd); \
529        ret = ORIG(fn)(arg); \
530        _zz_unlock(fd); \
531        FGETC_PREFUZZ \
532        FGETC_FUZZ \
533        if(ret == EOF) \
534            debug("%s([%i]) = EOF", __func__, fd); \
535        else \
536            debug("%s([%i]) = '%c'", __func__, fd, ret); \
537        END_STREAM(s); \
538    } while(0)
539
540#undef getc /* can be a macro; we don’t want that */
541int NEW(getc)(FILE *stream)
542{
543    int ret; FGETC(getc, stream, stream); return ret;
544}
545
546#undef getchar /* can be a macro; we don’t want that */
547int NEW(getchar)(void)
548{
549    int ret; FGETC(getchar, stdin, /* empty */); return ret;
550}
551
552int NEW(fgetc)(FILE *stream)
553{
554    int ret; FGETC(fgetc, stream, stream); return ret;
555}
556
557#if defined HAVE__IO_GETC
558int NEW(_IO_getc)(FILE *stream)
559{
560    int ret; FGETC(_IO_getc, stream, stream); return ret;
561}
562#endif
563
564#if defined HAVE_GETC_UNLOCKED
565#undef getc_unlocked /* can be a macro; we don’t want that */
566int NEW(getc_unlocked)(FILE *stream)
567{
568    int ret; FGETC(getc_unlocked, stream, stream); return ret;
569}
570#endif
571
572#if defined HAVE_GETCHAR_UNLOCKED
573#undef getchar_unlocked /* can be a macro; we don’t want that */
574int NEW(getchar_unlocked)(void)
575{
576    int ret; FGETC(getchar_unlocked, stdin, /* empty */); return ret;
577}
578#endif
579
580#if defined HAVE_FGETC_UNLOCKED
581#undef fgetc_unlocked /* can be a macro; we don’t want that */
582int NEW(fgetc_unlocked)(FILE *stream)
583{
584    int ret; FGETC(fgetc_unlocked, stream, stream); return ret;
585}
586#endif
587
588#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
589#   define FGETS_FUZZ(fn, fn2) \
590        _zz_lock(fd); \
591        ret = ORIG(fn)(s, size, stream); \
592        _zz_unlock(fd);
593#else
594#   define FGETS_FUZZ(fn, fn2) \
595        if(size <= 0) \
596            ret = NULL; \
597        else if(size == 1) \
598            s[0] = '\0'; \
599        else \
600        { \
601            int i; \
602            for(i = 0; i < size - 1; i++) \
603            { \
604                int ch; \
605                _zz_lock(fd); \
606                ch = ORIG(fn2)(stream); \
607                _zz_unlock(fd); \
608                if(ch == EOF) \
609                { \
610                    s[i] = '\0'; \
611                    if(!i) \
612                        ret = NULL; \
613                    break; \
614                } \
615                s[i] = (char)(unsigned char)ch; \
616                _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */ \
617                _zz_addpos(fd, 1); \
618                if(s[i] == '\n') \
619                { \
620                    s[i + 1] = '\0'; \
621                    break; \
622                } \
623            } \
624        }
625#endif
626
627#define FGETS(fn, fn2) \
628    do \
629    { \
630        int fd; \
631        ret = s; \
632        LOADSYM(fn); \
633        LOADSYM(fn2); \
634        fd = fileno(stream); \
635        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
636            return ORIG(fn)(s, size, stream); \
637        BEGIN_STREAM(s); \
638        FGETS_FUZZ(fn, fn2) \
639        debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret); \
640        END_STREAM(s); \
641    } while(0)
642
643char *NEW(fgets)(char *s, int size, FILE *stream)
644{
645    char *ret; FGETS(fgets, fgetc); return ret;
646}
647
648#if defined HAVE_FGETS_UNLOCKED
649char *NEW(fgets_unlocked)(char *s, int size, FILE *stream)
650{
651    char *ret; FGETS(fgets_unlocked, fgetc_unlocked); return ret;
652}
653#endif
654
655int NEW(ungetc)(int c, FILE *stream)
656{
657    int ret, fd;
658
659    LOADSYM(ungetc);
660    fd = fileno(stream);
661    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
662        return ORIG(ungetc)(c, stream);
663
664    BEGIN_STREAM(stream);
665    _zz_lock(fd);
666    ret = ORIG(ungetc)(c, stream);
667    _zz_unlock(fd);
668
669    if(ret != EOF)
670    {
671        struct fuzz *fuzz = _zz_getfuzz(fd);
672        fuzz->uflag = 1;
673        fuzz->upos = _zz_getpos(fd) - 1;
674        fuzz->uchar = c;
675#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
676#else
677        _zz_addpos(fd, -1);
678#endif
679    }
680
681    if(ret == EOF)
682        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
683    else
684        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
685    END_STREAM(stream);
686    return ret;
687}
688
689int NEW(fclose)(FILE *fp)
690{
691    int ret, fd;
692
693    LOADSYM(fclose);
694    fd = fileno(fp);
695    if(!_zz_ready || !_zz_iswatched(fd))
696        return ORIG(fclose)(fp);
697
698    BEGIN_STREAM(fp);
699    _zz_lock(fd);
700    ret = ORIG(fclose)(fp);
701    _zz_unlock(fd);
702    debug("%s([%i]) = %i", __func__, fd, ret);
703    _zz_unregister(fd);
704
705    return ret;
706}
707
708#define GETDELIM(fn, delim, need_delim) \
709    do { \
710        char *line; \
711        ssize_t done, size; \
712        int fd, already_fuzzed = 0, finished = 0; \
713        LOADSYM(fn); \
714        LOADSYM(getdelim); \
715        LOADSYM(fgetc); \
716        fd = fileno(stream); \
717        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
718        { \
719            ret = ORIG(getdelim)(lineptr, n, delim, stream); \
720            break; \
721        } \
722        BEGIN_STREAM(stream); \
723        line = *lineptr; \
724        size = line ? *n : 0; \
725        ret = done = finished = 0; \
726        for(;;) \
727        { \
728            int ch; \
729            if(done >= size) /* highly inefficient but I don't care */ \
730                line = realloc(line, size = done + 1); \
731            if(finished) \
732            { \
733                line[done] = '\0'; \
734                *n = size; \
735                *lineptr = line; \
736                break; \
737            } \
738            _zz_lock(fd); \
739            ch = ORIG(fgetc)(stream); \
740            _zz_unlock(fd); \
741            if(ch == EOF) \
742            { \
743                finished = 1; \
744                ret = done; \
745            } \
746            else \
747            { \
748                unsigned char c = ch; \
749                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
750                line[done++] = c; \
751                _zz_addpos(fd, 1); \
752                if(c == delim) \
753                { \
754                    finished = 1; \
755                    ret = done; \
756                } \
757            } \
758        } \
759        if(need_delim) \
760            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
761                  lineptr, n, delim, fd, (long int)ret); \
762        else \
763            debug("%s(%p, %p, [%i]) = %li", __func__, \
764                  lineptr, n, fd, (long int)ret); \
765        END_STREAM(stream); \
766        break; \
767    } while(0)
768
769#if defined HAVE_GETLINE
770ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
771{
772    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
773}
774#endif
775
776#if defined HAVE_GETDELIM
777ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
778{
779    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
780}
781#endif
782
783#if defined HAVE___GETDELIM
784ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
785{
786    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
787}
788#endif
789
790#if defined HAVE_FGETLN
791char *NEW(fgetln)(FILE *stream, size_t *len)
792{
793    char *ret;
794#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
795#else
796    struct fuzz *fuzz;
797    size_t i, size;
798#endif
799    int fd;
800
801    LOADSYM(fgetln);
802    LOADSYM(fgetc);
803    fd = fileno(stream);
804    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
805        return ORIG(fgetln)(stream, len);
806
807    BEGIN_STREAM(stream);
808#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
809    _zz_lock(fd);
810    ret = ORIG(fgetln)(stream, len);
811    _zz_unlock(fd);
812#else
813    fuzz = _zz_getfuzz(fd);
814
815    for(i = size = 0; ; /* i is incremented below */)
816    {
817        int ch;
818
819        _zz_lock(fd);
820        ch = ORIG(fgetc)(stream);
821        _zz_unlock(fd);
822
823        if(ch == EOF)
824            break;
825
826        if(i >= size)
827            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
828
829        fuzz->tmp[i] = (char)(unsigned char)ch;
830        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
831        _zz_addpos(fd, 1);
832
833        if(fuzz->tmp[i++] == '\n')
834            break;
835    }
836
837    *len = i;
838    ret = fuzz->tmp;
839#endif
840
841    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
842    END_STREAM(stream);
843    return ret;
844}
845#endif
846
847#if defined HAVE___FILBUF || defined HAVE___SRGET
848#   define REFILL_RETURNS_INT 1
849#elif defined HAVE___UFLOW
850#   define REFILL_RETURNS_INT 0
851#endif
852
853#define REFILL(fn, fn_advances) \
854    do \
855    { \
856        int64_t pos; \
857        off_t newpos; \
858        int fd; \
859        LOADSYM(fn); \
860        fd = fileno(fp); \
861        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
862            return ORIG(fn)(fp); \
863        BEGIN_STREAM(fp); \
864        pos = _zz_getpos(fd); \
865        _zz_lock(fd); \
866        ret = ORIG(fn)(fp); \
867        newpos = lseek(fd, 0, SEEK_CUR); \
868        _zz_unlock(fd); \
869        if(ret != EOF) \
870        { \
871            int already_fuzzed = 0; \
872            if(fn_advances) \
873            { \
874                uint8_t ch = (uint8_t)(unsigned int)ret; \
875                if(newpos != -1) \
876                    _zz_setpos(fd, newpos - get_stream_cnt(fp) - 1); \
877                already_fuzzed = _zz_getfuzzed(fd); \
878                _zz_fuzz(fd, &ch, 1); \
879                ret = get_stream_ptr(fp)[-1] = ch; \
880                _zz_setfuzzed(fd, get_stream_cnt(fp) + 1); \
881                _zz_addpos(fd, 1); \
882            } \
883            else \
884            { \
885                _zz_setfuzzed(fd, get_stream_cnt(fp)); \
886                if(newpos != -1) \
887                    _zz_setpos(fd, newpos - get_stream_cnt(fp)); \
888            } \
889            if(get_stream_cnt(fp) > already_fuzzed) \
890            { \
891                _zz_addpos(fd, already_fuzzed); \
892                _zz_fuzz(fd, get_stream_ptr(fp), \
893                             get_stream_cnt(fp) - already_fuzzed); \
894            } \
895            _zz_addpos(fd, get_stream_cnt(fp) - already_fuzzed); \
896        } \
897        _zz_setpos(fd, pos); /* FIXME: do we always need to do this? */ \
898        if (REFILL_RETURNS_INT) \
899            debug("%s([%i]) = %i", __func__, fd, ret); \
900        else if (ret == EOF) \
901            debug("%s([%i]) = EOF", __func__, fd, ret); \
902        else \
903            debug("%s([%i]) = '%c'", __func__, fd, ret); \
904        END_STREAM(fp); \
905    } \
906    while(0)
907
908#if defined HAVE___SREFILL
909int NEW(__srefill)(FILE *fp)
910{
911    int ret; REFILL(__srefill, 0); return ret;
912}
913#endif
914
915#if defined HAVE___SRGET && !defined HAVE___SREFILL
916int NEW(__srget)(FILE *fp)
917{
918    int ret; REFILL(__srget, 1); return ret;
919}
920#endif
921
922#if defined HAVE___FILBUF
923int NEW(__filbuf)(FILE *fp)
924{
925    int ret; REFILL(__filbuf, 1); return ret;
926}
927#endif
928
929#if defined HAVE___UFLOW
930int NEW(__uflow)(FILE *fp)
931{
932    int ret; REFILL(__uflow, 1); return ret;
933}
934#endif
935
Note: See TracBrowser for help on using the repository browser.