Files
zzuf/src/load-stream.c
2007-01-07 15:55:00 +00:00

592 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* zzuf - general purpose fuzzer
* Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
* All Rights Reserved
*
* $Id$
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/
/*
* load-stream.c: loaded stream functions
*/
#include "config.h"
#define _GNU_SOURCE /* for getline() and getdelim() */
#if defined HAVE_STDINT_H
# include <stdint.h>
#elif defined HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <stdlib.h>
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
#include "libzzuf.h"
#include "debug.h"
#include "fuzz.h"
#include "load.h"
#ifdef HAVE___SREFILL
int __srefill(FILE *fp);
#endif
/* Library functions that we divert */
static FILE * (*fopen_orig) (const char *path, const char *mode);
#ifdef HAVE_FOPEN64
static FILE * (*fopen64_orig) (const char *path, const char *mode);
#endif
static int (*fseek_orig) (FILE *stream, long offset, int whence);
#ifdef HAVE_FSEEKO
static int (*fseeko_orig) (FILE *stream, off_t offset, int whence);
#endif
static void (*rewind_orig) (FILE *stream);
static size_t (*fread_orig) (void *ptr, size_t size, size_t nmemb,
FILE *stream);
static int (*getc_orig) (FILE *stream);
static int (*fgetc_orig) (FILE *stream);
#ifdef HAVE__IO_GETC
static int (*_IO_getc_orig) (FILE *stream);
#endif
static char * (*fgets_orig) (char *s, int size, FILE *stream);
static int (*ungetc_orig) (int c, FILE *stream);
static int (*fclose_orig) (FILE *fp);
/* Additional GNUisms */
#ifdef HAVE_GETLINE
static ssize_t (*getline_orig) (char **lineptr, size_t *n, FILE *stream);
#endif
#ifdef HAVE_GETDELIM
static ssize_t (*getdelim_orig) (char **lineptr, size_t *n, int delim,
FILE *stream);
#endif
#ifdef HAVE___GETDELIM
static ssize_t (*__getdelim_orig) (char **lineptr, size_t *n, int delim,
FILE *stream);
#endif
/* Additional BSDisms */
#ifdef HAVE_FGETLN
static char * (*fgetln_orig) (FILE *stream, size_t *len);
#endif
#ifdef HAVE___SREFILL
int (*__srefill_orig) (FILE *fp);
#endif
void _zz_load_stream(void)
{
LOADSYM(fopen);
#ifdef HAVE_FOPEN64
LOADSYM(fopen64);
#endif
LOADSYM(fseek);
#ifdef HAVE_FSEEKO
LOADSYM(fseeko);
#endif
LOADSYM(rewind);
LOADSYM(fread);
LOADSYM(getc);
LOADSYM(fgetc);
#ifdef HAVE__IO_GETC
LOADSYM(_IO_getc);
#endif
LOADSYM(fgets);
LOADSYM(ungetc);
LOADSYM(fclose);
#ifdef HAVE_GETLINE
LOADSYM(getline);
#endif
#ifdef HAVE_GETDELIM
LOADSYM(getdelim);
#endif
#ifdef HAVE___GETDELIM
LOADSYM(__getdelim);
#endif
#ifdef HAVE_FGETLN
LOADSYM(fgetln);
#endif
#ifdef HAVE___SREFILL
LOADSYM(__srefill);
#endif
}
/* Our function wrappers */
#define FOPEN(fn) \
do \
{ \
if(!_zz_ready) \
{ \
LOADSYM(fn); \
return ORIG(fn)(path, mode); \
} \
_zz_disabled = 1; \
ret = ORIG(fn)(path, mode); \
_zz_disabled = 0; \
if(ret && _zz_mustwatch(path)) \
{ \
int fd = fileno(ret); \
_zz_register(fd); \
debug(STR(fn) "(\"%s\", \"%s\") = [%i]", path, mode, fd); \
} \
} while(0)
FILE *fopen(const char *path, const char *mode)
{
FILE *ret; FOPEN(fopen); return ret;
}
#ifdef HAVE_FOPEN64
FILE *fopen64(const char *path, const char *mode)
{
FILE *ret; FOPEN(fopen64); return ret;
}
#endif
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
# define FSEEK_FUZZ(fn2)
#else
# define FSEEK_FUZZ(fn2) \
if(ret == 0) \
{ \
/* FIXME: check what happens when fseek()ing a pipe */ \
switch(whence) \
{ \
case SEEK_END: \
offset = fn2(stream); \
/* fall through */ \
case SEEK_SET: \
_zz_setpos(fd, offset); \
break; \
case SEEK_CUR: \
_zz_addpos(fd, offset); \
break; \
} \
}
#endif
#define FSEEK(fn, fn2) \
do \
{ \
int fd; \
if(!_zz_ready) \
LOADSYM(fn); \
fd = fileno(stream); \
if(!_zz_ready || !_zz_iswatched(fd)) \
return ORIG(fn)(stream, offset, whence); \
_zz_disabled = 1; \
ret = ORIG(fn)(stream, offset, whence); \
_zz_disabled = 0; \
debug(STR(fn)"([%i], %lli, %i) = %i", \
fd, (long long int)offset, whence, ret); \
FSEEK_FUZZ(fn2) \
} while(0)
int fseek(FILE *stream, long offset, int whence)
{
int ret; FSEEK(fseek, ftell); return ret;
}
#ifdef HAVE_FSEEKO
int fseeko(FILE *stream, off_t offset, int whence)
{
int ret; FSEEK(fseeko, ftello); return ret;
}
#endif
void rewind(FILE *stream)
{
int fd;
if(!_zz_ready)
LOADSYM(rewind);
fd = fileno(stream);
if(!_zz_ready || !_zz_iswatched(fd))
{
rewind_orig(stream);
return;
}
_zz_disabled = 1;
rewind_orig(stream);
_zz_disabled = 0;
debug("rewind([%i])", fd);
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
/* FIXME: check what happens when rewind()ing a pipe */
_zz_setpos(fd, 0);
#endif
}
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
long int pos;
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
long int newpos;
#endif
size_t ret;
int fd;
if(!_zz_ready)
LOADSYM(fread);
fd = fileno(stream);
if(!_zz_ready || !_zz_iswatched(fd))
return fread_orig(ptr, size, nmemb, stream);
pos = ftell(stream);
_zz_disabled = 1;
ret = fread_orig(ptr, size, nmemb, stream);
_zz_disabled = 0;
debug("fread(%p, %li, %li, [%i]) = %li",
ptr, (long int)size, (long int)nmemb, fd, (long int)ret);
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
newpos = ftell(stream);
/* XXX: the number of bytes read is not ret * size, because
* a partial read may have advanced the stream pointer. However,
* when reading from a pipe ftell() will return 0, and ret * size
* is then better than nothing. */
if(newpos <= 0)
newpos = ret * size;
if(newpos != pos)
{
_zz_fuzz(fd, ptr, newpos - pos);
_zz_setpos(fd, newpos);
}
#endif
return ret;
}
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
# define FGETC_FUZZ
#else
# define FGETC_FUZZ \
if(ret != EOF) \
{ \
uint8_t ch = ret; \
_zz_fuzz(fd, &ch, 1); \
_zz_addpos(fd, 1); \
ret = ch; \
}
#endif
#define FGETC(fn) \
do { \
int fd; \
if(!_zz_ready) \
LOADSYM(fn); \
fd = fileno(stream); \
if(!_zz_ready || !_zz_iswatched(fd)) \
return ORIG(fn)(stream); \
_zz_disabled = 1; \
ret = ORIG(fn)(stream); \
_zz_disabled = 0; \
FGETC_FUZZ \
if(ret >= 0x20 && ret <= 0x7f) \
debug(STR(fn)"([%i]) = 0x%02x '%c'", fd, ret, (char)ret); \
else \
debug(STR(fn)"([%i]) = 0x%02x", fd, ret); \
} while(0)
#undef getc /* can be a macro; we dont want that */
int getc(FILE *stream)
{
int ret; FGETC(getc); return ret;
}
int fgetc(FILE *stream)
{
int ret; FGETC(fgetc); return ret;
}
#ifdef HAVE__IO_GETC
int _IO_getc(FILE *stream)
{
int ret; FGETC(_IO_getc); return ret;
}
#endif
char *fgets(char *s, int size, FILE *stream)
{
char *ret = s;
int fd;
if(!_zz_ready)
LOADSYM(fgets);
fd = fileno(stream);
if(!_zz_ready || !_zz_iswatched(fd))
return fgets_orig(s, size, stream);
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
_zz_disabled = 1;
ret = fgets_orig(s, size, stream);
_zz_disabled = 0;
#else
if(size <= 0)
ret = NULL;
else if(size == 1)
s[0] = '\0';
else
{
int i;
for(i = 0; i < size - 1; i++)
{
int ch;
_zz_disabled = 1;
ch = fgetc_orig(stream);
_zz_disabled = 0;
if(ch == EOF)
{
s[i] = '\0';
if(!i)
ret = NULL;
break;
}
s[i] = (char)(unsigned char)ch;
_zz_fuzz(fd, (uint8_t *)s + i, 1); /* rather inefficient */
_zz_addpos(fd, 1);
if(s[i] == '\n')
{
s[i + 1] = '\0';
break;
}
}
}
#endif
debug("fgets(%p, %i, [%i]) = %p", s, size, fd, ret);
return ret;
}
int ungetc(int c, FILE *stream)
{
unsigned char ch = c;
int ret, fd;
if(!_zz_ready)
LOADSYM(ungetc);
fd = fileno(stream);
if(!_zz_ready || !_zz_iswatched(fd))
return ungetc_orig(c, stream);
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
_zz_addpos(fd, -1);
_zz_fuzz(fd, &ch, 1);
#endif
_zz_disabled = 1;
ret = ungetc_orig((int)ch, stream);
_zz_disabled = 0;
if(ret >= 0)
ret = c;
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
else
_zz_addpos(fd, 1); /* revert what we did */
#endif
if(ret >= 0x20 && ret <= 0x7f)
debug("ungetc(0x%02x, [%i]) = 0x%02x '%c'", c, fd, ret, ret);
else
debug("ungetc(0x%02x, [%i]) = 0x%02x", c, fd, ret);
return ret;
}
int fclose(FILE *fp)
{
int ret, fd;
if(!_zz_ready)
LOADSYM(fclose);
fd = fileno(fp);
if(!_zz_ready || !_zz_iswatched(fd))
return fclose_orig(fp);
_zz_disabled = 1;
ret = fclose_orig(fp);
_zz_disabled = 0;
debug("fclose([%i]) = %i", fd, ret);
_zz_unregister(fd);
return ret;
}
#define GETDELIM(fn, delim, need_delim) \
do { \
char *line; \
ssize_t done, size; \
int fd, finished = 0; \
if(!_zz_ready) \
LOADSYM(fn); \
fd = fileno(stream); \
if(!_zz_ready || !_zz_iswatched(fd)) \
return getdelim_orig(lineptr, n, delim, stream); \
line = *lineptr; \
size = line ? *n : 0; \
ret = done = finished = 0; \
for(;;) \
{ \
int ch; \
if(done >= size) /* highly inefficient but I don't care */ \
line = realloc(line, size = done + 1); \
if(finished) \
{ \
line[done] = '\0'; \
*n = size; \
*lineptr = line; \
break; \
} \
_zz_disabled = 1; \
ch = fgetc_orig(stream); \
_zz_disabled = 0; \
if(ch == EOF) \
{ \
finished = 1; \
ret = done; \
} \
else \
{ \
unsigned char c = ch; \
_zz_fuzz(fd, &c, 1); /* even more inefficient */ \
line[done++] = c; \
_zz_addpos(fd, 1); \
if(c == delim) \
{ \
finished = 1; \
ret = done; \
} \
} \
} \
if(need_delim) \
debug(STR(fn) "(%p, %p, 0x%02x, [%i]) = %li", \
lineptr, n, delim, fd, (long int)ret); \
else \
debug(STR(fn) "(%p, %p, [%i]) = %li", \
lineptr, n, fd, (long int)ret); \
return ret; \
} while(0)
#ifdef HAVE_GETLINE
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
ssize_t ret; GETDELIM(getline, '\n', 0); return ret;
}
#endif
#ifdef HAVE_GETDELIM
ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
{
ssize_t ret; GETDELIM(getdelim, delim, 1); return ret;
}
#endif
#ifdef HAVE___GETDELIM
ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream)
{
ssize_t ret; GETDELIM(__getdelim, delim, 1); return ret;
}
#endif
#ifdef HAVE_FGETLN
char *fgetln(FILE *stream, size_t *len)
{
char *ret;
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
#else
struct fuzz *fuzz;
size_t i, size;
#endif
int fd;
if(!_zz_ready)
LOADSYM(fgetln);
fd = fileno(stream);
if(!_zz_ready || !_zz_iswatched(fd))
return fgetln_orig(stream, len);
#if defined HAVE___SREFILL /* Don't fuzz or seek if we have __srefill() */
_zz_disabled = 1;
ret = fgetln_orig(stream, len);
_zz_disabled = 0;
#else
fuzz = _zz_getfuzz(fd);
for(i = size = 0; ; /* i is incremented below */)
{
int ch;
_zz_disabled = 1;
ch = fgetc_orig(stream);
_zz_disabled = 0;
if(ch == EOF)
break;
if(i >= size)
fuzz->tmp = realloc(fuzz->tmp, (size += 80));
fuzz->tmp[i] = (char)(unsigned char)ch;
_zz_fuzz(fd, (uint8_t *)fuzz->tmp + i, 1); /* rather inefficient */
_zz_addpos(fd, 1);
if(fuzz->tmp[i++] == '\n')
break;
}
*len = i;
ret = fuzz->tmp;
#endif
debug("fgetln([%i], &%li) = %p", fd, (long int)*len, ret);
return ret;
}
#endif
#ifdef HAVE___SREFILL
int __srefill(FILE *fp)
{
off_t oldpos, newpos;
int ret, fd;
if(!_zz_ready)
LOADSYM(__srefill);
fd = fileno(fp);
if(!_zz_ready || !_zz_iswatched(fd))
return __srefill_orig(fp);
oldpos = lseek(fd, 0, SEEK_CUR);
ret = __srefill_orig(fp);
newpos = lseek(fd, 0, SEEK_CUR);
if(ret != EOF)
{
if(newpos != -1)
_zz_setpos(fd, newpos - fp->_r);
_zz_fuzz(fd, fp->_p, fp->_r);
_zz_addpos(fd, fp->_r);
}
if(!_zz_disabled)
debug("__srefill([%i]) = %i", fd, ret);
return ret;
}
#endif