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

Last change on this file was 4810, checked in by sam, 3 years ago

linux: fix a few compilation warnings.

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