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

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

Fix copyright information and remove Id tag everywhere.

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