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

Last change on this file since 2330 was 2330, checked in by sam, 6 years ago
  • Implement getc_unlocked() and fgetc_unlocked().
  • Property svn:keywords set to Id
File size: 15.1 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id$
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#define _GNU_SOURCE /* for getline() and getdelim() */
22
23#if defined HAVE_STDINT_H
24#   include <stdint.h>
25#elif defined HAVE_INTTYPES_H
26#   include <inttypes.h>
27#endif
28#include <stdlib.h>
29
30#include <stdio.h>
31#include <sys/types.h>
32#if defined HAVE___SREFILL && defined HAVE_UNISTD_H
33#   include <unistd.h> /* Needed for __srefill’s lseek() call */
34#endif
35
36#include "libzzuf.h"
37#include "lib-load.h"
38#include "debug.h"
39#include "fuzz.h"
40#include "fd.h"
41
42#if defined HAVE___SREFILL
43int NEW(__srefill)(FILE *fp);
44#endif
45
46/* Library functions that we divert */
47static FILE *  (*ORIG(fopen))    (const char *path, const char *mode);
48#if defined HAVE_FOPEN64
49static FILE *  (*ORIG(fopen64))  (const char *path, const char *mode);
50#endif
51static FILE *  (*ORIG(freopen))  (const char *path, const char *mode,
52                                  FILE *stream);
53static int     (*ORIG(fseek))    (FILE *stream, long offset, int whence);
54#if defined HAVE_FSEEKO
55static int     (*ORIG(fseeko))   (FILE *stream, off_t offset, int whence);
56#endif
57static void    (*ORIG(rewind))   (FILE *stream);
58static size_t  (*ORIG(fread))    (void *ptr, size_t size, size_t nmemb,
59                                  FILE *stream);
60static int     (*ORIG(getc))     (FILE *stream);
61static int     (*ORIG(fgetc))    (FILE *stream);
62#if defined HAVE__IO_GETC
63static int     (*ORIG(_IO_getc)) (FILE *stream);
64#endif
65#if defined HAVE_GETC_UNLOCKED
66static int     (*ORIG(getc_unlocked))  (FILE *stream);
67#endif
68#if defined HAVE_FGETC_UNLOCKED
69static int     (*ORIG(fgetc_unlocked)) (FILE *stream);
70#endif
71static char *  (*ORIG(fgets))    (char *s, int size, FILE *stream);
72static int     (*ORIG(ungetc))   (int c, FILE *stream);
73static int     (*ORIG(fclose))   (FILE *fp);
74
75/* Additional GNUisms */
76#if defined HAVE_GETLINE
77static ssize_t (*ORIG(getline))    (char **lineptr, size_t *n, FILE *stream);
78#endif
79#if defined HAVE_GETDELIM
80static ssize_t (*ORIG(getdelim))   (char **lineptr, size_t *n, int delim,
81                                    FILE *stream);
82#endif
83#if defined HAVE___GETDELIM
84static ssize_t (*ORIG(__getdelim)) (char **lineptr, size_t *n, int delim,
85                                    FILE *stream);
86#endif
87
88/* Additional BSDisms */
89#if defined HAVE_FGETLN
90static char *  (*ORIG(fgetln))    (FILE *stream, size_t *len);
91#endif
92#if defined HAVE___SREFILL
93int            (*ORIG(__srefill)) (FILE *fp);
94#endif
95
96/* Our function wrappers */
97#define FOPEN(fn) \
98    do \
99    { \
100        LOADSYM(fn); \
101        if(!_zz_ready) \
102            return ORIG(fn)(path, mode); \
103        _zz_lock(-1); \
104        ret = ORIG(fn)(path, mode); \
105        _zz_unlock(-1); \
106        if(ret && _zz_mustwatch(path)) \
107        { \
108            int fd = fileno(ret); \
109            _zz_register(fd); \
110            debug("%s(\"%s\", \"%s\") = [%i]", __func__, path, mode, fd); \
111        } \
112    } while(0)
113
114FILE *NEW(fopen)(const char *path, const char *mode)
115{
116    FILE *ret; FOPEN(fopen); return ret;
117}
118
119#if defined HAVE_FOPEN64
120FILE *NEW(fopen64)(const char *path, const char *mode)
121{
122    FILE *ret; FOPEN(fopen64); return ret;
123}
124#endif
125
126FILE *NEW(freopen)(const char *path, const char *mode, FILE *stream)
127{
128    FILE *ret;
129    int fd0 = -1, fd1 = -1, disp = 0;
130
131    LOADSYM(freopen);
132    if(_zz_ready && (fd0 = fileno(stream)) >= 0 && _zz_iswatched(fd0))
133    {
134        _zz_unregister(fd0);
135        disp = 1;
136    }
137
138    _zz_lock(-1);
139    ret = ORIG(freopen)(path, mode, stream);
140    _zz_unlock(-1);
141
142    if(ret && _zz_mustwatch(path))
143    {
144        fd1 = fileno(ret);
145        _zz_register(fd1);
146        disp = 1;
147    }
148
149    if(disp)
150        debug("%s(\"%s\", \"%s\", [%i]) = [%i]", __func__,
151              path, mode, fd0, fd1);
152
153    return ret;
154}
155
156#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
157#   define FSEEK_FUZZ(fn2)
158#else
159#   define FSEEK_FUZZ(fn2) \
160        if(ret == 0) \
161        { \
162            /* FIXME: check what happens when fseek()ing a pipe */ \
163            switch(whence) \
164            { \
165                case SEEK_END: \
166                    offset = fn2(stream); \
167                    /* fall through */ \
168                case SEEK_SET: \
169                    _zz_setpos(fd, offset); \
170                    break; \
171                case SEEK_CUR: \
172                    _zz_addpos(fd, offset); \
173                    break; \
174            } \
175        }
176#endif
177
178#define FSEEK(fn, fn2) \
179    do \
180    { \
181        int fd; \
182        LOADSYM(fn); \
183        fd = fileno(stream); \
184        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
185            return ORIG(fn)(stream, offset, whence); \
186        _zz_lock(fd); \
187        ret = ORIG(fn)(stream, offset, whence); \
188        _zz_unlock(fd); \
189        debug("%s([%i], %lli, %i) = %i", __func__, \
190              fd, (long long int)offset, whence, ret); \
191        FSEEK_FUZZ(fn2) \
192    } while(0)
193
194int NEW(fseek)(FILE *stream, long offset, int whence)
195{
196    int ret; FSEEK(fseek, ftell); return ret;
197}
198
199#if defined HAVE_FSEEKO
200int NEW(fseeko)(FILE *stream, off_t offset, int whence)
201{
202    int ret; FSEEK(fseeko, ftello); return ret;
203}
204#endif
205
206void NEW(rewind)(FILE *stream)
207{
208    int fd;
209
210    LOADSYM(rewind);
211    fd = fileno(stream);
212    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
213    {
214        ORIG(rewind)(stream);
215        return;
216    }
217
218    _zz_lock(fd);
219    ORIG(rewind)(stream);
220    _zz_unlock(fd);
221    debug("%s([%i])", __func__, fd);
222
223#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
224#else
225    /* FIXME: check what happens when rewind()ing a pipe */
226    _zz_setpos(fd, 0);
227#endif
228}
229
230size_t NEW(fread)(void *ptr, size_t size, size_t nmemb, FILE *stream)
231{
232    int64_t pos;
233#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
234#else
235    int64_t newpos;
236#endif
237    size_t ret;
238    int fd;
239
240    LOADSYM(fread);
241    fd = fileno(stream);
242    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
243        return ORIG(fread)(ptr, size, nmemb, stream);
244
245    pos = ftell(stream);
246    _zz_lock(fd);
247    ret = ORIG(fread)(ptr, size, nmemb, stream);
248    _zz_unlock(fd);
249
250#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
251#else
252    newpos = ftell(stream);
253    /* XXX: the number of bytes read is not ret * size, because
254     * a partial read may have advanced the stream pointer. However,
255     * when reading from a pipe ftell() will return 0, and ret * size
256     * is then better than nothing. */
257    if(newpos <= 0)
258    {
259        pos = _zz_getpos(fd);
260        newpos = pos + ret * size;
261    }
262    if(newpos != pos)
263    {
264        char *b = ptr;
265
266        _zz_fuzz(fd, ptr, newpos - pos);
267        _zz_setpos(fd, newpos);
268
269        if(newpos >= pos + 4)
270            debug("%s(%p, %li, %li, [%i]) = %li \"%c%c%c%c...", __func__, ptr,
271                  (long int)size, (long int)nmemb, fd, (long int)ret,
272                  b[0], b[1], b[2], b[3]);
273        else
274            debug("%s(%p, %li, %li, [%i]) = %li \"%c...", __func__, ptr,
275                  (long int)size, (long int)nmemb, fd, (long int)ret, b[0]);
276    }
277    else
278#endif
279        debug("%s(%p, %li, %li, [%i]) = %li", __func__, ptr,
280              (long int)size, (long int)nmemb, fd, (long int)ret);
281
282    return ret;
283}
284
285#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
286#   define FGETC_FUZZ
287#else
288#   define FGETC_FUZZ \
289        if(ret != EOF) \
290        { \
291            uint8_t ch = ret; \
292            _zz_fuzz(fd, &ch, 1); \
293            _zz_addpos(fd, 1); \
294            ret = ch; \
295        }
296#endif
297
298#define FGETC(fn) \
299    do { \
300        int fd; \
301        LOADSYM(fn); \
302        fd = fileno(stream); \
303        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
304            return ORIG(fn)(stream); \
305        _zz_lock(fd); \
306        ret = ORIG(fn)(stream); \
307        _zz_unlock(fd); \
308        FGETC_FUZZ \
309        if(ret == EOF) \
310            debug("%s([%i]) = EOF", __func__, fd); \
311        else \
312            debug("%s([%i]) = '%c'", __func__, fd, ret); \
313    } while(0)
314
315#undef getc /* can be a macro; we don’t want that */
316int NEW(getc)(FILE *stream)
317{
318    int ret; FGETC(getc); return ret;
319}
320
321int NEW(fgetc)(FILE *stream)
322{
323    int ret; FGETC(fgetc); return ret;
324}
325
326#if defined HAVE__IO_GETC
327int NEW(_IO_getc)(FILE *stream)
328{
329    int ret; FGETC(_IO_getc); return ret;
330}
331#endif
332
333#if defined HAVE_GETC_UNLOCKED
334int NEW(getc_unlocked)(FILE *stream)
335{
336    int ret; FGETC(getc_unlocked); return ret;
337}
338#endif
339
340#if defined HAVE_FGETC_UNLOCKED
341int NEW(fgetc_unlocked)(FILE *stream)
342{
343    int ret; FGETC(fgetc_unlocked); return ret;
344}
345#endif
346
347char *NEW(fgets)(char *s, int size, FILE *stream)
348{
349    char *ret = s;
350    int fd;
351
352    LOADSYM(fgets);
353    LOADSYM(fgetc);
354    fd = fileno(stream);
355    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
356        return ORIG(fgets)(s, size, stream);
357
358#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
359    _zz_lock(fd);
360    ret = ORIG(fgets)(s, size, stream);
361    _zz_unlock(fd);
362#else
363    if(size <= 0)
364        ret = NULL;
365    else if(size == 1)
366        s[0] = '\0';
367    else
368    {
369        int i;
370
371        for(i = 0; i < size - 1; i++)
372        {
373            int ch;
374
375            _zz_lock(fd);
376            ch = ORIG(fgetc)(stream);
377            _zz_unlock(fd);
378
379            if(ch == EOF)
380            {
381                s[i] = '\0';
382                if(!i)
383                    ret = NULL;
384                break;
385            }
386            s[i] = (char)(unsigned char)ch;
387            _zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
388            _zz_addpos(fd, 1);
389            if(s[i] == '\n')
390            {
391                s[i + 1] = '\0';
392                break;
393            }
394        }
395    }
396#endif
397
398    debug("%s(%p, %i, [%i]) = %p", __func__, s, size, fd, ret);
399    return ret;
400}
401
402int NEW(ungetc)(int c, FILE *stream)
403{
404    int ret, fd;
405
406    LOADSYM(ungetc);
407    fd = fileno(stream);
408    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
409        return ORIG(ungetc)(c, stream);
410
411    _zz_lock(fd);
412    ret = ORIG(ungetc)(c, stream);
413    _zz_unlock(fd);
414
415    if(ret != EOF)
416    {
417        struct fuzz *fuzz = _zz_getfuzz(fd);
418        fuzz->uflag = 1;
419        fuzz->upos = _zz_getpos(fd) - 1;
420        fuzz->uchar = c;
421#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
422#else
423        _zz_addpos(fd, -1);
424#endif
425    }
426
427    if(ret == EOF)
428        debug("%s(0x%02x, [%i]) = EOF", __func__, c, fd);
429    else
430        debug("%s(0x%02x, [%i]) = '%c'", __func__, c, fd, ret);
431
432    return ret;
433}
434
435int NEW(fclose)(FILE *fp)
436{
437    int ret, fd;
438
439    LOADSYM(fclose);
440    fd = fileno(fp);
441    if(!_zz_ready || !_zz_iswatched(fd))
442        return ORIG(fclose)(fp);
443
444    _zz_lock(fd);
445    ret = ORIG(fclose)(fp);
446    _zz_unlock(fd);
447    debug("%s([%i]) = %i", __func__, fd, ret);
448    _zz_unregister(fd);
449
450    return ret;
451}
452
453#define GETDELIM(fn, delim, need_delim) \
454    do { \
455        char *line; \
456        ssize_t done, size; \
457        int fd, finished = 0; \
458        LOADSYM(fn); \
459        LOADSYM(getdelim); \
460        LOADSYM(fgetc); \
461        fd = fileno(stream); \
462        if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd)) \
463            return ORIG(getdelim)(lineptr, n, delim, stream); \
464        line = *lineptr; \
465        size = line ? *n : 0; \
466        ret = done = finished = 0; \
467        for(;;) \
468        { \
469            int ch; \
470            if(done >= size) /* highly inefficient but I don't care */ \
471                line = realloc(line, size = done + 1); \
472            if(finished) \
473            { \
474                line[done] = '\0'; \
475                *n = size; \
476                *lineptr = line; \
477                break; \
478            } \
479            _zz_lock(fd); \
480            ch = ORIG(fgetc)(stream); \
481            _zz_unlock(fd); \
482            if(ch == EOF) \
483            { \
484                finished = 1; \
485                ret = done; \
486            } \
487            else \
488            { \
489                unsigned char c = ch; \
490                _zz_fuzz(fd, &c, 1); /* even more inefficient */ \
491                line[done++] = c; \
492                _zz_addpos(fd, 1); \
493                if(c == delim) \
494                { \
495                    finished = 1; \
496                    ret = done; \
497                } \
498            } \
499        } \
500        if(need_delim) \
501            debug("%s(%p, %p, '%c', [%i]) = %li", __func__, \
502                  lineptr, n, delim, fd, (long int)ret); \
503        else \
504            debug("%s(%p, %p, [%i]) = %li", __func__, \
505                  lineptr, n, fd, (long int)ret); \
506        return ret; \
507    } while(0)
508
509#if defined HAVE_GETLINE
510ssize_t NEW(getline)(char **lineptr, size_t *n, FILE *stream)
511{
512    ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
513}
514#endif
515
516#if defined HAVE_GETDELIM
517ssize_t NEW(getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
518{
519    ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
520}
521#endif
522
523#if defined HAVE___GETDELIM
524ssize_t NEW(__getdelim)(char **lineptr, size_t *n, int delim, FILE *stream)
525{
526    ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
527}
528#endif
529
530#if defined HAVE_FGETLN
531char *NEW(fgetln)(FILE *stream, size_t *len)
532{
533    char *ret;
534#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
535#else
536    struct fuzz *fuzz;
537    size_t i, size;
538#endif
539    int fd;
540
541    LOADSYM(fgetln);
542    LOADSYM(fgetc);
543    fd = fileno(stream);
544    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
545        return ORIG(fgetln)(stream, len);
546
547#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
548    _zz_lock(fd);
549    ret = ORIG(fgetln)(stream, len);
550    _zz_unlock(fd);
551#else
552    fuzz = _zz_getfuzz(fd);
553
554    for(i = size = 0; ; /* i is incremented below */)
555    {
556        int ch;
557
558        _zz_lock(fd);
559        ch = ORIG(fgetc)(stream);
560        _zz_unlock(fd);
561
562        if(ch == EOF)
563            break;
564
565        if(i >= size)
566            fuzz->tmp = realloc(fuzz->tmp, (size += 80));
567
568        fuzz->tmp[i] = (char)(unsigned char)ch;
569        _zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
570        _zz_addpos(fd, 1);
571
572        if(fuzz->tmp[i++] == '\n')
573            break;
574    }
575
576    *len = i;
577    ret = fuzz->tmp;
578#endif
579
580    debug("%s([%i], &%li) = %p", __func__, fd, (long int)*len, ret);
581    return ret;
582}
583#endif
584
585#if defined HAVE___SREFILL
586int NEW(__srefill)(FILE *fp)
587{
588    off_t newpos;
589    int ret, fd, tmp;
590
591    LOADSYM(__srefill);
592    fd = fileno(fp);
593    if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
594        return ORIG(__srefill)(fp);
595
596    _zz_lock(fd);
597    ret = ORIG(__srefill)(fp);
598    newpos = lseek(fd, 0, SEEK_CUR);
599    _zz_unlock(fd);
600    if(ret != EOF)
601    {
602        if(newpos != -1)
603            _zz_setpos(fd, newpos - fp->_r);
604        _zz_fuzz(fd, fp->_p, fp->_r);
605        _zz_addpos(fd, fp->_r);
606    }
607
608    if(!_zz_islocked(fd))
609        debug("%s([%i]) = %i", __func__, fd, ret);
610
611    return ret;
612}
613#endif
614
Note: See TracBrowser for help on using the repository browser.