diff options
author | Simon Rettberg | 2017-10-30 21:39:56 +0100 |
---|---|---|
committer | Simon Rettberg | 2017-10-30 21:39:56 +0100 |
commit | 46c3e0e276e62b6be76e69b68de56432692efcf3 (patch) | |
tree | 194ab981ea90f1227cfb94509b9d17b25bb29354 | |
parent | [SERVER] Missed occurence of AF_INET(6) -> HOST_IP[46] (diff) | |
download | dnbd3-46c3e0e276e62b6be76e69b68de56432692efcf3.tar.gz dnbd3-46c3e0e276e62b6be76e69b68de56432692efcf3.tar.xz dnbd3-46c3e0e276e62b6be76e69b68de56432692efcf3.zip |
[SERVER] Add AFL support
AFL is an instrumenting fuzzer.
It expects to pass input to the program to be tested
via command line (file name) or via stdin. This adds
support for reading messages that normally would arrive
via network directly from stdin. In this mode, the server
is pretty useless otherwise.
http://lcamtuf.coredump.cx/afl/
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/server/net.c | 37 | ||||
-rw-r--r-- | src/server/rpc.c | 11 | ||||
-rw-r--r-- | src/server/rpc.h | 1 | ||||
-rw-r--r-- | src/server/server.c | 39 | ||||
-rw-r--r-- | src/shared/log.c | 5 | ||||
-rw-r--r-- | src/types.h | 6 |
7 files changed, 101 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d738bd1..f2da9e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ OPTION(BUILD_FUSE_CLIENT "Build dnbd3 fuse client" ON) OPTION(BUILD_SERVER "Build dnbd3 server" ON) OPTION(BUILD_STRESSTEST "Build dnbd3 stress testing tool" OFF) +OPTION(SERVER_FOR_AFL "Build dnbd3-server for usage with afl-fuzz" OFF) + # Is there a non-retarded way to check if build type is debug or release? # When specifying, it is case insensitive, so DeBuG would also enable debug builds, # but in cmake, we can only do case sensitive matches... :/ @@ -140,6 +142,10 @@ ENDIF() ################################################################################ if(BUILD_SERVER) + IF(SERVER_FOR_AFL) + message(" ######################## Building server for AFL mode - will be useless otherwise!") + ADD_DEFINITIONS(-DAFL_MODE) + ENDIF() INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${JANSSON_INCLUDE_DIR}) FILE(GLOB SERVER_SRCS src/server/*.c src/shared/*.c src/server/picohttpparser/*.c) ADD_EXECUTABLE(dnbd3-server ${SERVER_SRCS}) diff --git a/src/server/net.c b/src/server/net.c index c7d34de..c11a78a 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -69,6 +69,9 @@ void net_updateGlobalSentStatsFromClient(dnbd3_client_t * const client) static inline bool recv_request_header(int sock, dnbd3_request_t *request) { ssize_t ret, fails = 0; +#ifdef AFL_MODE + sock = 0; +#endif // Read request header from socket while ( ( ret = recv( sock, request, sizeof(*request), MSG_WAITALL ) ) != sizeof(*request) ) { if ( errno == EINTR && ++fails < 10 ) continue; @@ -93,6 +96,9 @@ static inline bool recv_request_header(int sock, dnbd3_request_t *request) static inline bool recv_request_payload(int sock, uint32_t size, serialized_buffer_t *payload) { +#ifdef AFL_MODE + sock = 0; +#endif if ( size == 0 ) { logadd( LOG_ERROR, "Called recv_request_payload() to receive 0 bytes" ); return false; @@ -169,7 +175,11 @@ void* net_handleNewConnection(void *clientPtr) // Await data from client. Since this is a fresh connection, we expect data right away sock_setTimeout( client->sock, _clientTimeout ); do { +#ifdef AFL_MODE + const int ret = (int)recv( 0, &request, sizeof(request), MSG_WAITALL ); +#else const int ret = (int)recv( client->sock, &request, sizeof(request), MSG_WAITALL ); +#endif // It's expected to be a real dnbd3 client // Check request for validity. This implicitly dictates that all HTTP requests are more than 16 bytes... if ( ret != (int)sizeof(request) ) { @@ -408,7 +418,32 @@ void* net_handleNewConnection(void *clientPtr) realBytes = image->realFilesize - offset; } while ( done < realBytes ) { -#ifdef __linux__ +#ifdef AFL_MODE + char buf[1000]; + size_t cnt = realBytes - done; + if ( cnt > 1000 ) { + cnt = 1000; + } + const ssize_t ret = pread( image_file, buf, cnt, offset ); + if ( ret <= 0 ) { + const int err = errno; + if ( lock ) pthread_mutex_unlock( &client->sendMutex ); + if ( ret == -1 ) { + if ( err != EPIPE && err != ECONNRESET && err != ESHUTDOWN + && err != EAGAIN && err != EWOULDBLOCK ) { + logadd( LOG_DEBUG1, "sendfile to %s failed (image to net. sent %d/%d, errno=%d)", + client->hostName, (int)done, (int)realBytes, err ); + } + if ( err == EBADF || err == EFAULT || err == EINVAL || err == EIO ) { + logadd( LOG_INFO, "Disabling %s:%d", image->name, image->rid ); + image->working = false; + } + } + goto exit_client_cleanup; + } + write( client->sock, buf, ret ); + done += ret; +#elif defined(__linux__) const ssize_t ret = sendfile( client->sock, image_file, &foffset, realBytes - done ); if ( ret <= 0 ) { const int err = errno; diff --git a/src/server/rpc.c b/src/server/rpc.c index f64e90a..97cca9e 100644 --- a/src/server/rpc.c +++ b/src/server/rpc.c @@ -133,7 +133,11 @@ void rpc_sendStatsJson(int sock, dnbd3_host_t* host, const void* data, const int // Reaching here means partial request or parse error if ( pret == -2 ) { // Partial, keep reading prevLen = hoff; +#ifdef AFL_MODE + ssize_t ret = recv( 0, headerBuf + hoff, sizeof(headerBuf) - hoff, 0 ); +#else ssize_t ret = recv( sock, headerBuf + hoff, sizeof(headerBuf) - hoff, 0 ); +#endif if ( ret == 0 ) return; if ( ret == -1 ) { if ( errno == EINTR ) continue; @@ -260,6 +264,9 @@ static bool sendReply(int sock, const char *status, const char *ctype, const cha if ( keepAlive == HTTP_CLOSE ) { // Wait for flush shutdown( sock, SHUT_WR ); +#ifdef AFL_MODE + sock = 0; +#endif while ( read( sock, buffer, sizeof buffer ) > 0 ); return false; } @@ -303,7 +310,11 @@ static int getacl(dnbd3_host_t *host) if ( aclRules[i].bitMask != 0 && aclRules[i].host[aclRules[i].bytes] != ( host->addr[aclRules[i].bytes] & aclRules[i].bitMask ) ) continue; return aclRules[i].permissions; } +#ifdef AFL_MODE + return 0x7fffff; +#else return 0; +#endif } #define SETBIT(x) else if ( strcmp( argv[i], #x ) == 0 ) mask |= ACL_ ## x diff --git a/src/server/rpc.h b/src/server/rpc.h index 1d50e77..f337067 100644 --- a/src/server/rpc.h +++ b/src/server/rpc.h @@ -3,6 +3,7 @@ struct dnbd3_host_t; +void rpc_init(); void rpc_sendStatsJson(int sock, struct dnbd3_host_t* host, const void *data, const int dataLen); #endif diff --git a/src/server/server.c b/src/server/server.c index 5932b0b..5776765 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -28,6 +28,7 @@ #include "altservers.h" #include "integrity.h" #include "threadpool.h" +#include "rpc.h" #include "../version.h" #include "../shared/sockhelper.h" @@ -227,6 +228,40 @@ int main(int argc, char *argv[]) exit( EXIT_FAILURE ); } + timing_setBase(); + timing_get( &startupTime ); + +#ifdef AFL_MODE + // ###### AFL + // + image_serverStartup(); + net_init(); + uplink_globalsInit(); + rpc_init(); + if ( !image_loadAll( NULL ) || _shutdown ) { + fprintf( stderr, "Error loading images\n" ); + exit( 3 ); + } + { + struct sockaddr_storage client; + memset( &client, 0, sizeof client ); + client.ss_family = AF_INET; + dnbd3_client_t *dnbd3_client = dnbd3_prepareClient( &client, 1 ); + if ( dnbd3_client == NULL ) { + fprintf( stderr, "New client failed\n" ); + exit( 1 ); + } +#ifdef __AFL_HAVE_MANUAL_CONTROL + __AFL_INIT(); +#endif + net_handleNewConnection( dnbd3_client ); + exit( 0 ); + } + // + // ###### AFL END +#endif + + // One-shots first: if ( paramCreate != NULL ) { @@ -236,12 +271,12 @@ int main(int argc, char *argv[]) // No one-shot detected, normal server operation if ( demonize ) daemon( 1, 0 ); - timing_setBase(); image_serverStartup(); altservers_init(); integrity_init(); net_init(); uplink_globalsInit(); + rpc_init(); logadd( LOG_INFO, "DNBD3 server starting.... Machine type: " ENDIAN_MODE ); if ( altservers_load() < 0 ) { @@ -268,8 +303,6 @@ int main(int argc, char *argv[]) return 0; } - timing_get( &startupTime ); - // Give other threads some time to start up before accepting connections sleep( 1 ); diff --git a/src/shared/log.c b/src/shared/log.c index 1022cec..62b104e 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -114,8 +114,13 @@ void logadd(const logmask_t mask, const char *fmt, ...) pthread_mutex_unlock( &logLock ); } if ( maskCon & mask ) { +#ifdef AFL_MODE + fputs( buffer, stderr ); + fflush( stderr ); +#else fputs( buffer, stdout ); fflush( stdout ); +#endif } } diff --git a/src/types.h b/src/types.h index 313e907..fd7bd21 100644 --- a/src/types.h +++ b/src/types.h @@ -55,6 +55,12 @@ #include <netinet/in.h> #endif +#ifdef AFL_MODE +#define send(a,b,c,d) write(a,b,c) +#define recv(a,b,c,d) read(a,b,c) +#endif + + // ioctl #define DNBD3_MAGIC 'd' #define IOCTL_OPEN _IO(0xab, 1) |