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

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

Fix the OpenBSD port. On this platform, weird stuff happens such as
fseek() calling fseeko(). Fortunately, we already have the mechanisms
for those situations.

  • Property svn:keywords set to Id
File size: 30.2 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  load-stream.c: loaded stream functions
15 */
16
17#include "config.h"
18
19/* Needed for getline() and getdelim() */
20#define _GNU_SOURCE
21/* Needed for getc_unlocked() on OpenSolaris */
22#define __EXTENSIONS__
23
24/* Define the best ftell() clone */
25#if defined HAVE_FTELLO64
26#   define ZZ_FTELL ftello64
27#elif defined HAVE___FTELLO64
28#   define ZZ_FTELL __ftello64
29#elif defined HAVE_FTELLO
30#   define ZZ_FTELL ftello
31#else
32#   define ZZ_FTELL ftell
33#endif
34
35#if defined HAVE_STDINT_H
36#   include <stdint.h>
37#elif defined HAVE_INTTYPES_H
38#   include <inttypes.h>
39#endif
40#include <stdlib.h>
41
42#include <stdio.h>
43#include <sys/types.h>
44#if defined HAVE_UNISTD_H
45#   include <unistd.h> /* Needed for __srefill’s lseek() call */
46#endif
47
48#include "common.h"
49#include "libzzuf.h"
50#include "lib-load.h"
51#include "debug.h"
52#include "fuzz.h"
53#include "fd.h"
54
55#if defined HAVE___SREFILL
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             || _zz_islocked(fd)) \
335            return ORIG(myfseek)(stream, offset, whence); \
336        debug_stream("before", stream); \
337        /* FIXME: ftell() will return -1 on a pipe such as stdin */ \
338        oldpos = ZZ_FTELL(stream); \
339        oldoff = get_stream_off(stream); \
340        oldcnt = get_stream_cnt(stream); \
341        _zz_lock(fd); \
342        ret = ORIG(myfseek)(stream, offset, whence); \
343        _zz_unlock(fd); \
344        newpos = ZZ_FTELL(stream); \
345        if (newpos >= oldpos + oldcnt || newpos < oldpos - oldoff) \
346        { \
347            _zz_setpos(fd, newpos - get_stream_off(stream)); \
348            _zz_fuzz(fd, get_stream_ptr(stream) - get_stream_off(stream), \
349                         get_stream_cnt(stream) + get_stream_off(stream)); \
350        } \
351        _zz_setpos(fd, newpos); \
352        debug_stream("after", stream); \
353        debug("%s([%i], %lli, %s) = %i", __func__, \
354              fd, (long long int)offset, get_seek_mode_name(whence), ret); \
355    } while(0)
356
357#define ZZ_FSETPOS(myfsetpos) \
358    do \
359    { \
360        int64_t oldpos, newpos; \
361        int oldoff, oldcnt; \
362        int fd; \
363        LOADSYM(myfsetpos); \
364        fd = fileno(stream); \
365        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd) \
366             || _zz_islocked(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             || _zz_islocked(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             || _zz_islocked(fd)) \
486            return ORIG(myfread)(ptr, size, nmemb, stream); \
487        debug_stream("before", stream); \
488        /* FIXME: ftell() will return -1 on a pipe such as stdin */ \
489        oldpos = ZZ_FTELL(stream); \
490        oldoff = get_stream_off(stream); \
491        oldcnt = get_stream_cnt(stream); \
492        _zz_lock(fd); \
493        ret = ORIG(myfread)(ptr, size, nmemb, stream); \
494        _zz_unlock(fd); \
495        newpos = ZZ_FTELL(stream); \
496        if (newpos >= oldpos + oldcnt) \
497        { \
498            /* Fuzz returned data that wasn't in the old internal buffer */ \
499            _zz_setpos(fd, oldpos + oldcnt); \
500            _zz_fuzz(fd, b + oldcnt, newpos - oldpos - oldcnt); \
501            /* Fuzz the internal stream buffer */ \
502            _zz_setpos(fd, newpos - get_stream_off(stream)); \
503            _zz_fuzz(fd, get_stream_ptr(stream) - get_stream_off(stream), \
504                         get_stream_cnt(stream) + get_stream_off(stream)); \
505        } \
506        _zz_setpos(fd, newpos); \
507        debug_stream("after", stream); \
508        if (newpos >= oldpos + 4) \
509            debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, \
510                  ptr, (long int)size, (long int)nmemb, fd, \
511                  (long int)ret, b[0], b[1], b[2], b[3]); \
512        else if (newpos > oldpos) \
513            debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr, \
514                  (long int)size, (long int)nmemb, fd, \
515                  (long int)ret, b[0]); \
516        else \
517            debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr, \
518                  (long int)size, (long int)nmemb, fd, (long int)ret); \
519    } while(0)
520
521size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
522{
523    size_t ret; ZZ_FREAD(fread); return ret;
524}
525
526#if defined HAVE_FREAD_UNLOCKED
527#undef fread_unlocked /* can be a macro; we don’t want that */
528size_t NEW(fread_unlocked)(void *ptr, size_t size, size_t nmemb, FILE *stream)
529{
530    size_t ret; ZZ_FREAD(fread_unlocked); return ret;
531}
532#endif
533
534/*
535 * getc, getchar, fgetc etc.
536 *
537 * Strategy: we store the previous file position and internal buffer
538 * status, then call the original function. If the new file position
539 * lies outside the previous internal buffer, it means the buffer has
540 * been invalidated, so we fuzz whatever's preloaded in it.
541 */
542
543#define ZZ_FGETC(myfgetc, s, arg) \
544    do { \
545        int64_t oldpos, newpos; \
546        int oldoff, oldcnt; \
547        int fd; \
548        LOADSYM(myfgetc); \
549        fd = fileno(s); \
550        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd) \
551             || _zz_islocked(fd)) \
552            return ORIG(myfgetc)(arg); \
553        debug_stream("before", s); \
554        oldpos = ZZ_FTELL(s); \
555        oldoff = get_stream_off(s); \
556        oldcnt = get_stream_cnt(s); \
557        _zz_lock(fd); \
558        ret = ORIG(myfgetc)(arg); \
559        _zz_unlock(fd); \
560        newpos = ZZ_FTELL(s); \
561        if (oldcnt == 0 && ret != EOF) \
562        { \
563            /* Fuzz returned data that wasn't in the old internal buffer */ \
564            uint8_t ch = ret; \
565            _zz_setpos(fd, oldpos); \
566            _zz_fuzz(fd, &ch, 1); \
567            ret = ch; \
568        } \
569        if (newpos >= oldpos + oldcnt) \
570        { \
571            /* Fuzz the internal stream buffer */ \
572            _zz_setpos(fd, newpos - get_stream_off(s)); \
573            _zz_fuzz(fd, get_stream_ptr(s) - get_stream_off(s), \
574                         get_stream_cnt(s) + get_stream_off(s)); \
575        } \
576        _zz_setpos(fd, newpos); \
577        debug_stream("after", s); \
578        if(ret == EOF) \
579            debug("%s([%i]) = EOF", __func__, fd); \
580        else \
581            debug("%s([%i]) = '%c'", __func__, fd, ret); \
582    } while(0)
583
584#undef getc /* can be a macro; we don’t want that */
585int NEW(getc)(FILE *stream)
586{
587    int ret; ZZ_FGETC(getc, stream, stream); return ret;
588}
589
590#undef getchar /* can be a macro; we don’t want that */
591int NEW(getchar)(void)
592{
593    int ret; ZZ_FGETC(getchar, stdin, /* empty */); return ret;
594}
595
596int NEW(fgetc)(FILE *stream)
597{
598    int ret; ZZ_FGETC(fgetc, stream, stream); return ret;
599}
600
601#if defined HAVE__IO_GETC
602int NEW(_IO_getc)(FILE *stream)
603{
604    int ret; ZZ_FGETC(_IO_getc, stream, stream); return ret;
605}
606#endif
607
608#if defined HAVE_GETC_UNLOCKED
609#undef getc_unlocked /* can be a macro; we don’t want that */
610int NEW(getc_unlocked)(FILE *stream)
611{
612    int ret; ZZ_FGETC(getc_unlocked, stream, stream); return ret;
613}
614#endif
615
616#if defined HAVE_GETCHAR_UNLOCKED
617#undef getchar_unlocked /* can be a macro; we don’t want that */
618int NEW(getchar_unlocked)(void)
619{
620    int ret; ZZ_FGETC(getchar_unlocked, stdin, /* empty */); return ret;
621}
622#endif
623
624#if defined HAVE_FGETC_UNLOCKED
625#undef fgetc_unlocked /* can be a macro; we don’t want that */
626int NEW(fgetc_unlocked)(FILE *stream)
627{
628    int ret; ZZ_FGETC(fgetc_unlocked, stream, stream); return ret;
629}
630#endif
631
632/*
633 * fgets, fgets_unlocked
634 */
635
636#define ZZ_FGETS(myfgets, myfgetc) \
637    do \
638    { \
639        int64_t oldpos, newpos; \
640        int oldoff, oldcnt; \
641        int fd; \
642        ret = s; \
643        LOADSYM(myfgets); \
644        LOADSYM(myfgetc); \
645        fd = fileno(stream); \
646        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd) \
647             || _zz_islocked(fd)) \
648            return ORIG(myfgets)(s, size, stream); \
649        debug_stream("before", stream); \
650        oldpos = ZZ_FTELL(stream); \
651        oldoff = get_stream_off(stream); \
652        oldcnt = get_stream_cnt(stream); \
653        newpos = oldpos; \
654        if(size <= 0) \
655            ret = NULL; \
656        else if(size == 1) \
657            s[0] = '\0'; \
658        else \
659        { \
660            int i; \
661            for(i = 0; i < size - 1; i++) \
662            { \
663                int chr; \
664                _zz_lock(fd); \
665                chr = ORIG(myfgetc)(stream); \
666                _zz_unlock(fd); \
667                newpos = oldpos + 1; \
668                if (oldcnt == 0 && chr != EOF) \
669                { \
670                    /* Fuzz returned data that wasn't in the old buffer */ \
671                    uint8_t ch = chr; \
672                    _zz_setpos(fd, oldpos); \
673                    _zz_fuzz(fd, &ch, 1); \
674                    chr = ch; \
675                } \
676                if (newpos >= oldpos + oldcnt) \
677                { \
678                    /* Fuzz the internal stream buffer, if necessary */ \
679                    _zz_setpos(fd, newpos - get_stream_off(stream)); \
680                    _zz_fuzz(fd, get_stream_ptr(stream) - get_stream_off(stream), \
681                                 get_stream_cnt(stream) + get_stream_off(stream)); \
682                } \
683                oldpos = newpos; \
684                oldoff = get_stream_off(stream); \
685                oldcnt = get_stream_cnt(stream); \
686                if(chr == EOF) \
687                { \
688                    s[i] = '\0'; \
689                    if(!i) \
690                        ret = NULL; \
691                    break; \
692                } \
693                s[i] = (char)(unsigned char)chr; \
694                if(s[i] == '\n') \
695                { \
696                    s[i + 1] = '\0'; \
697                    break; \
698                } \
699            } \
700        } \
701        _zz_setpos(fd, newpos); \
702        debug_stream("after", stream); \
703        debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret); \
704    } while(0)
705
706char *NEW(fgets)(char *s, int size, FILE *stream)
707{
708    char *ret; ZZ_FGETS(fgets, fgetc); return ret;
709}
710
711#if defined HAVE_FGETS_UNLOCKED
712char *NEW(fgets_unlocked)(char *s, int size, FILE *stream)
713{
714    char *ret; ZZ_FGETS(fgets_unlocked, fgetc_unlocked); return ret;
715}
716#endif
717
718/*
719 * ungetc
720 */
721
722int NEW(ungetc)(int c, FILE *stream)
723{
724    int oldpos, ret, fd;
725
726    LOADSYM(ungetc);
727    fd = fileno(stream);
728    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)
729         || _zz_islocked(fd))
730        return ORIG(ungetc)(c, stream);
731
732    debug_stream("before", stream);
733    oldpos = ZZ_FTELL(stream);
734    _zz_lock(fd);
735    ret = ORIG(ungetc)(c, stream);
736    _zz_unlock(fd);
737    _zz_setpos(fd, oldpos - 1);
738
739    debug_stream("after", stream);
740    if(ret == EOF)
741        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
742    else
743        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
744    return ret;
745}
746
747/*
748 * fclose
749 */
750
751int NEW(fclose)(FILE *fp)
752{
753    int ret, fd;
754
755    LOADSYM(fclose);
756    fd = fileno(fp);
757    if(!_zz_ready || !_zz_iswatched(fd))
758        return ORIG(fclose)(fp);
759
760    debug_stream("before", fp);
761    _zz_lock(fd);
762    ret = ORIG(fclose)(fp);
763    _zz_unlock(fd);
764    debug("%s([%i]) = %i", __func__, fd, ret);
765    _zz_unregister(fd);
766
767    return ret;
768}
769
770/*
771 * getline, getdelim etc.
772 */
773
774#define ZZ_GETDELIM(mygetdelim, delim, need_delim) \
775    do { \
776        int64_t oldpos, newpos; \
777        char *line; \
778        ssize_t done, size; \
779        int oldoff, oldcnt; \
780        int fd, finished = 0; \
781        LOADSYM(mygetdelim); \
782        LOADSYM(getdelim); \
783        LOADSYM(fgetc); \
784        fd = fileno(stream); \
785        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd) \
786             || _zz_islocked(fd)) \
787            return ORIG(getdelim)(lineptr, n, delim, stream); \
788        debug_stream("before", stream); \
789        oldpos = ZZ_FTELL(stream); \
790        oldoff = get_stream_off(stream); \
791        oldcnt = get_stream_cnt(stream); \
792        newpos = oldpos; \
793        line = *lineptr; \
794        size = line ? *n : 0; \
795        ret = done = finished = 0; \
796        for(;;) \
797        { \
798            int chr; \
799            if(done >= size) /* highly inefficient but I don't care */ \
800                line = realloc(line, size = done + 1); \
801            if(finished) \
802            { \
803                line[done] = '\0'; \
804                *n = size; \
805                *lineptr = line; \
806                break; \
807            } \
808            _zz_lock(fd); \
809            chr = ORIG(fgetc)(stream); \
810            _zz_unlock(fd); \
811            newpos = oldpos + 1; \
812            if (oldcnt == 0 && chr != EOF) \
813            { \
814                /* Fuzz returned data that wasn't in the old buffer */ \
815                uint8_t ch = chr; \
816                _zz_setpos(fd, oldpos); \
817                _zz_fuzz(fd, &ch, 1); \
818                chr = ch; \
819            } \
820            if (newpos >= oldpos + oldcnt) \
821            { \
822                /* Fuzz the internal stream buffer, if necessary */ \
823                _zz_setpos(fd, newpos - get_stream_off(stream)); \
824                _zz_fuzz(fd, get_stream_ptr(stream) - get_stream_off(stream), \
825                             get_stream_cnt(stream) + get_stream_off(stream)); \
826            } \
827            oldpos = newpos; \
828            oldoff = get_stream_off(stream); \
829            oldcnt = get_stream_cnt(stream); \
830            if(chr == EOF) \
831            { \
832                finished = 1; \
833                ret = done ? done : -1; \
834            } \
835            else \
836            { \
837                unsigned char c = chr; \
838                line[done++] = c; \
839                if(c == delim) \
840                { \
841                    finished = 1; \
842                    ret = done; \
843                } \
844            } \
845        } \
846        _zz_setpos(fd, newpos); \
847        debug_stream("after", stream); \
848        if(need_delim) \
849            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
850                  lineptr, n, delim, fd, (long int)ret); \
851        else \
852            debug("%s(%p, %p, [%i]) = %li", __func__, \
853                  lineptr, n, fd, (long int)ret); \
854        break; \
855    } while(0)
856
857#if defined HAVE_GETLINE
858ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
859{
860    ssize_t ret; ZZ_GETDELIM(getline, '\n', 0); return ret;
861}
862#endif
863
864#if defined HAVE_GETDELIM
865ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
866{
867    ssize_t ret; ZZ_GETDELIM(getdelim, delim, 1); return ret;
868}
869#endif
870
871#if defined HAVE___GETDELIM
872ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
873{
874    ssize_t ret; ZZ_GETDELIM(__getdelim, delim, 1); return ret;
875}
876#endif
877
878/*
879 * fgetln
880 */
881
882#if defined HAVE_FGETLN
883char *NEW(fgetln)(FILE *stream, size_t *len)
884{
885    int64_t oldpos, newpos;
886    char *ret;
887    struct fuzz *fuzz;
888    size_t i, size;
889    int oldoff, oldcnt, fd;
890
891    LOADSYM(fgetln);
892    LOADSYM(fgetc);
893    fd = fileno(stream);
894    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)
895         || _zz_islocked(fd))
896        return ORIG(fgetln)(stream, len);
897
898    debug_stream("before", stream);
899    oldpos = ZZ_FTELL(stream);
900    oldoff = get_stream_off(stream);
901    oldcnt = get_stream_cnt(stream);
902    newpos = oldpos;
903
904    fuzz = _zz_getfuzz(fd);
905
906    for(i = size = 0; ; /* i is incremented below */)
907    {
908        int chr;
909
910        _zz_lock(fd);
911        chr = ORIG(fgetc)(stream);
912        _zz_unlock(fd);
913
914        newpos = oldpos + 1;
915        if (oldcnt == 0 && chr != EOF)
916        {
917            /* Fuzz returned data that wasn't in the old buffer */
918            uint8_t ch = chr;
919            _zz_setpos(fd, oldpos);
920            _zz_fuzz(fd, &ch, 1);
921            chr = ch;
922        }
923        if (newpos >= oldpos + oldcnt)
924        {
925            /* Fuzz the internal stream buffer, if necessary */
926            _zz_setpos(fd, newpos - get_stream_off(stream));
927            _zz_fuzz(fd, get_stream_ptr(stream) - get_stream_off(stream),
928                         get_stream_cnt(stream) + get_stream_off(stream));
929        }
930        oldpos = newpos;
931        oldoff = get_stream_off(stream);
932        oldcnt = get_stream_cnt(stream);
933
934        if(chr == EOF)
935            break;
936
937        if(i >= size)
938            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
939
940        fuzz->tmp[i] = (char)(unsigned char)chr;
941
942        if(fuzz->tmp[i++] == '\n')
943            break;
944    }
945
946    *len = i;
947    ret = fuzz->tmp;
948
949    debug_stream("after", stream);
950    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
951    return ret;
952}
953#endif
954
955/*
956 * __srefill, __filbuf, __srget, __uflow
957 */
958
959#if defined HAVE___UFLOW
960#   define REFILL_RETURNS_INT 0
961#else
962#   define REFILL_RETURNS_INT 1
963#endif
964
965#define ZZ_REFILL(myrefill, fn_advances) \
966    do \
967    { \
968        int64_t pos; \
969        off_t newpos; \
970        int fd; \
971        LOADSYM(myrefill); \
972        fd = fileno(fp); \
973        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd) \
974             || _zz_islocked(fd)) \
975            return ORIG(myrefill)(fp); \
976        debug_stream("before", fp); \
977        pos = _zz_getpos(fd); \
978        _zz_lock(fd); \
979        ret = ORIG(myrefill)(fp); \
980        newpos = lseek(fd, 0, SEEK_CUR); \
981        _zz_unlock(fd); \
982        if(ret != EOF) \
983        { \
984            int already_fuzzed = 0; \
985            if(fn_advances) \
986            { \
987                uint8_t ch = (uint8_t)(unsigned int)ret; \
988                if(newpos != -1) \
989                    _zz_setpos(fd, newpos - get_stream_cnt(fp) - 1); \
990                already_fuzzed = _zz_getfuzzed(fd); \
991                _zz_fuzz(fd, &ch, 1); \
992                ret = get_stream_ptr(fp)[-1] = ch; \
993                _zz_setfuzzed(fd, get_stream_cnt(fp) + 1); \
994                _zz_addpos(fd, 1); \
995            } \
996            else \
997            { \
998                _zz_setfuzzed(fd, get_stream_cnt(fp)); \
999                if(newpos != -1) \
1000                    _zz_setpos(fd, newpos - get_stream_cnt(fp)); \
1001            } \
1002            if(get_stream_cnt(fp) > already_fuzzed) \
1003            { \
1004                _zz_addpos(fd, already_fuzzed); \
1005                _zz_fuzz(fd, get_stream_ptr(fp), \
1006                             get_stream_cnt(fp) - already_fuzzed); \
1007            } \
1008            _zz_addpos(fd, get_stream_cnt(fp) - already_fuzzed); \
1009        } \
1010        _zz_setpos(fd, pos); /* FIXME: do we always need to do this? */ \
1011        debug_stream("after", fp); \
1012        if (REFILL_RETURNS_INT) \
1013            debug("%s([%i]) = %i", __func__, fd, ret); \
1014        else if (ret == EOF) \
1015            debug("%s([%i]) = EOF", __func__, fd); \
1016        else \
1017            debug("%s([%i]) = '%c'", __func__, fd, ret); \
1018    } \
1019    while(0)
1020
1021#if defined HAVE___SREFILL
1022int NEW(__srefill)(FILE *fp)
1023{
1024    int ret; ZZ_REFILL(__srefill, 0); return ret;
1025}
1026#endif
1027
1028#if defined HAVE___SRGET && !defined HAVE___SREFILL
1029int NEW(__srget)(FILE *fp)
1030{
1031    int ret; ZZ_REFILL(__srget, 1); return ret;
1032}
1033#endif
1034
1035#if defined HAVE___FILBUF
1036int NEW(__filbuf)(FILE *fp)
1037{
1038    int ret; ZZ_REFILL(__filbuf, 1); return ret;
1039}
1040#endif
1041
1042#if defined HAVE___UFLOW
1043int NEW(__uflow)(FILE *fp)
1044{
1045    int ret; ZZ_REFILL(__uflow, 1); return ret;
1046}
1047#endif
1048
Note: See TracBrowser for help on using the repository browser.