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

Last change on this file since 4011 was 4011, checked in by Sam Hocevar, 11 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: lib-stream.c 4011 2009-11-23 00:27:05Z 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#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.