zzuf/src/libzzuf/lib-fd.c
2012-04-16 23:02:42 +00:00

745 lines
20 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-2010 Sam Hocevar <sam@hocevar.net>
* 2007 Rémi Denis-Courmont <rdenis#simphalempin:com>
* 2007 Clément Stenac <zorglub#diwi:org>
* 2007 Dominik Kuhlen <dominik.kuhlen#gmit-gmbh:de>
* 2009 Corentin Delorme <codelorme@gmail.com>
* All Rights Reserved
*
* 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-fd.c: loaded file descriptor functions
*/
#include "config.h"
/* Use this to get lseek64() on glibc systems */
#define _LARGEFILE64_SOURCE
/* Use this to get off64_t() on Solaris systems */
#define _LARGEFILE_SOURCE
/* Use this to get proper prototypes on HP-UX systems */
#define _XOPEN_SOURCE_EXTENDED
#define _INCLUDE_POSIX_SOURCE
#if defined HAVE_STDINT_H
# include <stdint.h>
#elif defined HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#if defined HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#include <sys/types.h>
#if defined HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#if defined HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#if defined HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#if defined HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#if defined HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#include <stdarg.h>
#if defined HAVE_AIO_H
# include <aio.h>
#endif
#include "libzzuf.h"
#include "lib-load.h"
#include "debug.h"
#include "network.h"
#include "fuzz.h"
#include "fd.h"
#if defined HAVE_SOCKLEN_T
# define SOCKLEN_T socklen_t
#else
# define SOCKLEN_T int
#endif
#if defined CONNECT_USES_STRUCT_SOCKADDR
# define SOCKADDR_T struct sockaddr
#else
# define SOCKADDR_T void
#endif
/* Local prototypes */
#if defined HAVE_READV || defined HAVE_RECVMSG
static void fuzz_iovec (int fd, const struct iovec *iov, ssize_t ret);
#endif
static void offset_check (int fd);
/* Library functions that we divert */
static int (*ORIG(open)) (const char *file, int oflag, ...);
#if defined HAVE_OPEN64
static int (*ORIG(open64)) (const char *file, int oflag, ...);
#endif
#if defined HAVE___OPEN64
static int (*ORIG(__open64))(const char *file, int oflag, ...);
#endif
#if defined HAVE_DUP
static int (*ORIG(dup)) (int oldfd);
#endif
#if defined HAVE_DUP2
static int (*ORIG(dup2)) (int oldfd, int newfd);
#endif
#if defined HAVE_ACCEPT
static int (*ORIG(accept)) (int sockfd, SOCKADDR_T *addr,
SOCKLEN_T *addrlen);
#endif
#if defined HAVE_BIND
static int (*ORIG(bind)) (int sockfd, const SOCKADDR_T *my_addr,
SOCKLEN_T addrlen);
#endif
#if defined HAVE_CONNECT
static int (*ORIG(connect)) (int sockfd, const SOCKADDR_T *serv_addr,
SOCKLEN_T addrlen);
#endif
#if defined HAVE_SOCKET
static int (*ORIG(socket)) (int domain, int type, int protocol);
#endif
#if defined HAVE_RECV
static RECV_T (*ORIG(recv)) (int s, void *buf, size_t len, int flags);
#endif
#if defined HAVE___RECV_CHK
static RECV_T (*ORIG(__recv_chk)) (int s, void *buf, size_t len,
size_t buflen, int flags);
#endif
#if defined HAVE_RECVFROM
static RECV_T (*ORIG(recvfrom))(int s, void *buf, size_t len, int flags,
SOCKADDR_T *from, SOCKLEN_T *fromlen);
#endif
#if defined HAVE___RECVFROM_CHK
static RECV_T (*ORIG(__recvfrom_chk))(int s, void *buf, size_t len,
size_t buflen, int flags,
SOCKADDR_T *from, SOCKLEN_T *fromlen);
#endif
#if defined HAVE_RECVMSG
static RECV_T (*ORIG(recvmsg)) (int s, struct msghdr *hdr, int flags);
#endif
#if defined READ_USES_SSIZE_T
static ssize_t (*ORIG(read)) (int fd, void *buf, size_t count);
#else
static int (*ORIG(read)) (int fd, void *buf, unsigned int count);
#endif
#if defined HAVE___READ_CHK
static ssize_t (*ORIG(__read_chk)) (int fd, void *buf, size_t count,
size_t buflen);
#endif
#if defined HAVE_READV
static ssize_t (*ORIG(readv)) (int fd, const struct iovec *iov, int count);
#endif
#if defined HAVE_PREAD
static ssize_t (*ORIG(pread)) (int fd, void *buf, size_t count, off_t offset);
#endif
#if defined HAVE_AIO_READ
static int (*ORIG(aio_read)) (struct aiocb *aiocbp);
static ssize_t (*ORIG(aio_return)) (struct aiocb *aiocbp);
#endif
static off_t (*ORIG(lseek)) (int fd, off_t offset, int whence);
#if defined HAVE_LSEEK64
static off64_t (*ORIG(lseek64)) (int fd, off64_t offset, int whence);
#endif
#if defined HAVE___LSEEK64
static off64_t (*ORIG(__lseek64)) (int fd, off64_t offset, int whence);
#endif
static int (*ORIG(close)) (int fd);
#define ZZ_OPEN(myopen) \
do \
{ \
int mode = 0; \
LOADSYM(myopen); \
if(oflag & O_CREAT) \
{ \
va_list va; \
va_start(va, oflag); \
mode = va_arg(va, int); \
va_end(va); \
ret = ORIG(myopen)(file, oflag, mode); \
} \
else \
{ \
ret = ORIG(myopen)(file, oflag); \
} \
if(!_zz_ready || _zz_islocked(-1)) \
return ret; \
if(ret >= 0 \
&& ((oflag & (O_RDONLY | O_RDWR | O_WRONLY)) != O_WRONLY) \
&& _zz_mustwatch(file)) \
{ \
if(oflag & O_CREAT) \
debug("%s(\"%s\", %i, %i) = %i", \
__func__, file, oflag, mode, ret); \
else \
debug("%s(\"%s\", %i) = %i", __func__, file, oflag, ret); \
_zz_register(ret); \
} \
} while(0)
#undef open
int NEW(open)(const char *file, int oflag, ...)
{
int ret; ZZ_OPEN(open); return ret;
}
#if defined HAVE_OPEN64
#undef open64
int NEW(open64)(const char *file, int oflag, ...)
{
int ret; ZZ_OPEN(open64); return ret;
}
#endif
#if defined HAVE___OPEN64
#undef __open64
int NEW(__open64)(const char *file, int oflag, ...)
{
int ret; ZZ_OPEN(__open64); return ret;
}
#endif
#if defined HAVE_DUP
#undef dup
int NEW(dup)(int oldfd)
{
int ret;
LOADSYM(dup);
ret = ORIG(dup)(oldfd);
if(!_zz_ready || _zz_islocked(-1) || !_zz_iswatched(oldfd)
|| !_zz_isactive(oldfd))
return ret;
if(ret >= 0)
{
debug("%s(%i) = %i", __func__, oldfd, ret);
_zz_register(ret);
}
return ret;
}
#endif
#if defined HAVE_DUP2
#undef dup2
int NEW(dup2)(int oldfd, int newfd)
{
int ret;
LOADSYM(dup2);
ret = ORIG(dup2)(oldfd, newfd);
if(!_zz_ready || _zz_islocked(-1) || !_zz_iswatched(oldfd)
|| !_zz_isactive(oldfd))
return ret;
if(ret >= 0)
{
/* We must close newfd if it was open, but only if oldfd != newfd
* and if dup2() suceeded. */
if(oldfd != newfd && _zz_iswatched(newfd) && _zz_isactive(newfd))
_zz_unregister(newfd);
debug("%s(%i, %i) = %i", __func__, oldfd, newfd, ret);
_zz_register(ret);
}
return ret;
}
#endif
#if defined HAVE_ACCEPT
#undef accept
int NEW(accept)(int sockfd, SOCKADDR_T *addr, SOCKLEN_T *addrlen)
{
int ret;
LOADSYM(accept);
ret = ORIG(accept)(sockfd, addr, addrlen);
if(!_zz_ready || _zz_islocked(-1) || !_zz_network
|| !_zz_iswatched(sockfd) || !_zz_isactive(sockfd))
return ret;
if(ret >= 0)
{
if(addrlen)
debug("%s(%i, %p, &%i) = %i", __func__,
sockfd, addr, (int)*addrlen, ret);
else
debug("%s(%i, %p, NULL) = %i", __func__, sockfd, addr, ret);
_zz_register(ret);
}
return ret;
}
#endif
#if defined AF_INET6
# define case_AF_INET6 case AF_INET6:
#else
# define case_AF_INET6
#endif
#define ZZ_CONNECT(myconnect, addr) \
do \
{ \
LOADSYM(myconnect); \
ret = ORIG(myconnect)(sockfd, addr, addrlen); \
if(!_zz_ready || _zz_islocked(-1) || !_zz_network) \
return ret; \
if(ret >= 0) \
{ \
struct sockaddr_in in; \
long int port; \
switch(addr->sa_family) \
{ \
case AF_INET: \
case_AF_INET6 \
/* We need to copy rather than cast sockaddr* to sockaddr_in* \
* because sockaddr_in* has actually _larger_ alignment on \
* eg. Linux alpha. And we only need sin_port so we only copy \
* this member. */ \
memcpy(&in.sin_port, \
(char const *)addr + ((char *)&in.sin_port - (char *)&in), \
sizeof(in.sin_port)); \
port = ntohs(in.sin_port); \
if(_zz_portwatched(port)) \
break; \
/* Fall through */ \
default: \
_zz_unregister(sockfd); \
return ret; \
} \
debug("%s(%i, %p, %i) = %i", __func__, \
sockfd, addr, (int)addrlen, ret); \
} \
} while(0);
#if defined HAVE_BIND
#undef bind
int NEW(bind)(int sockfd, const SOCKADDR_T *my_addr, SOCKLEN_T addrlen)
{
int ret; ZZ_CONNECT(bind, my_addr); return ret;
}
#endif
#if defined HAVE_CONNECT
#undef connect
int NEW(connect)(int sockfd, const SOCKADDR_T *serv_addr,
SOCKLEN_T addrlen)
{
int ret; ZZ_CONNECT(connect, serv_addr); return ret;
}
#endif
#if defined HAVE_SOCKET
#undef socket
int NEW(socket)(int domain, int type, int protocol)
{
int ret;
LOADSYM(socket);
ret = ORIG(socket)(domain, type, protocol);
if(!_zz_ready || _zz_islocked(-1) || !_zz_network)
return ret;
if(ret >= 0)
{
debug("%s(%i, %i, %i) = %i", __func__, domain, type, protocol, ret);
_zz_register(ret);
}
return ret;
}
#endif
#define ZZ_RECV(myrecv, myargs) \
do \
{ \
LOADSYM(myrecv); \
ret = ORIG(myrecv) myargs; \
if(!_zz_ready || !_zz_iswatched(s) || !_zz_hostwatched(s) \
|| _zz_islocked(s) || !_zz_isactive(s)) \
return ret; \
if(ret > 0) \
{ \
char *b = buf; \
_zz_fuzz(s, buf, ret); \
_zz_addpos(s, ret); \
if(ret >= 4) \
debug("%s(%i, %p, %li, 0x%x) = %i \"%c%c%c%c...", __func__, \
s, buf, (long int)len, flags, ret, \
b[0], b[1], b[2], b[3]); \
else \
debug("%s(%i, %p, %li, 0x%x) = %i \"%c...", __func__, \
s, buf, (long int)len, flags, ret, b[0]); \
} \
else \
debug("%s(%i, %p, %li, 0x%x) = %i", __func__, \
s, buf, (long int)len, flags, ret); \
} while(0);
#if defined HAVE_RECV
#undef recv
RECV_T NEW(recv)(int s, void *buf, size_t len, int flags)
{
int ret; ZZ_RECV(recv, (s, buf, len, flags)); return ret;
}
#endif
#if defined HAVE___RECV_CHK
#undef __recv_chk
RECV_T NEW(__recv_chk)(int s, void *buf, size_t len, size_t buflen, int flags)
{
int ret; ZZ_RECV(__recv_chk, (s, buf, len, buflen, flags)); return ret;
}
#endif
#define ZZ_RECVFROM(myrecvfrom, myargs) \
do \
{ \
LOADSYM(myrecvfrom); \
ret = ORIG(myrecvfrom) myargs; \
if(!_zz_ready || !_zz_iswatched(s) || !_zz_hostwatched(s) \
|| _zz_islocked(s) || !_zz_isactive(s)) \
return ret; \
if(ret > 0) \
{ \
char tmp[128]; \
char *b = buf; \
_zz_fuzz(s, buf, ret); \
_zz_addpos(s, ret); \
if (fromlen) \
sprintf(tmp, "&%i", (int)*fromlen); \
else \
strcpy(tmp, "NULL"); \
if (ret >= 4) \
debug("%s(%i, %p, %li, 0x%x, %p, %s) = %i \"%c%c%c%c...", \
__func__, s, buf, (long int)len, flags, from, tmp, \
ret, b[0], b[1], b[2], b[3]); \
else \
debug("%s(%i, %p, %li, 0x%x, %p, %s) = %i \"%c...", \
__func__, s, buf, (long int)len, flags, from, tmp, \
ret, b[0]); \
} \
else \
debug("%s(%i, %p, %li, 0x%x, %p, %p) = %i", __func__, \
s, buf, (long int)len, flags, from, fromlen, ret); \
} while(0)
#if defined HAVE_RECVFROM
#undef recvfrom
RECV_T NEW(recvfrom)(int s, void *buf, size_t len, int flags,
SOCKADDR_T *from, SOCKLEN_T *fromlen)
{
int ret;
ZZ_RECVFROM(recvfrom, (s, buf, len, flags, from, fromlen));
return ret;
}
#endif
#if defined HAVE___RECVFROM_CHK
#undef __recvfrom_chk
RECV_T NEW(__recvfrom_chk)(int s, void *buf, size_t len, size_t buflen,
int flags, SOCKADDR_T *from, SOCKLEN_T *fromlen)
{
int ret;
ZZ_RECVFROM(__recvfrom_chk, (s, buf, len, buflen, flags, from, fromlen));
return ret;
}
#endif
#if defined HAVE_RECVMSG
#undef recvmsg
RECV_T NEW(recvmsg)(int s, struct msghdr *hdr, int flags)
{
ssize_t ret;
LOADSYM(recvmsg);
ret = ORIG(recvmsg)(s, hdr, flags);
if(!_zz_ready || !_zz_iswatched(s) || !_zz_hostwatched(s)
|| _zz_islocked(s) || !_zz_isactive(s))
return ret;
fuzz_iovec(s, hdr->msg_iov, ret);
debug("%s(%i, %p, %x) = %li", __func__, s, hdr, flags, (long int)ret);
return ret;
}
#endif
#define ZZ_READ(myread, myargs) \
do \
{ \
LOADSYM(myread); \
ret = ORIG(myread) myargs; \
if(!_zz_ready || !_zz_iswatched(fd) || !_zz_hostwatched(fd) \
|| _zz_islocked(fd) || !_zz_isactive(fd)) \
return ret; \
if(ret > 0) \
{ \
char *b = buf; \
_zz_fuzz(fd, buf, ret); \
_zz_addpos(fd, ret); \
if(ret >= 4) \
debug("%s(%i, %p, %li) = %i \"%c%c%c%c...", __func__, fd, \
buf, (long int)count, ret, b[0], b[1], b[2], b[3]); \
else \
debug("%s(%i, %p, %li) = %i \"%c...", __func__, fd, \
buf, (long int)count, ret, b[0]); \
} \
else \
debug("%s(%i, %p, %li) = %i", __func__, fd, \
buf, (long int)count, ret); \
offset_check(fd); \
} while(0)
#if defined READ_USES_SSIZE_T
#undef read
ssize_t NEW(read)(int fd, void *buf, size_t count)
{
int ret; ZZ_READ(read, (fd, buf, count)); return (ssize_t)ret;
}
#else
#undef read
int NEW(read)(int fd, void *buf, unsigned int count)
{
int ret; ZZ_READ(read, (fd, buf, count)); return ret;
}
#endif
#if defined HAVE___READ_CHK
#undef __read_chk
ssize_t NEW(__read_chk)(int fd, void *buf, size_t count, size_t buflen)
{
int ret; ZZ_READ(__read_chk, (fd, buf, count, buflen)); return (ssize_t)ret;
}
#endif
#if defined HAVE_READV
#undef readv
ssize_t NEW(readv)(int fd, const struct iovec *iov, int count)
{
ssize_t ret;
LOADSYM(readv);
ret = ORIG(readv)(fd, iov, count);
if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(fd)
|| !_zz_isactive(fd))
return ret;
fuzz_iovec(fd, iov, ret);
debug("%s(%i, %p, %i) = %li", __func__, fd, iov, count, (long int)ret);
offset_check(fd);
return ret;
}
#endif
#if defined HAVE_PREAD
#undef pread
ssize_t NEW(pread)(int fd, void *buf, size_t count, off_t offset)
{
int ret;
LOADSYM(pread);
ret = ORIG(pread)(fd, buf, count, offset);
if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(fd)
|| !_zz_isactive(fd))
return ret;
if(ret > 0)
{
long int curoff = _zz_getpos(fd);
char *b = buf;
_zz_setpos(fd, offset);
_zz_fuzz(fd, buf, ret);
_zz_setpos(fd, curoff);
if(ret >= 4)
debug("%s(%i, %p, %li, %li) = %i \"%c%c%c%c...", __func__, fd, buf,
(long int)count, (long int)offset, ret,
b[0], b[1], b[2], b[3]);
else
debug("%s(%i, %p, %li, %li) = %i \"%c...", __func__, fd, buf,
(long int)count, (long int)offset, ret, b[0]);
}
else
debug("%s(%i, %p, %li, %li) = %i", __func__, fd, buf,
(long int)count, (long int)offset, ret);
return ret;
}
#endif
#define ZZ_LSEEK(mylseek, off_t) \
do \
{ \
LOADSYM(mylseek); \
ret = ORIG(mylseek)(fd, offset, whence); \
if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(fd) \
|| !_zz_isactive(fd)) \
return ret; \
debug("%s(%i, %lli, %i) = %lli", __func__, fd, \
(long long int)offset, whence, (long long int)ret); \
if(ret != (off_t)-1) \
_zz_setpos(fd, ret); \
} while(0)
#undef lseek
off_t NEW(lseek)(int fd, off_t offset, int whence)
{
off_t ret;
ZZ_LSEEK(lseek, off_t);
return ret;
}
#if defined HAVE_LSEEK64
#undef lseek64
off64_t NEW(lseek64)(int fd, off64_t offset, int whence)
{
off64_t ret; ZZ_LSEEK(lseek64, off64_t); return ret;
}
#endif
#if defined HAVE___LSEEK64
#undef __lseek64
off64_t NEW(__lseek64)(int fd, off64_t offset, int whence)
{
off64_t ret; ZZ_LSEEK(__lseek64, off64_t); return ret;
}
#endif
#if defined HAVE_AIO_READ
#undef aio_read
int NEW(aio_read)(struct aiocb *aiocbp)
{
int ret;
int fd = aiocbp->aio_fildes;
LOADSYM(aio_read);
if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
return ORIG(aio_read)(aiocbp);
_zz_lock(fd);
ret = ORIG(aio_read)(aiocbp);
debug("%s({%i, %i, %i, %p, %li, ..., %li}) = %i", __func__,
fd, aiocbp->aio_lio_opcode, aiocbp->aio_reqprio, aiocbp->aio_buf,
(long int)aiocbp->aio_nbytes, (long int)aiocbp->aio_offset, ret);
return ret;
}
#undef aio_return
ssize_t NEW(aio_return)(struct aiocb *aiocbp)
{
ssize_t ret;
int fd = aiocbp->aio_fildes;
LOADSYM(aio_return);
if(!_zz_ready || !_zz_iswatched(fd) || !_zz_isactive(fd))
return ORIG(aio_return)(aiocbp);
ret = ORIG(aio_return)(aiocbp);
_zz_unlock(fd);
/* FIXME: make sure were actually *reading* */
if(ret > 0)
{
_zz_setpos(fd, aiocbp->aio_offset);
_zz_fuzz(fd, aiocbp->aio_buf, ret);
_zz_addpos(fd, ret);
}
debug("%s({%i, %i, %i, %p, %li, ..., %li}) = %li", __func__,
fd, aiocbp->aio_lio_opcode, aiocbp->aio_reqprio, aiocbp->aio_buf,
(long int)aiocbp->aio_nbytes, (long int)aiocbp->aio_offset,
(long int)ret);
return ret;
}
#endif
#undef close
int NEW(close)(int fd)
{
int ret;
/* Hey, its our debug channel! Silently pretend we closed it. */
if(fd == _zz_debugfd)
return 0;
LOADSYM(close);
ret = ORIG(close)(fd);
if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(fd))
return ret;
debug("%s(%i) = %i", __func__, fd, ret);
_zz_unregister(fd);
return ret;
}
/* XXX: the following functions are local */
#if defined HAVE_READV || defined HAVE_RECVMSG
static void fuzz_iovec(int fd, const struct iovec *iov, ssize_t ret)
{
/* NOTE: We assume that iov countains at least <ret> bytes. */
while(ret > 0)
{
void *b = iov->iov_base;
size_t len = iov->iov_len;
if(len > (size_t)ret)
len = ret;
_zz_fuzz(fd, b, len);
_zz_addpos(fd, len);
iov++;
ret -= len;
}
}
#endif
/* Sanity check, can be OK though (for instance with a character device) */
static void offset_check(int fd)
{
int orig_errno = errno;
#if defined HAVE_LSEEK64
off64_t ret;
LOADSYM(lseek64);
ret = ORIG(lseek64)(fd, 0, SEEK_CUR);
#else
off_t ret;
LOADSYM(lseek);
ret = ORIG(lseek)(fd, 0, SEEK_CUR);
#endif
if(ret != -1 && ret != _zz_getpos(fd))
debug("warning: offset inconsistency");
errno = orig_errno;
}