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

Last change on this file was 4888, checked in by sam, 2 weeks ago

win32: update to newer mingw compiler version.

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