/* * zzuf - general purpose fuzzer * Copyright (c) 2006, 2007 Sam Hocevar * 2007 Rémi Denis-Courmont * 2007 Clément Stenac * 2007 Dominik Kuhlen * 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-fd.c: loaded file descriptor functions */ #include "config.h" /* Need this for RTLD_NEXT */ #define _GNU_SOURCE /* Use this to get lseek64() on glibc systems */ #define _LARGEFILE64_SOURCE #if defined HAVE_STDINT_H # include #elif defined HAVE_INTTYPES_H # include #endif #include #include #include #include #if defined HAVE_SYS_SOCKET_H # include #endif #if defined HAVE_SYS_UIO_H # include #endif #include #include #include #if defined HAVE_AIO_H # include #endif #include "libzzuf.h" #include "lib-load.h" #include "debug.h" #include "fuzz.h" #include "fd.h" #if defined HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else # define SOCKLEN_T int #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 (*open_orig) (const char *file, int oflag, ...); #if defined HAVE_OPEN64 static int (*open64_orig) (const char *file, int oflag, ...); #endif #if defined HAVE_ACCEPT static int (*accept_orig) (int sockfd, struct sockaddr *addr, SOCKLEN_T *addrlen); #endif #if defined HAVE_SOCKET static int (*socket_orig) (int domain, int type, int protocol); #endif #if defined HAVE_RECV static int (*recv_orig) (int s, void *buf, size_t len, int flags); #endif #if defined HAVE_RECVFROM static int (*recvfrom_orig)(int s, void *buf, size_t len, int flags, struct sockaddr *from, SOCKLEN_T *fromlen); #endif #if defined HAVE_RECVMSG static int (*recvmsg_orig) (int s, struct msghdr *hdr, int flags); #endif #if defined READ_USES_SSIZE_T static ssize_t (*read_orig) (int fd, void *buf, size_t count); #else static int (*read_orig) (int fd, void *buf, unsigned int count); #endif #if defined HAVE_READV static ssize_t (*readv_orig) (int fd, const struct iovec *iov, int count); #endif #if defined HAVE_PREAD static ssize_t (*pread_orig) (int fd, void *buf, size_t count, off_t offset); #endif #if defined HAVE_AIO_READ static int (*aio_read_orig) (struct aiocb *aiocbp); static ssize_t (*aio_return_orig) (struct aiocb *aiocbp); #endif static off_t (*lseek_orig) (int fd, off_t offset, int whence); #if defined HAVE_LSEEK64 static off64_t (*lseek64_orig) (int fd, off64_t offset, int whence); #endif static int (*close_orig) (int fd); #define OPEN(fn) \ do \ { \ int mode = 0; \ LOADSYM(fn); \ if(oflag & O_CREAT) \ { \ va_list va; \ va_start(va, oflag); \ mode = va_arg(va, int); \ va_end(va); \ ret = ORIG(fn)(file, oflag, mode); \ } \ else \ { \ ret = ORIG(fn)(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) int open(const char *file, int oflag, ...) { int ret; OPEN(open); return ret; } #if defined HAVE_OPEN64 int open64(const char *file, int oflag, ...) { int ret; OPEN(open64); return ret; } #endif #if defined HAVE_ACCEPT int accept(int sockfd, struct sockaddr *addr, SOCKLEN_T *addrlen) { int ret; LOADSYM(accept); ret = accept_orig(sockfd, addr, addrlen); if(!_zz_ready || _zz_islocked(-1) || !_zz_network) return ret; if(ret >= 0) { debug("%s(%i, %p, %p) = %i", __func__, sockfd, addr, addrlen, ret); _zz_register(ret); } return ret; } #endif #if defined HAVE_SOCKET int socket(int domain, int type, int protocol) { int ret; LOADSYM(socket); ret = socket_orig(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 #if defined HAVE_RECV int recv(int s, void *buf, size_t len, int flags) { int ret; LOADSYM(recv); ret = recv_orig(s, buf, len, flags); if(!_zz_ready || !_zz_iswatched(s) || _zz_islocked(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); return ret; } #endif #if defined HAVE_RECVFROM int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, SOCKLEN_T *fromlen) { int ret; LOADSYM(recvfrom); ret = recvfrom_orig(s, buf, len, flags, from, fromlen); if(!_zz_ready || !_zz_iswatched(s) || _zz_islocked(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, %p, %p) = %i \"%c%c%c%c...", __func__, s, buf, (long int)len, flags, from, fromlen, ret, b[0], b[1], b[2], b[3]); else debug("%s(%i, %p, %li, 0x%x, %p, %p) = %i \"%c...", __func__, s, buf, (long int)len, flags, from, fromlen, ret, b[0]); } else debug("%s(%i, %p, %li, 0x%x, %p, %p) = %i", __func__, s, buf, (long int)len, flags, from, fromlen, ret); return ret; } #endif #if defined HAVE_RECVMSG int recvmsg(int s, struct msghdr *hdr, int flags) { ssize_t ret; LOADSYM(recvmsg); ret = recvmsg_orig(s, hdr, flags); if(!_zz_ready || !_zz_iswatched(s) || _zz_islocked(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 #if defined READ_USES_SSIZE_T ssize_t read(int fd, void *buf, size_t count) #else int read(int fd, void *buf, unsigned int count) #endif { int ret; LOADSYM(read); ret = read_orig(fd, buf, count); if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(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); return ret; } #if defined HAVE_READV ssize_t readv(int fd, const struct iovec *iov, int count) { ssize_t ret; LOADSYM(readv); ret = readv_orig(fd, iov, count); if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(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 ssize_t pread(int fd, void *buf, size_t count, off_t offset) { int ret; LOADSYM(pread); ret = pread_orig(fd, buf, count, offset); if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(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 LSEEK(fn, off_t) \ do \ { \ LOADSYM(fn); \ ret = ORIG(fn)(fd, offset, whence); \ if(!_zz_ready || !_zz_iswatched(fd) || _zz_islocked(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) off_t lseek(int fd, off_t offset, int whence) { off_t ret; LSEEK(lseek, off_t); return ret; } #if defined HAVE_LSEEK64 off64_t lseek64(int fd, off64_t offset, int whence) { off64_t ret; LSEEK(lseek64, off64_t); return ret; } #endif #if defined HAVE_AIO_READ int aio_read(struct aiocb *aiocbp) { int ret; int fd = aiocbp->aio_fildes; LOADSYM(aio_read); if(!_zz_ready || !_zz_iswatched(fd)) return aio_read_orig(aiocbp); _zz_lock(fd); ret = aio_read_orig(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; } ssize_t aio_return(struct aiocb *aiocbp) { ssize_t ret; int fd = aiocbp->aio_fildes; LOADSYM(aio_return); if(!_zz_ready || !_zz_iswatched(fd)) return aio_return_orig(aiocbp); ret = aio_return_orig(aiocbp); _zz_unlock(fd); /* FIXME: make sure we’re 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 int close(int fd) { int ret; /* Hey, it’s our debug channel! Silently pretend we closed it. */ if(fd == DEBUG_FILENO) return 0; LOADSYM(close); ret = close_orig(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 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 static void offset_check(int fd) { /* Sanity check, can be OK though (for instance with a character device) */ #if defined HAVE_LSEEK64 off64_t ret; LOADSYM(lseek64); ret = lseek64_orig(fd, 0, SEEK_CUR); #else off_t ret; LOADSYM(lseek); ret = lseek_orig(fd, 0, SEEK_CUR); #endif if(ret != -1 && ret != _zz_getpos(fd)) debug("warning: offset inconsistency"); }