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

Last change on this file since 4011 was 4011, checked in by sam, 4 years ago

Reimplement uflow() under Linux. Unlocked IO now works on Linux, except
in combination with getdelim()/getline().

  • Property svn:keywords set to Id
File size: 24.7 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$
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#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
399#   define FREAD_PREFUZZ() \
400    do \
401    { \
402        int64_t tmp = _zz_getpos(fd); \
403        _zz_setpos(fd, pos); \
404        already_fuzzed = _zz_getfuzzed(fd); \
405        _zz_setpos(fd, tmp); \
406    } \
407    while(0)
408#else
409#   define FREAD_PREFUZZ() do {} while(0)
410#endif
411
412#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
413#   define FREAD_FUZZ() \
414    do \
415    { \
416        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
417              (long int)size, (long int)nmemb, fd, (long int)ret); \
418    } while(0)
419#else
420#   define FREAD_FUZZ() \
421    do \
422    { \
423        int64_t newpos = ftell(stream); \
424        /* XXX: the number of bytes read is not ret * size, because \
425         * a partial read may have advanced the stream pointer. However, \
426         * when reading from a pipe ftell() will return 0, and ret * size \
427         * is then better than nothing. */ \
428        if(newpos <= 0) \
429        { \
430            pos = _zz_getpos(fd); \
431            newpos = pos + ret * size; \
432        } \
433        if(newpos != pos) \
434        { \
435            char *b = ptr; \
436            /* Skip bytes that were already fuzzed by __filbuf or __srget */ \
437            if(newpos > pos + already_fuzzed) \
438            { \
439                _zz_setpos(fd, pos + already_fuzzed); \
440                _zz_fuzz(fd, ptr, newpos - pos - already_fuzzed); \
441            } \
442            _zz_setpos(fd, newpos); \
443            if(newpos >= pos + 4) \
444                debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, \
445                      ptr, (long int)size, (long int)nmemb, fd, \
446                      (long int)ret, b[0], b[1], b[2], b[3]); \
447            else \
448                debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr, \
449                      (long int)size, (long int)nmemb, fd, \
450                      (long int)ret, b[0]); \
451        } \
452        else \
453            debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
454                  (long int)size, (long int)nmemb, fd, (long int)ret); \
455    } while(0)
456#endif
457
458#define FREAD(fn) \
459    do \
460    { \
461        int64_t pos; \
462        int fd, already_fuzzed = 0; \
463        LOADSYM(fn); \
464        fd = fileno(stream); \
465        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
466            return ORIG(fn)(ptr, size, nmemb, stream); \
467        BEGIN_STREAM(stream); \
468        pos = ftell(stream); \
469        _zz_lock(fd); \
470        ret = ORIG(fn)(ptr, size, nmemb, stream); \
471        _zz_unlock(fd); \
472        FREAD_PREFUZZ(); \
473        FREAD_FUZZ(); \
474        END_STREAM(stream); \
475    } while(0)
476
477size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
478{
479    size_t ret; FREAD(fread); return ret;
480}
481
482#if defined HAVE_FREAD_UNLOCKED
483#undef fread_unlocked /* can be a macro; we don’t want that */
484size_t NEW(fread_unlocked)(void *ptr, size_t size, size_t nmemb, FILE *stream)
485{
486    size_t ret; FREAD(fread_unlocked); return ret;
487}
488#endif
489
490#if defined HAVE___FILBUF || defined HAVE___SRGET || defined HAVE___UFLOW
491#   define FGETC_PREFUZZ already_fuzzed = _zz_getfuzzed(fd);
492#else
493#   define FGETC_PREFUZZ
494#endif
495
496#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
497#   define FGETC_FUZZ
498#else
499#   define FGETC_FUZZ \
500        if(ret != EOF) \
501        { \
502            uint8_t ch = ret; \
503            if(already_fuzzed <= 0) \
504               _zz_fuzz(fd, &ch, 1); \
505            _zz_addpos(fd, 1); \
506            ret = ch; \
507        }
508#endif
509
510#define FGETC(fn, s, arg) \
511    do { \
512        int fd, already_fuzzed = 0; \
513        LOADSYM(fn); \
514        fd = fileno(s); \
515        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
516            return ORIG(fn)(arg); \
517        BEGIN_STREAM(s); \
518        _zz_lock(fd); \
519        ret = ORIG(fn)(arg); \
520        _zz_unlock(fd); \
521        FGETC_PREFUZZ \
522        FGETC_FUZZ \
523        if(ret == EOF) \
524            debug("%s([%i]) = EOF", __func__, fd); \
525        else \
526            debug("%s([%i]) = '%c'", __func__, fd, ret); \
527        END_STREAM(s); \
528    } while(0)
529
530#undef getc /* can be a macro; we don’t want that */
531int NEW(getc)(FILE *stream)
532{
533    int ret; FGETC(getc, stream, stream); return ret;
534}
535
536#undef getchar /* can be a macro; we don’t want that */
537int NEW(getchar)(void)
538{
539    int ret; FGETC(getchar, stdin, /* empty */); return ret;
540}
541
542int NEW(fgetc)(FILE *stream)
543{
544    int ret; FGETC(fgetc, stream, stream); return ret;
545}
546
547#if defined HAVE__IO_GETC
548int NEW(_IO_getc)(FILE *stream)
549{
550    int ret; FGETC(_IO_getc, stream, stream); return ret;
551}
552#endif
553
554#if defined HAVE_GETC_UNLOCKED
555#undef getc_unlocked /* can be a macro; we don’t want that */
556int NEW(getc_unlocked)(FILE *stream)
557{
558    int ret; FGETC(getc_unlocked, stream, stream); return ret;
559}
560#endif
561
562#if defined HAVE_GETCHAR_UNLOCKED
563#undef getchar_unlocked /* can be a macro; we don’t want that */
564int NEW(getchar_unlocked)(void)
565{
566    int ret; FGETC(getchar_unlocked, stdin, /* empty */); return ret;
567}
568#endif
569
570#if defined HAVE_FGETC_UNLOCKED
571#undef fgetc_unlocked /* can be a macro; we don’t want that */
572int NEW(fgetc_unlocked)(FILE *stream)
573{
574    int ret; FGETC(fgetc_unlocked, stream, stream); return ret;
575}
576#endif
577
578#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
579#   define FGETS_FUZZ(fn, fn2) \
580        _zz_lock(fd); \
581        ret = ORIG(fn)(s, size, stream); \
582        _zz_unlock(fd);
583#else
584#   define FGETS_FUZZ(fn, fn2) \
585        if(size <= 0) \
586            ret = NULL; \
587        else if(size == 1) \
588            s[0] = '\0'; \
589        else \
590        { \
591            int i; \
592            for(i = 0; i < size - 1; i++) \
593            { \
594                int ch; \
595                _zz_lock(fd); \
596                ch = ORIG(fn2)(stream); \
597                _zz_unlock(fd); \
598                if(ch == EOF) \
599                { \
600                    s[i] = '\0'; \
601                    if(!i) \
602                        ret = NULL; \
603                    break; \
604                } \
605                s[i] = (char)(unsigned char)ch; \
606                _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */ \
607                _zz_addpos(fd, 1); \
608                if(s[i] == '\n') \
609                { \
610                    s[i + 1] = '\0'; \
611                    break; \
612                } \
613            } \
614        }
615#endif
616
617#define FGETS(fn, fn2) \
618    do \
619    { \
620        int fd; \
621        ret = s; \
622        LOADSYM(fn); \
623        LOADSYM(fn2); \
624        fd = fileno(stream); \
625        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
626            return ORIG(fn)(s, size, stream); \
627        BEGIN_STREAM(s); \
628        FGETS_FUZZ(fn, fn2) \
629        debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret); \
630        END_STREAM(s); \
631    } while(0)
632
633char *NEW(fgets)(char *s, int size, FILE *stream)
634{
635    char *ret; FGETS(fgets, fgetc); return ret;
636}
637
638#if defined HAVE_FGETS_UNLOCKED
639char *NEW(fgets_unlocked)(char *s, int size, FILE *stream)
640{
641    char *ret; FGETS(fgets_unlocked, fgetc_unlocked); return ret;
642}
643#endif
644
645int NEW(ungetc)(int c, FILE *stream)
646{
647    int ret, fd;
648
649    LOADSYM(ungetc);
650    fd = fileno(stream);
651    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
652        return ORIG(ungetc)(c, stream);
653
654    BEGIN_STREAM(stream);
655    _zz_lock(fd);
656    ret = ORIG(ungetc)(c, stream);
657    _zz_unlock(fd);
658
659    if(ret != EOF)
660    {
661        struct fuzz *fuzz = _zz_getfuzz(fd);
662        fuzz->uflag = 1;
663        fuzz->upos = _zz_getpos(fd) - 1;
664        fuzz->uchar = c;
665#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
666#else
667        _zz_addpos(fd, -1);
668#endif
669    }
670
671    if(ret == EOF)
672        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
673    else
674        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
675    END_STREAM(stream);
676    return ret;
677}
678
679int NEW(fclose)(FILE *fp)
680{
681    int ret, fd;
682
683    LOADSYM(fclose);
684    fd = fileno(fp);
685    if(!_zz_ready || !_zz_iswatched(fd))
686        return ORIG(fclose)(fp);
687
688    BEGIN_STREAM(fp);
689    _zz_lock(fd);
690    ret = ORIG(fclose)(fp);
691    _zz_unlock(fd);
692    debug("%s([%i]) = %i", __func__, fd, ret);
693    _zz_unregister(fd);
694
695    return ret;
696}
697
698#define GETDELIM(fn, delim, need_delim) \
699    do { \
700        char *line; \
701        ssize_t done, size; \
702        int fd, finished = 0; \
703        LOADSYM(fn); \
704        LOADSYM(getdelim); \
705        LOADSYM(fgetc); \
706        fd = fileno(stream); \
707        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
708        { \
709            ret = ORIG(getdelim)(lineptr, n, delim, stream); \
710            break; \
711        } \
712        BEGIN_STREAM(stream); \
713        line = *lineptr; \
714        size = line ? *n : 0; \
715        ret = done = finished = 0; \
716        for(;;) \
717        { \
718            int ch; \
719            if(done >= size) /* highly inefficient but I don't care */ \
720                line = realloc(line, size = done + 1); \
721            if(finished) \
722            { \
723                line[done] = '\0'; \
724                *n = size; \
725                *lineptr = line; \
726                break; \
727            } \
728            _zz_lock(fd); \
729            ch = ORIG(fgetc)(stream); \
730            _zz_unlock(fd); \
731            if(ch == EOF) \
732            { \
733                finished = 1; \
734                ret = done; \
735            } \
736            else \
737            { \
738                unsigned char c = ch; \
739                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
740                line[done++] = c; \
741                _zz_addpos(fd, 1); \
742                if(c == delim) \
743                { \
744                    finished = 1; \
745                    ret = done; \
746                } \
747            } \
748        } \
749        if(need_delim) \
750            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
751                  lineptr, n, delim, fd, (long int)ret); \
752        else \
753            debug("%s(%p, %p, [%i]) = %li", __func__, \
754                  lineptr, n, fd, (long int)ret); \
755        END_STREAM(stream); \
756        break; \
757    } while(0)
758
759#if defined HAVE_GETLINE
760ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
761{
762    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
763}
764#endif
765
766#if defined HAVE_GETDELIM
767ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
768{
769    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
770}
771#endif
772
773#if defined HAVE___GETDELIM
774ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
775{
776    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
777}
778#endif
779
780#if defined HAVE_FGETLN
781char *NEW(fgetln)(FILE *stream, size_t *len)
782{
783    char *ret;
784#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
785#else
786    struct fuzz *fuzz;
787    size_t i, size;
788#endif
789    int fd;
790
791    LOADSYM(fgetln);
792    LOADSYM(fgetc);
793    fd = fileno(stream);
794    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
795        return ORIG(fgetln)(stream, len);
796
797    BEGIN_STREAM(stream);
798#if defined REFILL_ONLY_STDIO /* Don't fuzz or seek if we have __srefill() */
799    _zz_lock(fd);
800    ret = ORIG(fgetln)(stream, len);
801    _zz_unlock(fd);
802#else
803    fuzz = _zz_getfuzz(fd);
804
805    for(i = size = 0; ; /* i is incremented below */)
806    {
807        int ch;
808
809        _zz_lock(fd);
810        ch = ORIG(fgetc)(stream);
811        _zz_unlock(fd);
812
813        if(ch == EOF)
814            break;
815
816        if(i >= size)
817            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
818
819        fuzz->tmp[i] = (char)(unsigned char)ch;
820        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
821        _zz_addpos(fd, 1);
822
823        if(fuzz->tmp[i++] == '\n')
824            break;
825    }
826
827    *len = i;
828    ret = fuzz->tmp;
829#endif
830
831    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
832    END_STREAM(stream);
833    return ret;
834}
835#endif
836
837#if defined HAVE___FILBUF || defined HAVE___SRGET
838#   define REFILL_RETURNS_INT 1
839#elif defined HAVE___UFLOW
840#   define REFILL_RETURNS_INT 0
841#endif
842
843#define REFILL(fn, fn_advances) \
844    do \
845    { \
846        int64_t pos; \
847        off_t newpos; \
848        int fd; \
849        LOADSYM(fn); \
850        fd = fileno(fp); \
851        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
852            return ORIG(fn)(fp); \
853        BEGIN_STREAM(fp); \
854        pos = _zz_getpos(fd); \
855        _zz_lock(fd); \
856        ret = ORIG(fn)(fp); \
857        newpos = lseek(fd, 0, SEEK_CUR); \
858        _zz_unlock(fd); \
859        if(ret != EOF) \
860        { \
861            int already_fuzzed = 0; \
862            if(fn_advances) \
863            { \
864                uint8_t ch = (uint8_t)(unsigned int)ret; \
865                if(newpos != -1) \
866                    _zz_setpos(fd, newpos - get_stream_cnt(fp) - 1); \
867                already_fuzzed = _zz_getfuzzed(fd); \
868                _zz_fuzz(fd, &ch, 1); \
869                ret = get_stream_ptr(fp)[-1] = ch; \
870                _zz_setfuzzed(fd, get_stream_cnt(fp) + 1); \
871                _zz_addpos(fd, 1); \
872            } \
873            else \
874            { \
875                _zz_setfuzzed(fd, get_stream_cnt(fp)); \
876                if(newpos != -1) \
877                    _zz_setpos(fd, newpos - get_stream_cnt(fp)); \
878            } \
879            if(get_stream_cnt(fp) > already_fuzzed) \
880            { \
881                _zz_addpos(fd, already_fuzzed); \
882                _zz_fuzz(fd, get_stream_ptr(fp), \
883                             get_stream_cnt(fp) - already_fuzzed); \
884            } \
885            _zz_addpos(fd, get_stream_cnt(fp) - already_fuzzed); \
886        } \
887        _zz_setpos(fd, pos); /* FIXME: do we always need to do this? */ \
888        if (REFILL_RETURNS_INT) \
889            debug("%s([%i]) = %i", __func__, fd, ret); \
890        else if (ret == EOF) \
891            debug("%s([%i]) = EOF", __func__, fd, ret); \
892        else \
893            debug("%s([%i]) = '%c'", __func__, fd, ret); \
894        END_STREAM(fp); \
895    } \
896    while(0)
897
898#if defined HAVE___SREFILL
899int NEW(__srefill)(FILE *fp)
900{
901    int ret; REFILL(__srefill, 0); return ret;
902}
903#endif
904
905#if defined HAVE___SRGET && !defined HAVE___SREFILL
906int NEW(__srget)(FILE *fp)
907{
908    int ret; REFILL(__srget, 1); return ret;
909}
910#endif
911
912#if defined HAVE___FILBUF
913int NEW(__filbuf)(FILE *fp)
914{
915    int ret; REFILL(__filbuf, 1); return ret;
916}
917#endif
918
919#if defined HAVE___UFLOW
920int NEW(__uflow)(FILE *fp)
921{
922    int ret; REFILL(__uflow, 1); return ret;
923}
924#endif
925
Note: See TracBrowser for help on using the repository browser.