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

Last change on this file since 4249 was 4249, checked in by Sam Hocevar, 11 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: lib-stream.c 4249 2010-01-12 23:53:38Z sam $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  load-stream.c: loaded stream functions
17 */
18
19#include "config.h"
20
21/* Needed for getline() and getdelim() */
22#define _GNU_SOURCE
23/* Needed for getc_unlocked() on OpenSolaris */
24#define __EXTENSIONS__
25
26/* Define 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.