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

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

Fix compilation warning due to kfreebsd’s fcntl.h defining FREAD.

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