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

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

We need to know the FILE buffer's current fill status. Implemented for
glibc for now.

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