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

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

Improve the OpenSolaris? port: support for FILE structures, and a few
additional defines to activate libc features.

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