]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
afl-fuzz debugging support. divVerent/afl-fuzz
authorRudolf Polzer <divverent@xonotic.org>
Sat, 28 Feb 2015 21:46:54 +0000 (22:46 +0100)
committerRudolf Polzer <divverent@xonotic.org>
Mon, 2 Mar 2015 13:30:19 +0000 (14:30 +0100)
Includes:

- SIGABRT in case of Host_Error calls (these would normally "safely"
  exit the dedicated server, so remote exploits calling this are
  interesting).
- SIGXCPU in case NetConn_ServerFrame is hit again after the injected
  packet (shouldn't ever happen - just in case, as it also would
  indicate a remote exploitable problem as remaining sockets in the list
  were skipped then).
- Facility "net_dump" to dump all incoming network packets to files.
  Allows to easily choose an "interesting" one.
- Facility "net_inject" to replace a specific packet by the content of a
  file. Includes a flag net_inject_index, which causes the engine to
  exit successfully after the injected packet was processed.
  Note that successful injection requires the following engine flags:
  -nosound +sys_usenoclockbutbenchmark 1 +cl_maxfps 30 +sv_random_seed 0
  to remove any nondeterministic influence (including external timing
  sources).
- After injecting, the loop is executed that crashes in Xonotic bug
  report https://gitlab.com/xonotic/darkplaces/issues/24 - this helps to figure
  out said bug report.
- afl-fuzz instrumentation (and thus afl-fuzz's fork server) is started
  only just before injection. This keeps afl-fuzz runs fast (about
  70/sec), skipping the otherwise unavoidable loading time of 20
  seconds.
- Disable vid_glx's signal handler, so afl-fuzz will actually see
  crashes as crashes (and as fast as possible - in my testing crashes
  were sometimes misdetcted as hangs as the signal handler, which tries
  a clean exit, will acquire mutexes held during the crash).

host.c
netconn.c
vid_glx.c

diff --git a/host.c b/host.c
index 7e7bc56a7f2950de8df0e6c6a9ffa52273338326..304ab96e4ac365fb1d8e8a1ce61f292322994e3b 100644 (file)
--- a/host.c
+++ b/host.c
@@ -102,6 +102,8 @@ aborts the current host frame and goes on with the next one
 void Host_AbortCurrentFrame(void) DP_FUNC_NORETURN;
 void Host_AbortCurrentFrame(void)
 {
+       abort();
+
        // in case we were previously nice, make us mean again
        Sys_MakeProcessMean();
 
index abf8e31af82a5ef0df7e62843cf99811b65c0e72..1f584c8d284954e65332848ec375a4176ead12a0 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "thread.h"
 #include "lhnet.h"
+#include <sys/signal.h>
+#include <signal.h>
 
 // for secure rcon authentication
 #include "hmac.h"
@@ -80,6 +82,12 @@ char cl_readstring[MAX_INPUTLINE];
 char sv_readstring[MAX_INPUTLINE];
 
 cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
+cvar_t net_inject = {0, "net_inject", "", "file name of a packet to inject"};
+cvar_t net_inject_index = {0, "net_inject_index", "0", "packet index where to inject"};
+cvar_t net_inject_exit = {0, "net_inject_exit", "0", "exit after injecting"};
+cvar_t net_dump = {0, "net_dump", "", "file name of a packet to dump"};
+cvar_t net_dump_index = {0, "net_dump_index", "0", "current packet index when dumping"};
+cvar_t net_dump_maxindex = {0, "net_dump_maxindex", "0", "max packet index to dump"};
 cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
 cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
@@ -3535,12 +3543,53 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
 void NetConn_ServerFrame(void)
 {
+       extern volatile char __afl_start_here;
+       if (__afl_start_here && net_inject_exit.integer) {
+               // Shouldn't ever happen - this would mean ServerFrame was exited weirdly (longjmp?).
+               raise(SIGXCPU);
+       }
        int i, length;
        lhnetaddress_t peeraddress;
        unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
        for (i = 0;i < sv_numsockets;i++)
                while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
+               {
+                       if (net_inject_index.integer == 1) {
+                               __afl_start_here = 1;
+                               if (__afl_start_here != 1) {
+                                       // Shouldn't ever happen.
+                                       // This code just serves to force a branch so afl can start its fork server.
+                                       __afl_start_here = 2;
+                               }
+                               fs_offset_t size = 0;
+                               unsigned char *buf = FS_LoadFile(net_inject.string, tempmempool, false, &size);
+                               if (buf) {
+                                       NetConn_ServerParsePacket(sv_sockets[i], buf, size, &peeraddress);
+                                       Mem_Free(buf);
+                               }
+                               if (net_inject_exit.integer) {
+                                       for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+                                       {
+                                               // never timeout loopback connections
+                                               if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+                                               {
+                                                       Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
+                                                       SV_DropClient(false);
+                                               }
+                                       }
+                                       exit(0);
+                               }
+                       }
+                       if (net_inject_index.integer >= 1) {
+                               Cvar_SetValueQuick(&net_inject_index, net_inject_index.integer - 1);
+                       }
+                       if (net_dump_index.integer < net_dump_maxindex.integer) {
+                               Cvar_SetValueQuick(&net_dump_index, net_dump_index.integer + 1);
+                               char vabuf[1024];
+                               FS_WriteFile(va(vabuf, sizeof(vabuf), "%s.%d", net_dump.string, net_dump_index.integer), readbuffer, length);
+                       }
                        NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
+               }
        for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
        {
                // never timeout loopback connections
@@ -3809,6 +3858,12 @@ void NetConn_Init(void)
        Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
 #endif
        Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
+       Cvar_RegisterVariable(&net_inject);
+       Cvar_RegisterVariable(&net_inject_index);
+       Cvar_RegisterVariable(&net_inject_exit);
+       Cvar_RegisterVariable(&net_dump);
+       Cvar_RegisterVariable(&net_dump_index);
+       Cvar_RegisterVariable(&net_dump_maxindex);
        Cvar_RegisterVariable(&net_test);
        Cvar_RegisterVariable(&net_usesizelimit);
        Cvar_RegisterVariable(&net_burstreserve);
index 8327b2608e9a8e301af2d7d27d0101c0e6a816fd..d9ec25d46bf8145c9097488c88180d86aefd7de0 100644 (file)
--- a/vid_glx.c
+++ b/vid_glx.c
@@ -899,6 +899,7 @@ static void signal_handler(int sig)
 
 static void InitSig(void)
 {
+       /*
        signal(SIGHUP, signal_handler);
        signal(SIGINT, signal_handler);
        signal(SIGQUIT, signal_handler);
@@ -909,6 +910,7 @@ static void InitSig(void)
        signal(SIGFPE, signal_handler);
        signal(SIGSEGV, signal_handler);
        signal(SIGTERM, signal_handler);
+       */
 }
 
 void VID_Finish (void)