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

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

getline() and getdelim() must return -1 if EOF was reached and no bytes
were read.

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