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

Last change on this file since 4249 was 4249, checked in by sam, 5 years ago

Fix the FreeBSD port by not fuzzing the srget() output unless it's called
directly.

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