summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/fuzz
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/fuzz
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/VBox/Runtime/common/fuzz/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz-observer.cpp1331
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz.cpp1217
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp216
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp1351
5 files changed, 4115 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/fuzz/Makefile.kup b/src/VBox/Runtime/common/fuzz/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/Makefile.kup
diff --git a/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp b/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp
new file mode 100644
index 00000000..343a29e9
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp
@@ -0,0 +1,1331 @@
+/* $Id: fuzz-observer.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, observer.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/md5.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+
+
+/** Poll ID for the reading end of the stdout pipe from the client process. */
+#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT 0
+/** Poll ID for the reading end of the stderr pipe from the client process. */
+#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR 1
+/** Poll ID for the writing end of the stdin pipe to the client process. */
+#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN 2
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to the internal fuzzing observer state. */
+typedef struct RTFUZZOBSINT *PRTFUZZOBSINT;
+
+
+/**
+ * Observer thread state for one process.
+ */
+typedef struct RTFUZZOBSTHRD
+{
+ /** The thread handle. */
+ RTTHREAD hThread;
+ /** The observer ID. */
+ uint32_t idObs;
+ /** Flag whether to shutdown. */
+ volatile bool fShutdown;
+ /** Pointer to te global observer state. */
+ PRTFUZZOBSINT pFuzzObs;
+ /** Current fuzzer input. */
+ RTFUZZINPUT hFuzzInput;
+ /** Flag whether to keep the input. */
+ bool fKeepInput;
+ /** Flag whether a new input is waiting. */
+ volatile bool fNewInput;
+} RTFUZZOBSTHRD;
+/** Pointer to an observer thread state. */
+typedef RTFUZZOBSTHRD *PRTFUZZOBSTHRD;
+
+
+/**
+ * Internal fuzzing observer state.
+ */
+typedef struct RTFUZZOBSINT
+{
+ /** The fuzzing context used for this observer. */
+ RTFUZZCTX hFuzzCtx;
+ /** Temp directory for input files. */
+ char *pszTmpDir;
+ /** Results directory. */
+ char *pszResultsDir;
+ /** The binary to run. */
+ char *pszBinary;
+ /** Arguments to run the binary with, terminated by a NULL entry. */
+ char **papszArgs;
+ /** Number of arguments. */
+ uint32_t cArgs;
+ /** Maximum time to wait for the client to terminate until it is considered hung and killed. */
+ RTMSINTERVAL msWaitMax;
+ /** The channel the binary expects the input. */
+ RTFUZZOBSINPUTCHAN enmInputChan;
+ /** Flag whether to shutdown the master and all workers. */
+ volatile bool fShutdown;
+ /** Global observer thread handle. */
+ RTTHREAD hThreadGlobal;
+ /** The event semaphore handle for the global observer thread. */
+ RTSEMEVENT hEvtGlobal;
+ /** Notification event bitmap. */
+ volatile uint64_t bmEvt;
+ /** Number of threads created - one for each process. */
+ uint32_t cThreads;
+ /** Pointer to the array of observer thread states. */
+ PRTFUZZOBSTHRD paObsThreads;
+ /** Timestamp of the last stats query. */
+ uint64_t tsLastStats;
+ /** Last number of fuzzed inputs per second if we didn't gather enough data in between
+ * statistic queries. */
+ uint32_t cFuzzedInputsPerSecLast;
+ /** Fuzzing statistics. */
+ RTFUZZOBSSTATS Stats;
+} RTFUZZOBSINT;
+
+
+/**
+ * Stdout/Stderr buffer.
+ */
+typedef struct RTFUZZOBSSTDOUTERRBUF
+{
+ /** Current amount buffered. */
+ size_t cbBuf;
+ /** Maxmium amount to buffer. */
+ size_t cbBufMax;
+ /** Base pointer to the data buffer. */
+ uint8_t *pbBase;
+} RTFUZZOBSSTDOUTERRBUF;
+/** Pointer to a stdout/stderr buffer. */
+typedef RTFUZZOBSSTDOUTERRBUF *PRTFUZZOBSSTDOUTERRBUF;
+
+
+/**
+ * Worker execution context.
+ */
+typedef struct RTFUZZOBSEXECCTX
+{
+ /** The stdout pipe handle - reading end. */
+ RTPIPE hPipeStdoutR;
+ /** The stdout pipe handle - writing end. */
+ RTPIPE hPipeStdoutW;
+ /** The stderr pipe handle - reading end. */
+ RTPIPE hPipeStderrR;
+ /** The stderr pipe handle - writing end. */
+ RTPIPE hPipeStderrW;
+ /** The stdin pipe handle - reading end. */
+ RTPIPE hPipeStdinR;
+ /** The stind pipe handle - writing end. */
+ RTPIPE hPipeStdinW;
+ /** The stdout handle. */
+ RTHANDLE StdoutHandle;
+ /** The stderr handle. */
+ RTHANDLE StderrHandle;
+ /** The stdin handle. */
+ RTHANDLE StdinHandle;
+ /** The pollset to monitor. */
+ RTPOLLSET hPollSet;
+ /** The process to monitor. */
+ RTPROCESS hProc;
+ /** Execution time of the process. */
+ RTMSINTERVAL msExec;
+ /** Current input data pointer. */
+ uint8_t *pbInputCur;
+ /** Number of bytes left for the input. */
+ size_t cbInputLeft;
+ /** The stdout data buffer. */
+ RTFUZZOBSSTDOUTERRBUF StdOutBuf;
+ /** The stderr data buffer. */
+ RTFUZZOBSSTDOUTERRBUF StdErrBuf;
+ /** Modified arguments vector - variable in size. */
+ char *apszArgs[1];
+} RTFUZZOBSEXECCTX;
+/** Pointer to an execution context. */
+typedef RTFUZZOBSEXECCTX *PRTFUZZOBSEXECCTX;
+/** Pointer to an execution context pointer. */
+typedef PRTFUZZOBSEXECCTX *PPRTFUZZOBSEXECCTX;
+
+
+/**
+ * A variable descriptor.
+ */
+typedef struct RTFUZZOBSVARIABLE
+{
+ /** The variable. */
+ const char *pszVar;
+ /** Length of the variable in characters - excluding the terminator. */
+ uint32_t cchVar;
+ /** The replacement value. */
+ const char *pszVal;
+} RTFUZZOBSVARIABLE;
+/** Pointer to a variable descriptor. */
+typedef RTFUZZOBSVARIABLE *PRTFUZZOBSVARIABLE;
+
+
+/**
+ * Initializes the given stdout/stderr buffer.
+ *
+ * @returns nothing.
+ * @param pBuf The buffer to initialize.
+ */
+static void rtFuzzObsStdOutErrBufInit(PRTFUZZOBSSTDOUTERRBUF pBuf)
+{
+ pBuf->cbBuf = 0;
+ pBuf->cbBufMax = 0;
+ pBuf->pbBase = NULL;
+}
+
+
+/**
+ * Frees all allocated resources in the given stdout/stderr buffer.
+ *
+ * @returns nothing.
+ * @param pBuf The buffer to free.
+ */
+static void rtFuzzObsStdOutErrBufFree(PRTFUZZOBSSTDOUTERRBUF pBuf)
+{
+ if (pBuf->pbBase)
+ RTMemFree(pBuf->pbBase);
+}
+
+
+/**
+ * Clears the given stdout/stderr buffer.
+ *
+ * @returns nothing.
+ * @param pBuf The buffer to clear.
+ */
+static void rtFuzzObsStdOutErrBufClear(PRTFUZZOBSSTDOUTERRBUF pBuf)
+{
+ pBuf->cbBuf = 0;
+}
+
+
+/**
+ * Fills the given stdout/stderr buffer from the given pipe.
+ *
+ * @returns IPRT status code.
+ * @param pBuf The buffer to fill.
+ * @param hPipeRead The pipe to read from.
+ */
+static int rtFuzzObsStdOutErrBufFill(PRTFUZZOBSSTDOUTERRBUF pBuf, RTPIPE hPipeRead)
+{
+ int rc = VINF_SUCCESS;
+
+ size_t cbRead = 0;
+ size_t cbThisRead = 0;
+ do
+ {
+ cbThisRead = pBuf->cbBufMax - pBuf->cbBuf;
+ if (!cbThisRead)
+ {
+ /* Try to increase the buffer. */
+ uint8_t *pbNew = (uint8_t *)RTMemRealloc(pBuf->pbBase, pBuf->cbBufMax + _4K);
+ if (RT_LIKELY(pbNew))
+ {
+ pBuf->cbBufMax += _4K;
+ pBuf->pbBase = pbNew;
+ }
+ cbThisRead = pBuf->cbBufMax - pBuf->cbBuf;
+ }
+
+ if (cbThisRead)
+ {
+ rc = RTPipeRead(hPipeRead, pBuf->pbBase + pBuf->cbBuf, cbThisRead, &cbRead);
+ if (RT_SUCCESS(rc))
+ pBuf->cbBuf += cbRead;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ } while ( RT_SUCCESS(rc)
+ && cbRead == cbThisRead);
+
+ return rc;
+}
+
+
+/**
+ * Writes the given stdout/stderr buffer to the given filename.
+ *
+ * @returns IPRT status code.
+ * @param pBuf The buffer to write.
+ * @param pszFilename The filename to write the buffer to.
+ */
+static int rtFuzzStdOutErrBufWriteToFile(PRTFUZZOBSSTDOUTERRBUF pBuf, const char *pszFilename)
+{
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pBuf->pbBase, pBuf->cbBuf, NULL);
+ AssertRC(rc);
+ RTFileClose(hFile);
+
+ if (RT_FAILURE(rc))
+ RTFileDelete(pszFilename);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Replaces a variable with its value.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param ppszNew In/Out.
+ * @param pcchNew In/Out. (Messed up on failure.)
+ * @param offVar Variable offset.
+ * @param cchVar Variable length.
+ * @param pszValue The value.
+ * @param cchValue Value length.
+ */
+static int rtFuzzObsReplaceStringVariable(char **ppszNew, size_t *pcchNew, size_t offVar, size_t cchVar,
+ const char *pszValue, size_t cchValue)
+{
+ size_t const cchAfter = *pcchNew - offVar - cchVar;
+ if (cchVar < cchValue)
+ {
+ *pcchNew += cchValue - cchVar;
+ int rc = RTStrRealloc(ppszNew, *pcchNew + 1);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ char *pszNew = *ppszNew;
+ memmove(&pszNew[offVar + cchValue], &pszNew[offVar + cchVar], cchAfter + 1);
+ memcpy(&pszNew[offVar], pszValue, cchValue);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Replace the variables found in the source string, returning a new string that
+ * lives on the string heap.
+ *
+ * @returns IPRT status code.
+ * @param pszSrc The source string.
+ * @param paVars Pointer to the array of known variables.
+ * @param ppszNew Where to return the new string.
+ */
+static int rtFuzzObsReplaceStringVariables(const char *pszSrc, PRTFUZZOBSVARIABLE paVars, char **ppszNew)
+{
+ /* Lazy approach that employs memmove. */
+ int rc = VINF_SUCCESS;
+ size_t cchNew = strlen(pszSrc);
+ char *pszNew = RTStrDup(pszSrc);
+
+ if (paVars)
+ {
+ char *pszDollar = pszNew;
+ while ((pszDollar = strchr(pszDollar, '$')) != NULL)
+ {
+ if (pszDollar[1] == '{')
+ {
+ const char *pszEnd = strchr(&pszDollar[2], '}');
+ if (pszEnd)
+ {
+ size_t const cchVar = pszEnd - pszDollar + 1; /* includes "${}" */
+ size_t offDollar = pszDollar - pszNew;
+ PRTFUZZOBSVARIABLE pVar = paVars;
+ while (pVar->pszVar != NULL)
+ {
+ if ( cchVar == pVar->cchVar
+ && !memcmp(pszDollar, pVar->pszVar, cchVar))
+ {
+ size_t const cchValue = strlen(pVar->pszVal);
+ rc = rtFuzzObsReplaceStringVariable(&pszNew, &cchNew, offDollar,
+ cchVar, pVar->pszVal, cchValue);
+ offDollar += cchValue;
+ break;
+ }
+
+ pVar++;
+ }
+
+ pszDollar = &pszNew[offDollar];
+
+ if (RT_FAILURE(rc))
+ {
+ RTStrFree(pszNew);
+ *ppszNew = NULL;
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ *ppszNew = pszNew;
+ return rc;
+}
+
+/**
+ * Prepares the argument vector for the child process.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ * @param pExecCtx The execution context to prepare the argument vector for.
+ * @param paVars Pointer to the array of known variables.
+ */
+static int rtFuzzObsExecCtxArgvPrepare(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTFUZZOBSVARIABLE paVars)
+{
+ int rc = VINF_SUCCESS;
+ for (unsigned i = 0; i < pThis->cArgs && RT_SUCCESS(rc); i++)
+ rc = rtFuzzObsReplaceStringVariables(pThis->papszArgs[i], paVars, &pExecCtx->apszArgs[i]);
+
+ return rc;
+}
+
+
+/**
+ * Creates a new execution context.
+ *
+ * @returns IPRT status code.
+ * @param ppExecCtx Where to store the pointer to the execution context on success.
+ * @param pThis The internal fuzzing observer state.
+ */
+static int rtFuzzObsExecCtxCreate(PPRTFUZZOBSEXECCTX ppExecCtx, PRTFUZZOBSINT pThis)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZOBSEXECCTX pExecCtx = (PRTFUZZOBSEXECCTX)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZOBSEXECCTX, apszArgs[pThis->cArgs + 1]));
+ if (RT_LIKELY(pExecCtx))
+ {
+ pExecCtx->hPipeStdoutR = NIL_RTPIPE;
+ pExecCtx->hPipeStdoutW = NIL_RTPIPE;
+ pExecCtx->hPipeStderrR = NIL_RTPIPE;
+ pExecCtx->hPipeStderrW = NIL_RTPIPE;
+ pExecCtx->hPipeStdinR = NIL_RTPIPE;
+ pExecCtx->hPipeStdinW = NIL_RTPIPE;
+ pExecCtx->hPollSet = NIL_RTPOLLSET;
+ pExecCtx->hProc = NIL_RTPROCESS;
+ pExecCtx->msExec = 0;
+ rtFuzzObsStdOutErrBufInit(&pExecCtx->StdOutBuf);
+ rtFuzzObsStdOutErrBufInit(&pExecCtx->StdErrBuf);
+
+ rc = RTPollSetCreate(&pExecCtx->hPollSet);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPipeCreate(&pExecCtx->hPipeStdoutR, &pExecCtx->hPipeStdoutW, RTPIPE_C_INHERIT_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ RTHANDLE Handle;
+ Handle.enmType = RTHANDLETYPE_PIPE;
+ Handle.u.hPipe = pExecCtx->hPipeStdoutR;
+ rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_READ, RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT);
+ AssertRC(rc);
+
+ rc = RTPipeCreate(&pExecCtx->hPipeStderrR, &pExecCtx->hPipeStderrW, RTPIPE_C_INHERIT_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ Handle.u.hPipe = pExecCtx->hPipeStderrR;
+ rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_READ, RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR);
+ AssertRC(rc);
+
+ /* Create the stdin pipe handles if not a file input. */
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN || pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
+ {
+ rc = RTPipeCreate(&pExecCtx->hPipeStdinR, &pExecCtx->hPipeStdinW, RTPIPE_C_INHERIT_READ);
+ if (RT_SUCCESS(rc))
+ {
+ pExecCtx->StdinHandle.enmType = RTHANDLETYPE_PIPE;
+ pExecCtx->StdinHandle.u.hPipe = pExecCtx->hPipeStdinR;
+
+ Handle.u.hPipe = pExecCtx->hPipeStdinW;
+ rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_WRITE, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
+ AssertRC(rc);
+ }
+ }
+ else
+ {
+ pExecCtx->StdinHandle.enmType = RTHANDLETYPE_PIPE;
+ pExecCtx->StdinHandle.u.hPipe = NIL_RTPIPE;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pExecCtx->StdoutHandle.enmType = RTHANDLETYPE_PIPE;
+ pExecCtx->StdoutHandle.u.hPipe = pExecCtx->hPipeStdoutW;
+ pExecCtx->StderrHandle.enmType = RTHANDLETYPE_PIPE;
+ pExecCtx->StderrHandle.u.hPipe = pExecCtx->hPipeStderrW;
+ *ppExecCtx = pExecCtx;
+ return VINF_SUCCESS;
+ }
+
+ RTPipeClose(pExecCtx->hPipeStderrR);
+ RTPipeClose(pExecCtx->hPipeStderrW);
+ }
+
+ RTPipeClose(pExecCtx->hPipeStdoutR);
+ RTPipeClose(pExecCtx->hPipeStdoutW);
+ }
+
+ RTPollSetDestroy(pExecCtx->hPollSet);
+ }
+
+ RTMemFree(pExecCtx);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Destroys the given execution context.
+ *
+ * @returns nothing.
+ * @param pThis The internal fuzzing observer state.
+ * @param pExecCtx The execution context to destroy.
+ */
+static void rtFuzzObsExecCtxDestroy(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx)
+{
+ RTPipeClose(pExecCtx->hPipeStdoutR);
+ RTPipeClose(pExecCtx->hPipeStdoutW);
+ RTPipeClose(pExecCtx->hPipeStderrR);
+ RTPipeClose(pExecCtx->hPipeStderrW);
+
+ if ( pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN
+ || pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
+ {
+ RTPipeClose(pExecCtx->hPipeStdinR);
+ RTPipeClose(pExecCtx->hPipeStdinW);
+ }
+
+ RTPollSetDestroy(pExecCtx->hPollSet);
+ char **ppszArg = &pExecCtx->apszArgs[0];
+ while (*ppszArg != NULL)
+ {
+ RTStrFree(*ppszArg);
+ ppszArg++;
+ }
+
+ rtFuzzObsStdOutErrBufFree(&pExecCtx->StdOutBuf);
+ rtFuzzObsStdOutErrBufFree(&pExecCtx->StdErrBuf);
+ RTMemFree(pExecCtx);
+}
+
+
+/**
+ * Runs the client binary pumping all data back and forth waiting for the client to finish.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_TIMEOUT if the client didn't finish in the given deadline and was killed.
+ * @param pThis The internal fuzzing observer state.
+ * @param pExecCtx The execution context.
+ * @param pProcStat Where to store the process exit status on success.
+ */
+static int rtFuzzObsExecCtxClientRun(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTPROCSTATUS pProcStat)
+{
+ rtFuzzObsStdOutErrBufClear(&pExecCtx->StdOutBuf);
+ rtFuzzObsStdOutErrBufClear(&pExecCtx->StdErrBuf);
+
+ int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], RTENV_DEFAULT, 0 /*fFlags*/, &pExecCtx->StdinHandle,
+ &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, NULL, &pExecCtx->hProc);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t tsMilliesStart = RTTimeSystemMilliTS();
+ for (;;)
+ {
+ /* Wait a bit for something to happen on one of the pipes. */
+ uint32_t fEvtsRecv = 0;
+ uint32_t idEvt = 0;
+ rc = RTPoll(pExecCtx->hPollSet, 10 /*cMillies*/, &fEvtsRecv, &idEvt);
+ if (RT_SUCCESS(rc))
+ {
+ if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT)
+ {
+ Assert(fEvtsRecv & RTPOLL_EVT_READ);
+ rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdOutBuf, pExecCtx->hPipeStdoutR);
+ AssertRC(rc);
+ }
+ else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
+ {
+ Assert(fEvtsRecv & RTPOLL_EVT_READ);
+ rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdErrBuf, pExecCtx->hPipeStderrR);
+ AssertRC(rc);
+ }
+ else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN)
+ {
+ /* Feed the next input. */
+ Assert(fEvtsRecv & RTPOLL_EVT_WRITE);
+ size_t cbWritten = 0;
+ rc = RTPipeWrite(pExecCtx->hPipeStdinW, pExecCtx->pbInputCur, pExecCtx->cbInputLeft, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ pExecCtx->cbInputLeft -= cbWritten;
+ if (!pExecCtx->cbInputLeft)
+ {
+ /* Close stdin pipe. */
+ rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
+ AssertRC(rc);
+ RTPipeClose(pExecCtx->hPipeStdinW);
+ }
+ }
+ }
+ else
+ AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
+ }
+ else
+ Assert(rc == VERR_TIMEOUT);
+
+ /* Check the process status. */
+ rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
+ if (RT_SUCCESS(rc))
+ break;
+ else
+ {
+ Assert(rc == VERR_PROCESS_RUNNING);
+ /* Check whether we reached the limit. */
+ if (RTTimeSystemMilliTS() - tsMilliesStart > pThis->msWaitMax)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+ } /* for (;;) */
+
+ /* Kill the process on a timeout. */
+ if (rc == VERR_TIMEOUT)
+ {
+ int rc2 = RTProcTerminate(pExecCtx->hProc);
+ AssertRC(rc2);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Runs the fuzzing aware client binary pumping all data back and forth waiting for the client to crash.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_TIMEOUT if the client didn't finish in the given deadline and was killed.
+ * @param pThis The internal fuzzing observer state.
+ * @param pExecCtx The execution context.
+ * @param pProcStat Where to store the process exit status on success.
+ */
+static int rtFuzzObsExecCtxClientRunFuzzingAware(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTPROCSTATUS pProcStat)
+{
+ rtFuzzObsStdOutErrBufClear(&pExecCtx->StdOutBuf);
+ rtFuzzObsStdOutErrBufClear(&pExecCtx->StdErrBuf);
+
+ int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], RTENV_DEFAULT, 0 /*fFlags*/, &pExecCtx->StdinHandle,
+ &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, NULL, &pExecCtx->hProc);
+ if (RT_SUCCESS(rc))
+ {
+ /* Send the initial fuzzing context state over to the client. */
+ void *pvState = NULL;
+ size_t cbState = 0;
+ rc = RTFuzzCtxStateExport(pThis->hFuzzCtx, &pvState, &cbState);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbStateWr = (uint32_t)cbState;
+ rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, &cbStateWr, sizeof(cbStateWr), NULL);
+ rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, pvState, cbState, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
+ AssertRC(rc);
+
+ uint64_t tsMilliesLastSignal = RTTimeSystemMilliTS();
+ uint32_t cFuzzedInputs = 0;
+ for (;;)
+ {
+ /* Wait a bit for something to happen on one of the pipes. */
+ uint32_t fEvtsRecv = 0;
+ uint32_t idEvt = 0;
+ rc = RTPoll(pExecCtx->hPollSet, 10 /*cMillies*/, &fEvtsRecv, &idEvt);
+ if (RT_SUCCESS(rc))
+ {
+ if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT)
+ {
+ Assert(fEvtsRecv & RTPOLL_EVT_READ);
+ for (;;)
+ {
+ char achBuf[512];
+ size_t cbRead = 0;
+ rc = RTPipeRead(pExecCtx->hPipeStdoutR, &achBuf[0], sizeof(achBuf), &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ if (!cbRead)
+ break;
+
+ tsMilliesLastSignal = RTTimeMilliTS();
+ for (unsigned i = 0; i < cbRead; i++)
+ {
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
+
+ if (achBuf[i] == '.')
+ cFuzzedInputs++;
+ else if (achBuf[i] == 'A')
+ {
+ /** @todo Advance our fuzzer to get the added input. */
+ }
+ }
+ }
+ else
+ break;
+ }
+ AssertRC(rc);
+ }
+ else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
+ {
+ Assert(fEvtsRecv & RTPOLL_EVT_READ);
+ rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdErrBuf, pExecCtx->hPipeStderrR);
+ AssertRC(rc);
+ }
+ else
+ AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
+ }
+ else
+ Assert(rc == VERR_TIMEOUT);
+
+ /* Check the process status. */
+ rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
+ if (RT_SUCCESS(rc))
+ break;
+ else
+ {
+ Assert(rc == VERR_PROCESS_RUNNING);
+ /* Check when the last response from the client was. */
+ if (RTTimeSystemMilliTS() - tsMilliesLastSignal > pThis->msWaitMax)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+ } /* for (;;) */
+
+ /* Kill the process on a timeout. */
+ if (rc == VERR_TIMEOUT)
+ {
+ int rc2 = RTProcTerminate(pExecCtx->hProc);
+ AssertRC(rc2);
+ }
+ }
+ }
+ }
+
+ RTHANDLE Handle;
+ Handle.enmType = RTHANDLETYPE_PIPE;
+ Handle.u.hPipe = pExecCtx->hPipeStdinW;
+ rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_WRITE, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
+ AssertRC(rc);
+
+ return rc;
+}
+
+
+/**
+ * Adds the input to the results directory.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ * @param hFuzzInput Fuzzing input handle to write.
+ * @param pExecCtx Execution context.
+ */
+static int rtFuzzObsAddInputToResults(PRTFUZZOBSINT pThis, RTFUZZINPUT hFuzzInput, PRTFUZZOBSEXECCTX pExecCtx)
+{
+ char aszDigest[RTMD5_STRING_LEN + 1];
+ int rc = RTFuzzInputQueryDigestString(hFuzzInput, &aszDigest[0], sizeof(aszDigest));
+ if (RT_SUCCESS(rc))
+ {
+ /* Create a directory. */
+ char szPath[RTPATH_MAX];
+ rc = RTPathJoin(szPath, sizeof(szPath), pThis->pszResultsDir, &aszDigest[0]);
+ AssertRC(rc);
+
+ rc = RTDirCreate(&szPath[0], 0700, 0 /*fCreate*/);
+ if (RT_SUCCESS(rc))
+ {
+ /* Write the input. */
+ char szTmp[RTPATH_MAX];
+ rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "input");
+ AssertRC(rc);
+
+ rc = RTFuzzInputWriteToFile(hFuzzInput, &szTmp[0]);
+ if (RT_SUCCESS(rc))
+ {
+ /* Stdout and Stderr. */
+ rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stdout");
+ AssertRC(rc);
+ rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stderr");
+ AssertRC(rc);
+ rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Fuzzing observer worker loop.
+ *
+ * @returns IPRT status code.
+ * @param hThrd The thread handle.
+ * @param pvUser Opaque user data.
+ */
+static DECLCALLBACK(int) rtFuzzObsWorkerLoop(RTTHREAD hThrd, void *pvUser)
+{
+ PRTFUZZOBSTHRD pObsThrd = (PRTFUZZOBSTHRD)pvUser;
+ PRTFUZZOBSINT pThis = pObsThrd->pFuzzObs;
+ PRTFUZZOBSEXECCTX pExecCtx = NULL;
+
+ int rc = rtFuzzObsExecCtxCreate(&pExecCtx, pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ while (!pObsThrd->fShutdown)
+ {
+ char szInput[RTPATH_MAX];
+
+ /* Wait for work. */
+ rc = RTThreadUserWait(hThrd, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ if (pObsThrd->fShutdown)
+ break;
+
+ if (!ASMAtomicXchgBool(&pObsThrd->fNewInput, false))
+ continue;
+
+ AssertPtr(pObsThrd->hFuzzInput);
+
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
+ {
+ char szFilename[32];
+
+ ssize_t cbBuf = RTStrPrintf2(&szFilename[0], sizeof(szFilename), "%u", pObsThrd->idObs);
+ Assert(cbBuf > 0); RT_NOREF(cbBuf);
+
+ RT_ZERO(szInput);
+ rc = RTPathJoin(szInput, sizeof(szInput), pThis->pszTmpDir, &szFilename[0]);
+ AssertRC(rc);
+
+ rc = RTFuzzInputWriteToFile(pObsThrd->hFuzzInput, &szInput[0]);
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZOBSVARIABLE aVar[2] = {
+ { "${INPUT}", sizeof("${INPUT}") - 1, &szInput[0] },
+ { NULL, 0, NULL }
+ };
+ rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, &aVar[0]);
+ }
+ }
+ else if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN)
+ {
+ rc = RTFuzzInputQueryData(pObsThrd->hFuzzInput, (void **)&pExecCtx->pbInputCur, &pExecCtx->cbInputLeft);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, NULL);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS ProcSts;
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
+ rc = rtFuzzObsExecCtxClientRunFuzzingAware(pThis, pExecCtx, &ProcSts);
+ else
+ {
+ rc = rtFuzzObsExecCtxClientRun(pThis, pExecCtx, &ProcSts);
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ if (ProcSts.enmReason != RTPROCEXITREASON_NORMAL)
+ {
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsCrash);
+ rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
+ }
+ }
+ else if (rc == VERR_TIMEOUT)
+ {
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsHang);
+ rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
+ }
+ else
+ AssertFailed();
+
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
+ RTFileDelete(&szInput[0]);
+ }
+
+ ASMAtomicBitSet(&pThis->bmEvt, pObsThrd->idObs);
+ RTSemEventSignal(pThis->hEvtGlobal);
+ }
+
+ rtFuzzObsExecCtxDestroy(pThis, pExecCtx);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Fuzzing observer master worker loop.
+ *
+ * @returns IPRT status code.
+ * @param hThread The thread handle.
+ * @param pvUser Opaque user data.
+ */
+static DECLCALLBACK(int) rtFuzzObsMasterLoop(RTTHREAD hThread, void *pvUser)
+{
+ RT_NOREF(hThread);
+ int rc = VINF_SUCCESS;
+ PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)pvUser;
+
+ RTThreadUserSignal(hThread);
+
+ while ( !pThis->fShutdown
+ && RT_SUCCESS(rc))
+ {
+ uint64_t bmEvt = ASMAtomicXchgU64(&pThis->bmEvt, 0);
+ uint32_t idxObs = 0;
+ while (bmEvt != 0)
+ {
+ if (bmEvt & 0x1)
+ {
+ /* Create a new input for this observer and kick it. */
+ PRTFUZZOBSTHRD pObsThrd = &pThis->paObsThreads[idxObs];
+
+ /* Release the old input. */
+ if (pObsThrd->hFuzzInput)
+ {
+ if (pObsThrd->fKeepInput)
+ {
+ int rc2 = RTFuzzInputAddToCtxCorpus(pObsThrd->hFuzzInput);
+ Assert(RT_SUCCESS(rc2) || rc2 == VERR_ALREADY_EXISTS); RT_NOREF(rc2);
+ pObsThrd->fKeepInput= false;
+ }
+ RTFuzzInputRelease(pObsThrd->hFuzzInput);
+ }
+
+ rc = RTFuzzCtxInputGenerate(pThis->hFuzzCtx, &pObsThrd->hFuzzInput);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteBool(&pObsThrd->fNewInput, true);
+ RTThreadUserSignal(pObsThrd->hThread);
+ }
+ }
+
+ idxObs++;
+ bmEvt >>= 1;
+ }
+
+ rc = RTSemEventWait(pThis->hEvtGlobal, RT_INDEFINITE_WAIT);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes the given worker thread structure.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ * @param iObs Observer ID.
+ * @param pObsThrd The observer thread structure.
+ */
+static int rtFuzzObsWorkerThreadInit(PRTFUZZOBSINT pThis, uint32_t idObs, PRTFUZZOBSTHRD pObsThrd)
+{
+ pObsThrd->pFuzzObs = pThis;
+ pObsThrd->hFuzzInput = NULL;
+ pObsThrd->idObs = idObs;
+ pObsThrd->fShutdown = false;
+
+ ASMAtomicBitSet(&pThis->bmEvt, idObs);
+ return RTThreadCreate(&pObsThrd->hThread, rtFuzzObsWorkerLoop, pObsThrd, 0, RTTHREADTYPE_IO,
+ RTTHREADFLAGS_WAITABLE, "Fuzz-Worker");
+}
+
+
+/**
+ * Creates the given amount of worker threads and puts them into waiting state.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ * @param cThreads Number of worker threads to create.
+ */
+static int rtFuzzObsWorkersCreate(PRTFUZZOBSINT pThis, uint32_t cThreads)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZOBSTHRD paObsThreads = (PRTFUZZOBSTHRD)RTMemAllocZ(cThreads * sizeof(RTFUZZOBSTHRD));
+ if (RT_LIKELY(paObsThreads))
+ {
+ for (unsigned i = 0; i < cThreads && RT_SUCCESS(rc); i++)
+ {
+ rc = rtFuzzObsWorkerThreadInit(pThis, i, &paObsThreads[i]);
+ if (RT_FAILURE(rc))
+ {
+ /* Rollback. */
+
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pThis->paObsThreads = paObsThreads;
+ pThis->cThreads = cThreads;
+ }
+ else
+ RTMemFree(paObsThreads);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Creates the global worker thread managing the input creation and other worker threads.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ */
+static int rtFuzzObsMasterCreate(PRTFUZZOBSINT pThis)
+{
+ pThis->fShutdown = false;
+
+ int rc = RTSemEventCreate(&pThis->hEvtGlobal);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pThis->hThreadGlobal, rtFuzzObsMasterLoop, pThis, 0, RTTHREADTYPE_IO,
+ RTTHREADFLAGS_WAITABLE, "Fuzz-Master");
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadUserWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT);
+ }
+ else
+ {
+ RTSemEventDestroy(pThis->hEvtGlobal);
+ pThis->hEvtGlobal = NIL_RTSEMEVENT;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsCreate(PRTFUZZOBS phFuzzObs)
+{
+ AssertPtrReturn(phFuzzObs, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->pszBinary = NULL;
+ pThis->papszArgs = NULL;
+ pThis->msWaitMax = 1000;
+ pThis->hThreadGlobal = NIL_RTTHREAD;
+ pThis->hEvtGlobal = NIL_RTSEMEVENT;
+ pThis->bmEvt = 0;
+ pThis->cThreads = 0;
+ pThis->paObsThreads = NULL;
+ pThis->tsLastStats = RTTimeMilliTS();
+ pThis->Stats.cFuzzedInputsPerSec = 0;
+ pThis->Stats.cFuzzedInputs = 0;
+ pThis->Stats.cFuzzedInputsHang = 0;
+ pThis->Stats.cFuzzedInputsCrash = 0;
+ rc = RTFuzzCtxCreate(&pThis->hFuzzCtx);
+ if (RT_SUCCESS(rc))
+ {
+ *phFuzzObs = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsDestroy(RTFUZZOBS hFuzzObs)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ RTFuzzObsExecStop(hFuzzObs);
+
+ /* Clean up all acquired resources. */
+ for (unsigned i = 0; i < pThis->cArgs; i++)
+ RTStrFree(pThis->papszArgs[i]);
+
+ RTMemFree(pThis->papszArgs);
+
+ if (pThis->hEvtGlobal != NIL_RTSEMEVENT)
+ RTSemEventDestroy(pThis->hEvtGlobal);
+
+ if (pThis->pszResultsDir)
+ RTStrFree(pThis->pszResultsDir);
+ if (pThis->pszTmpDir)
+ RTStrFree(pThis->pszTmpDir);
+ if (pThis->pszBinary)
+ RTStrFree(pThis->pszBinary);
+ RTFuzzCtxRelease(pThis->hFuzzCtx);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzObsQueryCtx(RTFUZZOBS hFuzzObs, PRTFUZZCTX phFuzzCtx)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+
+ RTFuzzCtxRetain(pThis->hFuzzCtx);
+ *phFuzzCtx = pThis->hFuzzCtx;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzObsQueryStats(RTFUZZOBS hFuzzObs, PRTFUZZOBSSTATS pStats)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pStats, VERR_INVALID_POINTER);
+
+ uint64_t tsStatsQuery = RTTimeMilliTS();
+ uint32_t cFuzzedInputsPerSec = ASMAtomicXchgU32(&pThis->Stats.cFuzzedInputsPerSec, 0);
+
+ pStats->cFuzzedInputsCrash = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsCrash);
+ pStats->cFuzzedInputsHang = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsHang);
+ pStats->cFuzzedInputs = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputs);
+ uint64_t cPeriodSec = (tsStatsQuery - pThis->tsLastStats) / 1000;
+ if (cPeriodSec)
+ {
+ pStats->cFuzzedInputsPerSec = cFuzzedInputsPerSec / cPeriodSec;
+ pThis->cFuzzedInputsPerSecLast = pStats->cFuzzedInputsPerSec;
+ pThis->tsLastStats = tsStatsQuery;
+ }
+ else
+ pStats->cFuzzedInputsPerSec = pThis->cFuzzedInputsPerSecLast;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzObsSetTmpDirectory(RTFUZZOBS hFuzzObs, const char *pszTmp)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszTmp, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ pThis->pszTmpDir = RTStrDup(pszTmp);
+ if (!pThis->pszTmpDir)
+ rc = VERR_NO_STR_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsSetResultDirectory(RTFUZZOBS hFuzzObs, const char *pszResults)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszResults, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ pThis->pszResultsDir = RTStrDup(pszResults);
+ if (!pThis->pszResultsDir)
+ rc = VERR_NO_STR_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsSetTestBinary(RTFUZZOBS hFuzzObs, const char *pszBinary, RTFUZZOBSINPUTCHAN enmInputChan)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszBinary, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ pThis->enmInputChan = enmInputChan;
+ pThis->pszBinary = RTStrDup(pszBinary);
+ if (RT_UNLIKELY(!pThis->pszBinary))
+ rc = VERR_NO_STR_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsSetTestBinaryArgs(RTFUZZOBS hFuzzObs, const char * const *papszArgs, unsigned cArgs)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ char **papszArgsOld = pThis->papszArgs;
+ if (papszArgs)
+ {
+ pThis->papszArgs = (char **)RTMemAllocZ(sizeof(char **) * (cArgs + 1));
+ if (RT_LIKELY(pThis->papszArgs))
+ {
+ for (unsigned i = 0; i < cArgs; i++)
+ {
+ pThis->papszArgs[i] = RTStrDup(papszArgs[i]);
+ if (RT_UNLIKELY(!pThis->papszArgs[i]))
+ {
+ while (i > 0)
+ {
+ i--;
+ RTStrFree(pThis->papszArgs[i]);
+ }
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pThis->papszArgs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(rc))
+ pThis->papszArgs = papszArgsOld;
+ else
+ pThis->cArgs = cArgs;
+ }
+ else
+ {
+ pThis->papszArgs = NULL;
+ pThis->cArgs = 0;
+ if (papszArgsOld)
+ {
+ char **ppsz = papszArgsOld;
+ while (*ppsz != NULL)
+ {
+ RTStrFree(*ppsz);
+ ppsz++;
+ }
+ RTMemFree(papszArgsOld);
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsExecStart(RTFUZZOBS hFuzzObs, uint32_t cProcs)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(cProcs <= sizeof(uint64_t) * 8, VERR_INVALID_PARAMETER);
+ AssertReturn( pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE
+ || pThis->pszTmpDir != NULL,
+ VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (!cProcs)
+ cProcs = RT_MIN(RTMpGetPresentCoreCount(), sizeof(uint64_t) * 8);
+
+ /* Spin up the worker threads first. */
+ rc = rtFuzzObsWorkersCreate(pThis, cProcs);
+ if (RT_SUCCESS(rc))
+ {
+ /* Spin up the global thread. */
+ rc = rtFuzzObsMasterCreate(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsExecStop(RTFUZZOBS hFuzzObs)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ /* Wait for the master thread to terminate. */
+ if (pThis->hThreadGlobal != NIL_RTTHREAD)
+ {
+ ASMAtomicXchgBool(&pThis->fShutdown, true);
+ RTSemEventSignal(pThis->hEvtGlobal);
+ RTThreadWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT, NULL);
+ pThis->hThreadGlobal = NIL_RTTHREAD;
+ }
+
+ /* Destroy the workers. */
+ if (pThis->paObsThreads)
+ {
+ for (unsigned i = 0; i < pThis->cThreads; i++)
+ {
+ PRTFUZZOBSTHRD pThrd = &pThis->paObsThreads[i];
+ ASMAtomicXchgBool(&pThrd->fShutdown, true);
+ RTThreadUserSignal(pThrd->hThread);
+ RTThreadWait(pThrd->hThread, RT_INDEFINITE_WAIT, NULL);
+ if (pThrd->hFuzzInput)
+ RTFuzzInputRelease(pThrd->hFuzzInput);
+ }
+
+ RTMemFree(pThis->paObsThreads);
+ pThis->paObsThreads = NULL;
+ pThis->cThreads = 0;
+ }
+
+ RTSemEventDestroy(pThis->hEvtGlobal);
+ pThis->hEvtGlobal = NIL_RTSEMEVENT;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/fuzz/fuzz.cpp b/src/VBox/Runtime/common/fuzz/fuzz.cpp
new file mode 100644
index 00000000..a74d130b
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz.cpp
@@ -0,0 +1,1217 @@
+/* $Id: fuzz.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, core.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/list.h>
+#include <iprt/md5.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/vfs.h>
+
+
+#define RTFUZZCTX_MAGIC UINT32_C(0xdeadc0de) /** @todo */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to the internal fuzzer state. */
+typedef struct RTFUZZCTXINT *PRTFUZZCTXINT;
+
+/**
+ * A fuzzing input seed.
+ */
+typedef struct RTFUZZINPUTINT
+{
+ /** The AVL tree core. */
+ AVLU64NODECORE Core;
+ /** Node for the global list. */
+ RTLISTNODE NdInputs;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+/** @todo add magic here (unused padding space on 64-bit hosts). */
+ /** The fuzzer this input belongs to. */
+ PRTFUZZCTXINT pFuzzer;
+ /** Complete MD5 hash of the input data. */
+ uint8_t abMd5Hash[RTMD5_HASH_SIZE];
+ /** Size of the input data. */
+ size_t cbInput;
+ /** Input data - variable in size. */
+ uint8_t abInput[1];
+} RTFUZZINPUTINT;
+/** Pointer to the internal input state. */
+typedef RTFUZZINPUTINT *PRTFUZZINPUTINT;
+/** Pointer to an internal input state pointer. */
+typedef PRTFUZZINPUTINT *PPRTFUZZINPUTINT;
+
+
+/**
+ * Intermediate indexing structure.
+ */
+typedef struct RTFUZZINTERMEDIATE
+{
+ /** The AVL tree core. */
+ AVLU64NODECORE Core;
+ /** The AVL tree for indexing the input seeds (keyed by the lower half of the MD5). */
+ AVLU64TREE TreeSeedsLow;
+} RTFUZZINTERMEDIATE;
+/** Pointer to an intermediate indexing state. */
+typedef RTFUZZINTERMEDIATE *PRTFUZZINTERMEDIATE;
+/** Pointer to an intermediate indexing state pointer. */
+typedef PRTFUZZINTERMEDIATE *PPRTFUZZINTERMEDIATE;
+
+/**
+ * The fuzzer state.
+ */
+typedef struct RTFUZZCTXINT
+{
+ /** Magic value for identification. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The random number generator. */
+ RTRAND hRand;
+ /** The AVL tree for indexing the input seeds (keyed by the upper half of the MD5). */
+ AVLU64TREE TreeSeedsHigh;
+ /** Sequential list of all inputs. */
+ RTLISTANCHOR LstInputs;
+ /** Number of inputs currently in the tree. */
+ uint32_t cInputs;
+ /** The maximum size of one input seed to generate. */
+ size_t cbInputMax;
+ /** Behavioral flags. */
+ uint32_t fFlagsBehavioral;
+} RTFUZZCTXINT;
+
+
+/**
+ * Available mutators enum.
+ */
+typedef enum RTFUZZCTXMUTATOR
+{
+ /** Invalid mutator, not used. */
+ RTFUZZCTXMUTATOR_INVALID = 0,
+ /** Flips a single bit in the input. */
+ RTFUZZCTXMUTATOR_BIT_FLIP,
+ /** Replaces a single byte in the input. */
+ RTFUZZCTXMUTATOR_BYTE_REPLACE,
+ /** Inserts a byte sequence into the input. */
+ RTFUZZCTXMUTATOR_BYTE_SEQUENCE_INSERT,
+ /** Appends a byte sequence to the input. */
+ RTFUZZCTXMUTATOR_BYTE_SEQUENCE_APPEND,
+ /** Deletes a single byte from the input. */
+ RTFUZZCTXMUTATOR_BYTE_DELETE,
+ /** Deletes a sequence of bytes from the input. */
+ RTFUZZCTXMUTATOR_BYTE_SEQUENCE_DELETE,
+ /** Last valid mutator. */
+ RTFUZZCTXMUTATOR_LAST = RTFUZZCTXMUTATOR_BYTE_SEQUENCE_DELETE,
+ /** 32bit hack. */
+ RTFUZZCTXMUTATOR_32BIT_HACK = 0x7fffffff
+} RTFUZZCTXMUTATOR;
+/** Pointer to a mutator enum. */
+typedef RTFUZZCTXMUTATOR *PRTFUZZCTXMUTATOR;
+
+
+/**
+ * The fuzzer state to be exported - all members are stored in little endian form.
+ */
+typedef struct RTFUZZCTXSTATE
+{
+ /** Magic value for identification. */
+ uint32_t u32Magic;
+ /** Size of the PRNG state following in bytes. */
+ uint32_t cbPrng;
+ /** Number of input descriptors following. */
+ uint32_t cInputs;
+ /** Behavioral flags. */
+ uint32_t fFlagsBehavioral;
+ /** Maximum input size to generate. */
+ uint64_t cbInputMax;
+} RTFUZZCTXSTATE;
+/** Pointer to a fuzzing context state. */
+typedef RTFUZZCTXSTATE *PRTFUZZCTXSTATE;
+
+
+/**
+ * Mutator callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pvBuf The input buffer to mutate.
+ * @param cbBuf Size of the buffer in bytes.
+ * @param ppInputMutated Where to store the pointer to the mutated input success.
+ */
+typedef DECLCALLBACK(int) FNRTFUZZCTXMUTATOR(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated);
+/** Pointer to a mutator callback. */
+typedef FNRTFUZZCTXMUTATOR *PFNRTFUZZCTXMUTATOR;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlip(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplace(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsert(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceAppend(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDelete(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDelete(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf,
+ PPRTFUZZINPUTINT ppInputMutated);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of all available mutators.
+ */
+static PFNRTFUZZCTXMUTATOR const g_apfnMutators[] =
+{
+ NULL,
+ rtFuzzCtxMutatorBitFlip,
+ rtFuzzCtxMutatorByteReplace,
+ rtFuzzCtxMutatorByteSequenceInsert,
+ rtFuzzCtxMutatorByteSequenceAppend,
+ rtFuzzCtxMutatorByteDelete,
+ rtFuzzCtxMutatorByteSequenceDelete,
+ NULL
+};
+
+
+
+/**
+ * Tries to locate an input seed with the given input MD5 seed.
+ *
+ * @returns Pointer to the input seed on success or NULL if not found.
+ * @param pThis The fuzzer context instance.
+ * @param pbMd5Hash The MD5 hash to search for.
+ * @param fExact Flag whether to search for an exact match or return the next best fit if no match exists.
+ * @param ppIntermediate Where to store the pointer to the intermediate layer upon success, optional.
+ */
+static PRTFUZZINPUTINT rtFuzzCtxInputLocate(PRTFUZZCTXINT pThis, uint8_t *pbMd5Hash, bool fExact, PPRTFUZZINTERMEDIATE ppIntermediate)
+{
+ PRTFUZZINPUTINT pInput = NULL;
+ uint64_t u64Md5High = *(uint64_t *)&pbMd5Hash[RTMD5_HASH_SIZE / 2];
+ uint64_t u64Md5Low = *(uint64_t *)&pbMd5Hash[0];
+ PRTFUZZINTERMEDIATE pIntermediate = (PRTFUZZINTERMEDIATE)RTAvlU64Get(&pThis->TreeSeedsHigh, u64Md5High);
+ if (!fExact && !pIntermediate)
+ pIntermediate = (PRTFUZZINTERMEDIATE)RTAvlU64GetBestFit(&pThis->TreeSeedsHigh, u64Md5High, true /*fAbove*/);
+ if (!fExact && !pIntermediate)
+ pIntermediate = (PRTFUZZINTERMEDIATE)RTAvlU64GetBestFit(&pThis->TreeSeedsHigh, u64Md5High, false /*fAbove*/);
+
+ if (pIntermediate)
+ {
+ /* 2nd level lookup. */
+ pInput = (PRTFUZZINPUTINT)RTAvlU64Get(&pIntermediate->TreeSeedsLow, u64Md5Low);
+ if (!fExact && !pInput)
+ pInput = (PRTFUZZINPUTINT)RTAvlU64GetBestFit(&pIntermediate->TreeSeedsLow, u64Md5Low, true /*fAbove*/);
+ if (!fExact && !pInput)
+ pInput = (PRTFUZZINPUTINT)RTAvlU64GetBestFit(&pIntermediate->TreeSeedsLow, u64Md5Low, false /*fAbove*/);
+ }
+
+ if (ppIntermediate)
+ *ppIntermediate = pIntermediate;
+
+ return pInput;
+}
+
+
+/**
+ * Adds the given input to the corpus of the given fuzzer context.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pInput The input to add.
+ */
+static int rtFuzzCtxInputAdd(PRTFUZZCTXINT pThis, PRTFUZZINPUTINT pInput)
+{
+ int rc = VINF_SUCCESS;
+ uint64_t u64Md5High = *(uint64_t *)&pInput->abMd5Hash[RTMD5_HASH_SIZE / 2];
+ uint64_t u64Md5Low = *(uint64_t *)&pInput->abMd5Hash[0];
+
+ pInput->Core.Key = u64Md5Low;
+ PRTFUZZINTERMEDIATE pIntermediate = (PRTFUZZINTERMEDIATE)RTAvlU64Get(&pThis->TreeSeedsHigh, u64Md5High);
+ if (!pIntermediate)
+ {
+ pIntermediate = (PRTFUZZINTERMEDIATE)RTMemAllocZ(sizeof(*pIntermediate));
+ if (RT_LIKELY(pIntermediate))
+ {
+ pIntermediate->Core.Key = u64Md5High;
+ pIntermediate->TreeSeedsLow = NULL;
+ bool fIns = RTAvlU64Insert(&pThis->TreeSeedsHigh, &pIntermediate->Core);
+ Assert(fIns); RT_NOREF(fIns);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ AssertPtr(pIntermediate);
+ bool fIns = RTAvlU64Insert(&pIntermediate->TreeSeedsLow, &pInput->Core);
+ if (!fIns)
+ rc = VERR_ALREADY_EXISTS;
+ else
+ {
+ RTListAppend(&pThis->LstInputs, &pInput->NdInputs);
+ pThis->cInputs++;
+ RTFuzzInputRetain(pInput);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Returns a random input from the corpus of the given fuzzer context.
+ *
+ * @returns Pointer to a randomly picked input.
+ * @param pThis The fuzzer context instance.
+ */
+static PRTFUZZINPUTINT rtFuzzCtxInputPickRnd(PRTFUZZCTXINT pThis)
+{
+ /* Generate a random MD5 hash and do a non exact localisation. */
+ uint8_t abDigestRnd[RTMD5_HASH_SIZE];
+ RTRandAdvBytes(pThis->hRand, &abDigestRnd[0], sizeof(abDigestRnd));
+
+ return rtFuzzCtxInputLocate(pThis, &abDigestRnd[0], false /*fExact*/, NULL /*ppIntermediate*/);
+}
+
+
+/**
+ * Clones a given input.
+ *
+ * @returns Pointer to the cloned input or NULL if out of memory.
+ * @param pThis The fuzzer context instance.
+ * @param pvBuf The buffer to clone.
+ * @param cbBuf Size of the buffer in bytes.
+ */
+static PRTFUZZINPUTINT rtFuzzCtxInputClone(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf)
+{
+ PRTFUZZINPUTINT pInpClone = (PRTFUZZINPUTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZINPUTINT, abInput[cbBuf]));
+ if (RT_LIKELY(pInpClone))
+ {
+ pInpClone->cRefs = 1;
+ pInpClone->pFuzzer = pThis,
+ pInpClone->cbInput = cbBuf;
+ memcpy(&pInpClone->abInput[0], pvBuf, cbBuf);
+ }
+
+ return pInpClone;
+}
+
+
+/**
+ * Creates an empty input seed capable of holding the given number of bytes.
+ *
+ * @returns Pointer to the newly created input seed.
+ * @param pThis The fuzzer context instance.
+ * @param cbInput Input seed size in bytes.
+ */
+static PRTFUZZINPUTINT rtFuzzCtxInputCreate(PRTFUZZCTXINT pThis, size_t cbInput)
+{
+ PRTFUZZINPUTINT pInput = (PRTFUZZINPUTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZINPUTINT, abInput[cbInput]));
+ if (RT_LIKELY(pInput))
+ {
+ pInput->pFuzzer = pThis;
+ pInput->cRefs = 1;
+ pInput->cbInput = cbInput;
+ }
+
+ return pInput;
+}
+
+
+/**
+ * Destroys the given input.
+ *
+ * @returns nothing.
+ * @param pInput The input to destroy.
+ */
+static void rtFuzzInputDestroy(PRTFUZZINPUTINT pInput)
+{
+ RTMemFree(pInput);
+}
+
+
+/**
+ * Destorys the given fuzzer context freeing all allocated resources.
+ *
+ * @returns nothing.
+ * @param pThis The fuzzer context instance.
+ */
+static void rtFuzzCtxDestroy(PRTFUZZCTXINT pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/**
+ * Mutator callback - flips a single bit in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlip(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputClone(pThis, pvBuf, cbBuf);
+ if (RT_LIKELY(pInputMutated))
+ {
+ int32_t iBit = RTRandAdvS32Ex(pThis->hRand, 0, (uint32_t)cbBuf * 8 - 1);
+ ASMBitToggle(&pInputMutated->abInput[0], iBit);
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Mutator callback - replaces a single byte in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplace(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputClone(pThis, pvBuf, cbBuf);
+ if (RT_LIKELY(pInputMutated))
+ {
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+ uint32_t offByte = RTRandAdvU32Ex(pThis->hRand, 0, (uint32_t)cbBuf - 1);
+ RTRandAdvBytes(pThis->hRand, &pInputMutated->abInput[offByte], 1);
+ if (pbBuf[offByte] != pInputMutated->abInput[offByte])
+ {
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ RTMemFree(pInputMutated);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Mutator callback - inserts a byte sequence into the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsert(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ if (cbBuf < pThis->cbInputMax)
+ {
+ size_t cbInputMutated = RTRandAdvU32Ex(pThis->hRand, (uint32_t)cbBuf + 1, (uint32_t)pThis->cbInputMax);
+ size_t cbInsert = cbInputMutated - cbBuf;
+ uint32_t offInsert = RTRandAdvU32Ex(pThis->hRand, 0, (uint32_t)cbBuf);
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputCreate(pThis, cbInputMutated);
+ if (RT_LIKELY(pInputMutated))
+ {
+ if (offInsert)
+ memcpy(&pInputMutated->abInput[0], pbBuf, offInsert);
+ RTRandAdvBytes(pThis->hRand, &pInputMutated->abInput[offInsert], cbInsert);
+ memcpy(&pInputMutated->abInput[offInsert + cbInsert], &pbBuf[offInsert], cbBuf - offInsert);
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Mutator callback - appends a byte sequence to the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceAppend(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ if (cbBuf < pThis->cbInputMax)
+ {
+ size_t cbInputMutated = RTRandAdvU32Ex(pThis->hRand, (uint32_t)cbBuf + 1, (uint32_t)pThis->cbInputMax);
+ size_t cbInsert = cbInputMutated - cbBuf;
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputCreate(pThis, cbInputMutated);
+ if (RT_LIKELY(pInputMutated))
+ {
+ memcpy(&pInputMutated->abInput[0], pvBuf, cbBuf);
+ RTRandAdvBytes(pThis->hRand, &pInputMutated->abInput[cbBuf], cbInsert);
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Mutator callback - deletes a single byte in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDelete(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ if (cbBuf > 1)
+ {
+ uint32_t offDelete = RTRandAdvU32Ex(pThis->hRand, 0, (uint32_t)cbBuf - 1);
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputCreate(pThis, cbBuf - 1);
+ if (RT_LIKELY(pInputMutated))
+ {
+ if (offDelete)
+ memcpy(&pInputMutated->abInput[0], pbBuf, offDelete);
+ if (offDelete < cbBuf - 1)
+ memcpy(&pInputMutated->abInput[offDelete], &pbBuf[offDelete + 1], cbBuf - offDelete - 1);
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Mutator callback - deletes a byte sequence in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDelete(PRTFUZZCTXINT pThis, const void *pvBuf, size_t cbBuf, PPRTFUZZINPUTINT ppInputMutated)
+{
+ int rc = VINF_SUCCESS;
+ if (cbBuf > 1)
+ {
+ size_t cbInputMutated = RTRandAdvU32Ex(pThis->hRand, 0, (uint32_t)cbBuf - 1);
+ size_t cbDel = cbBuf - cbInputMutated;
+ uint32_t offDel = RTRandAdvU32Ex(pThis->hRand, 0, (uint32_t)(cbBuf - cbDel));
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+
+ PRTFUZZINPUTINT pInputMutated = rtFuzzCtxInputCreate(pThis, cbInputMutated);
+ if (RT_LIKELY(pInputMutated))
+ {
+ if (offDel)
+ memcpy(&pInputMutated->abInput[0], pbBuf, offDel);
+ memcpy(&pInputMutated->abInput[offDel], &pbBuf[offDel + cbDel], cbBuf - offDel - cbDel);
+ RTMd5(&pInputMutated->abInput[0], pInputMutated->cbInput, &pInputMutated->abMd5Hash[0]);
+ *ppInputMutated = pInputMutated;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static PRTFUZZCTXINT rtFuzzCtxCreateEmpty(void)
+{
+ PRTFUZZCTXINT pThis = (PRTFUZZCTXINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = RTFUZZCTX_MAGIC;
+ pThis->cRefs = 1;
+ pThis->TreeSeedsHigh = NULL;
+ pThis->cbInputMax = UINT32_MAX;
+ pThis->cInputs = 0;
+ pThis->fFlagsBehavioral = 0;
+ RTListInit(&pThis->LstInputs);
+
+ int rc = RTRandAdvCreateParkMiller(&pThis->hRand);
+ if (RT_SUCCESS(rc))
+ {
+ RTRandAdvSeed(pThis->hRand, RTTimeSystemNanoTS());
+ return pThis;
+ }
+
+ RTMemFree(pThis);
+ }
+
+ return NULL;
+}
+
+
+RTDECL(int) RTFuzzCtxCreate(PRTFUZZCTX phFuzzCtx)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PRTFUZZCTXINT pThis = rtFuzzCtxCreateEmpty();
+ if (RT_LIKELY(pThis))
+ {
+ *phFuzzCtx = pThis;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCreateFromState(PRTFUZZCTX phFuzzCtx, const void *pvState, size_t cbState)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvState, VERR_INVALID_POINTER);
+ AssertReturn(cbState > 0, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ if (cbState >= sizeof(RTFUZZCTXSTATE))
+ {
+ RTFUZZCTXSTATE StateImport;
+
+ memcpy(&StateImport, pvState, sizeof(RTFUZZCTXSTATE));
+ if ( RT_LE2H_U32(StateImport.u32Magic) == RTFUZZCTX_MAGIC
+ && RT_LE2H_U32(StateImport.cbPrng) <= cbState - sizeof(RTFUZZCTXSTATE))
+ {
+ PRTFUZZCTXINT pThis = rtFuzzCtxCreateEmpty();
+ if (RT_LIKELY(pThis))
+ {
+ pThis->cbInputMax = (size_t)RT_LE2H_U64(StateImport.cbInputMax);
+ pThis->fFlagsBehavioral = RT_LE2H_U32(StateImport.fFlagsBehavioral);
+
+ uint8_t *pbState = (uint8_t *)pvState;
+ uint32_t cInputs = RT_LE2H_U32(StateImport.cInputs);
+ rc = RTRandAdvRestoreState(pThis->hRand, (const char *)&pbState[sizeof(RTFUZZCTXSTATE)]);
+ if (RT_SUCCESS(rc))
+ {
+ /* Go through the inputs and add them. */
+ pbState += sizeof(RTFUZZCTXSTATE) + RT_LE2H_U32(StateImport.cbPrng);
+ cbState -= sizeof(RTFUZZCTXSTATE) + RT_LE2H_U32(StateImport.cbPrng);
+
+ uint32_t idx = 0;
+ while ( idx < cInputs
+ && RT_SUCCESS(rc))
+ {
+ size_t cbInput = 0;
+ if (cbState >= sizeof(uint32_t))
+ {
+ memcpy(&cbInput, pbState, sizeof(uint32_t));
+ cbInput = RT_LE2H_U32(cbInput);
+ pbState += sizeof(uint32_t);
+ }
+
+ if ( cbInput
+ && cbInput <= cbState)
+ {
+ PRTFUZZINPUTINT pInput = rtFuzzCtxInputCreate(pThis, cbInput);
+ if (RT_LIKELY(pInput))
+ {
+ memcpy(&pInput->abInput[0], pbState, cbInput);
+ RTMd5(&pInput->abInput[0], pInput->cbInput, &pInput->abMd5Hash[0]);
+ rc = rtFuzzCtxInputAdd(pThis, pInput);
+ if (RT_FAILURE(rc))
+ RTMemFree(pInput);
+ pbState += cbInput;
+ }
+ }
+ else
+ rc = VERR_INVALID_STATE;
+
+ idx++;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *phFuzzCtx = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+
+ rtFuzzCtxDestroy(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_INVALID_MAGIC;
+ }
+ else
+ rc = VERR_INVALID_MAGIC;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCreateFromStateFile(PRTFUZZCTX phFuzzCtx, const char *pszFilename)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ void *pv = NULL;
+ size_t cb = 0;
+ int rc = RTFileReadAll(pszFilename, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzCtxCreateFromState(phFuzzCtx, pv, cb);
+ RTFileReadAllFree(pv, cb);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTFuzzCtxRetain(RTFUZZCTX hFuzzCtx)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTFuzzCtxRelease(RTFUZZCTX hFuzzCtx)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ if (pThis == NIL_RTFUZZCTX)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtFuzzCtxDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTFuzzCtxStateExport(RTFUZZCTX hFuzzCtx, void **ppvState, size_t *pcbState)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppvState, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbState, VERR_INVALID_POINTER);
+
+ char aszPrngExport[_4K]; /* Should be plenty of room here. */
+ size_t cbPrng = sizeof(aszPrngExport);
+ int rc = RTRandAdvSaveState(pThis->hRand, &aszPrngExport[0], &cbPrng);
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZCTXSTATE StateExport;
+
+ StateExport.u32Magic = RT_H2LE_U32(RTFUZZCTX_MAGIC);
+ StateExport.cbPrng = RT_H2LE_U32((uint32_t)cbPrng);
+ StateExport.cInputs = RT_H2LE_U32(pThis->cInputs);
+ StateExport.fFlagsBehavioral = RT_H2LE_U32(pThis->fFlagsBehavioral);
+ StateExport.cbInputMax = RT_H2LE_U64(pThis->cbInputMax);
+
+ /* Estimate the size of the required state. */
+ size_t cbState = sizeof(StateExport)
+ + cbPrng
+ + pThis->cInputs * ((pThis->cbInputMax < _1M ? pThis->cbInputMax : _64K) + sizeof(uint32_t)); /* For the size indicator before each input. */
+ uint8_t *pbState = (uint8_t *)RTMemAllocZ(cbState);
+ if (RT_LIKELY(pbState))
+ {
+ size_t offState = 0;
+ memcpy(pbState, &StateExport, sizeof(StateExport));
+ offState += sizeof(StateExport);
+ memcpy(&pbState[offState], &aszPrngExport[0], cbPrng);
+ offState += cbPrng;
+
+ /* Export each input. */
+ PRTFUZZINPUTINT pIt;
+ RTListForEach(&pThis->LstInputs, pIt, RTFUZZINPUTINT, NdInputs)
+ {
+ /* Ensure buffer size. */
+ if (offState + pIt->cbInput + sizeof(uint32_t) > cbState)
+ {
+ uint8_t *pbStateNew = (uint8_t *)RTMemRealloc(pbState, cbState + pIt->cbInput + sizeof(uint32_t));
+ if (RT_LIKELY(pbStateNew))
+ {
+ pbState = pbStateNew;
+ cbState += pIt->cbInput + sizeof(uint32_t);
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+
+ *(uint32_t *)&pbState[offState] = RT_H2LE_U32((uint32_t)pIt->cbInput);
+ offState += sizeof(uint32_t);
+ memcpy(&pbState[offState], &pIt->abInput[0], pIt->cbInput);
+ offState += pIt->cbInput;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppvState = pbState;
+ *pcbState = offState;
+ }
+ else
+ RTMemFree(pbState);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxStateExportToFile(RTFUZZCTX hFuzzCtx, const char *pszFilename)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ void *pvState = NULL;
+ size_t cbState = 0;
+ int rc = RTFuzzCtxStateExport(hFuzzCtx, &pvState, &cbState);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hFile;
+
+ rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pvState, cbState, NULL);
+ RTFileClose(hFile);
+ if (RT_FAILURE(rc))
+ RTFileDelete(pszFilename);
+ }
+
+ RTMemFree(pvState);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAdd(RTFUZZCTX hFuzzCtx, const void *pvInput, size_t cbInput)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvInput, VERR_INVALID_POINTER);
+ AssertReturn(cbInput, VERR_INVALID_POINTER);
+
+ /* Generate MD5 checksum and try to locate input. */
+ int rc = VINF_SUCCESS;
+ uint8_t abDigest[RTMD5_HASH_SIZE];
+ RTMd5(pvInput, cbInput, &abDigest[0]);
+
+ PRTFUZZINPUTINT pInput = rtFuzzCtxInputLocate(pThis, &abDigest[0], true /*fExact*/, NULL /*ppIntermediate*/);
+ if (!pInput)
+ {
+ pInput = (PRTFUZZINPUTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZINPUTINT, abInput[cbInput]));
+ if (RT_LIKELY(pInput))
+ {
+ pInput->cRefs = 1;
+ pInput->pFuzzer = pThis;
+ pInput->cbInput = cbInput;
+ memcpy(&pInput->abInput[0], pvInput, cbInput);
+ memcpy(&pInput->abMd5Hash[0], &abDigest[0], sizeof(abDigest));
+ rc = rtFuzzCtxInputAdd(pThis, pInput);
+ if (RT_FAILURE(rc))
+ RTMemFree(pInput);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromFile(RTFUZZCTX hFuzzCtx, const char *pszFilename)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ void *pv = NULL;
+ size_t cb = 0;
+ int rc = RTFileReadAll(pszFilename, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzCtxCorpusInputAdd(hFuzzCtx, pv, cb);
+ RTFileReadAllFree(pv, cb);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromVfsFile(RTFUZZCTX hFuzzCtx, RTVFSFILE hVfsFile)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(hVfsFile != NIL_RTVFSFILE, VERR_INVALID_HANDLE);
+
+ uint64_t cbFile = 0;
+ int rc = RTVfsFileGetSize(hVfsFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ PRTFUZZINPUTINT pInput = (PRTFUZZINPUTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZINPUTINT, abInput[cbFile]));
+ if (RT_LIKELY(pInput))
+ {
+ pInput->cRefs = 1;
+ pInput->pFuzzer = pThis;
+ pInput->cbInput = cbFile;
+
+ rc = RTVfsFileRead(hVfsFile, &pInput->abInput[0], cbFile, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Generate MD5 checksum and try to locate input. */
+ uint8_t abDigest[RTMD5_HASH_SIZE];
+ RTMd5(&pInput->abInput[0], cbFile, &abDigest[0]);
+
+ if (!rtFuzzCtxInputLocate(pThis, &abDigest[0], true /*fExact*/, NULL /*ppIntermediate*/))
+ {
+ memcpy(&pInput->abMd5Hash[0], &abDigest[0], sizeof(abDigest));
+ rc = rtFuzzCtxInputAdd(pThis, pInput);
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pInput);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromDirPath(RTFUZZCTX hFuzzCtx, const char *pszDirPath)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDirPath, VERR_INVALID_POINTER);
+
+ RTDIR hDir;
+ int rc = RTDirOpen(&hDir, pszDirPath);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ RTDIRENTRY DirEntry;
+ rc = RTDirRead(hDir, &DirEntry, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Skip '.', '..' and other non-files. */
+ if ( DirEntry.enmType != RTDIRENTRYTYPE_UNKNOWN
+ && DirEntry.enmType != RTDIRENTRYTYPE_FILE)
+ continue;
+ if (RTDirEntryIsStdDotLink(&DirEntry))
+ continue;
+
+ /* Compose the full path, result 'unknown' entries and skip non-files. */
+ char szFile[RTPATH_MAX];
+ RT_ZERO(szFile);
+ rc = RTPathJoin(szFile, sizeof(szFile), pszDirPath, DirEntry.szName);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN)
+ {
+ RTDirQueryUnknownType(szFile, false, &DirEntry.enmType);
+ if (DirEntry.enmType != RTDIRENTRYTYPE_FILE)
+ continue;
+ }
+
+ /* Okay, it's a file we can add. */
+ rc = RTFuzzCtxCorpusInputAddFromFile(hFuzzCtx, szFile);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+ RTDirClose(hDir);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCfgSetInputSeedMaximum(RTFUZZCTX hFuzzCtx, size_t cbMax)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ pThis->cbInputMax = cbMax;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(size_t) RTFuzzCtxCfgGetInputSeedMaximum(RTFUZZCTX hFuzzCtx)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cbInputMax;
+}
+
+
+RTDECL(int) RTFuzzCtxCfgSetBehavioralFlags(RTFUZZCTX hFuzzCtx, uint32_t fFlags)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(!(fFlags & ~RTFUZZCTX_F_BEHAVIORAL_VALID), VERR_INVALID_PARAMETER);
+
+ pThis->fFlagsBehavioral = fFlags;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTFuzzCfgGetBehavioralFlags(RTFUZZCTX hFuzzCtx)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->fFlagsBehavioral;
+}
+
+
+RTDECL(int) RTFuzzCtxCfgSetTmpDirectory(RTFUZZCTX hFuzzCtx, const char *pszPathTmp)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPathTmp, VERR_INVALID_POINTER);
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(const char *) RTFuzzCtxCfgGetTmpDirectory(RTFUZZCTX hFuzzCtx)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, NULL);
+
+ return NULL;
+}
+
+
+RTDECL(int) RTFuzzCtxReseed(RTFUZZCTX hFuzzCtx, uint64_t uSeed)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ RTRandAdvSeed(pThis->hRand, uSeed);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzCtxInputGenerate(RTFUZZCTX hFuzzCtx, PRTFUZZINPUT phFuzzInput)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFuzzInput, VERR_INVALID_POINTER);
+
+ uint32_t cTries = 0;
+ PRTFUZZINPUTINT pSrc = rtFuzzCtxInputPickRnd(pThis);
+ do
+ {
+ RTFUZZCTXMUTATOR enmMutator = (RTFUZZCTXMUTATOR)RTRandAdvU32Ex(pThis->hRand, 1, RTFUZZCTXMUTATOR_LAST);
+ PRTFUZZINPUTINT pInput = NULL;
+ rc = g_apfnMutators[enmMutator](pThis, &pSrc->abInput[0], pSrc->cbInput, &pInput);
+ if ( RT_SUCCESS(rc)
+ && VALID_PTR(pInput))
+ {
+ if (pThis->fFlagsBehavioral & RTFUZZCTX_F_BEHAVIORAL_ADD_INPUT_AUTOMATICALLY_TO_CORPUS)
+ rtFuzzCtxInputAdd(pThis, pInput);
+ *phFuzzInput = pInput;
+ return rc;
+ }
+ } while (++cTries <= 50);
+
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxMutateBuffer(RTFUZZCTX hFuzzCtx, void *pvBuf, size_t cbBuf, PRTFUZZINPUT phFuzzInput)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFuzzInput, VERR_INVALID_POINTER);
+
+ uint32_t cTries = 0;
+ do
+ {
+ RTFUZZCTXMUTATOR enmMutator = (RTFUZZCTXMUTATOR)RTRandAdvU32Ex(pThis->hRand, 1, RTFUZZCTXMUTATOR_LAST);
+ PRTFUZZINPUTINT pInput = NULL;
+ rc = g_apfnMutators[enmMutator](pThis, pvBuf, cbBuf, &pInput);
+ if ( RT_SUCCESS(rc)
+ && VALID_PTR(pInput))
+ {
+ *phFuzzInput = pInput;
+ return rc;
+ }
+ } while (++cTries <= 50);
+
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTFuzzInputRetain(RTFUZZINPUT hFuzzInput)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTFuzzInputRelease(RTFUZZINPUT hFuzzInput)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ if (pThis == NIL_RTFUZZINPUT)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtFuzzInputDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTFuzzInputQueryData(RTFUZZINPUT hFuzzInput, void **ppv, size_t *pcb)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppv, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ *ppv = &pThis->abInput[0];
+ *pcb = pThis->cbInput;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzInputQueryDigestString(RTFUZZINPUT hFuzzInput, char *pszDigest, size_t cchDigest)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszDigest, VERR_INVALID_POINTER);
+ AssertReturn(cchDigest >= RTMD5_STRING_LEN + 1, VERR_INVALID_PARAMETER);
+
+ return RTMd5ToString(&pThis->abMd5Hash[0], pszDigest, cchDigest);
+}
+
+
+RTDECL(int) RTFuzzInputWriteToFile(RTFUZZINPUT hFuzzInput, const char *pszFilename)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, &pThis->abInput[0], pThis->cbInput, NULL);
+ AssertRC(rc);
+ RTFileClose(hFile);
+
+ if (RT_FAILURE(rc))
+ RTFileDelete(pszFilename);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzInputAddToCtxCorpus(RTFUZZINPUT hFuzzInput)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ return rtFuzzCtxInputAdd(pThis->pFuzzer, pThis);
+}
+
+
+RTDECL(int) RTFuzzInputRemoveFromCtxCorpus(RTFUZZINPUT hFuzzInput)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ PRTFUZZINTERMEDIATE pIntermediate = NULL;
+ PRTFUZZINPUTINT pInputLoc = rtFuzzCtxInputLocate(pThis->pFuzzer, &pThis->abMd5Hash[0], true /*fExact*/,
+ &pIntermediate);
+ if (pInputLoc)
+ {
+ AssertPtr(pIntermediate);
+ Assert(pInputLoc == pThis);
+
+ uint64_t u64Md5Low = *(uint64_t *)&pThis->abMd5Hash[0];
+ RTAvlU64Remove(&pIntermediate->TreeSeedsLow, u64Md5Low);
+ RTFuzzInputRelease(hFuzzInput);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp b/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp
new file mode 100644
index 00000000..701cdd0b
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp
@@ -0,0 +1,216 @@
+/* $Id: fuzzclientcmd.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, fuzzed client command.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+#include <iprt/vfs.h>
+
+
+/**
+ * Fuzzing client command state.
+ */
+typedef struct RTFUZZCMDCLIENT
+{
+ /** Our own fuzzing context containing all the data. */
+ RTFUZZCTX hFuzzCtx;
+ /** Consumption callback. */
+ PFNFUZZCLIENTCONSUME pfnConsume;
+ /** Opaque user data to pass to the consumption callback. */
+ void *pvUser;
+ /** Standard input VFS handle. */
+ RTVFSIOSTREAM hVfsStdIn;
+ /** Standard output VFS handle. */
+ RTVFSIOSTREAM hVfsStdOut;
+} RTFUZZCMDCLIENT;
+/** Pointer to a fuzzing client command state. */
+typedef RTFUZZCMDCLIENT *PRTFUZZCMDCLIENT;
+
+
+/**
+ * The fuzzing client mainloop.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing client command state.
+ */
+static int rtFuzzCmdClientMainloop(PRTFUZZCMDCLIENT pThis)
+{
+ int rc = VINF_SUCCESS;
+ bool fShutdown = false;
+
+ while ( !fShutdown
+ && RT_SUCCESS(rc))
+ {
+ RTFUZZINPUT hFuzzInput;
+
+ rc = RTFuzzCtxInputGenerate(pThis->hFuzzCtx, &hFuzzInput);
+ if (RT_SUCCESS(rc))
+ {
+ void *pv = NULL;
+ size_t cb = 0;
+ rc = RTFuzzInputQueryData(hFuzzInput, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ char bResp = '.';
+ int rc2 = pThis->pfnConsume(pv, cb, pThis->pvUser);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = RTFuzzInputAddToCtxCorpus(hFuzzInput);
+ bResp = 'A';
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = RTVfsIoStrmWrite(pThis->hVfsStdOut, &bResp, 1, true /*fBlocking*/, NULL);
+ }
+
+ RTFuzzInputRelease(hFuzzInput);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Run the fuzzing client.
+ *
+ * @returns Process exit status.
+ * @param pThis The fuzzing client command state.
+ */
+static RTEXITCODE rtFuzzCmdClientRun(PRTFUZZCMDCLIENT pThis)
+{
+ int rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT, 0, true /*fLeaveOpen*/, &pThis->hVfsStdIn);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, 0, true /*fLeaveOpen*/, &pThis->hVfsStdOut);
+ if (RT_SUCCESS(rc))
+ {
+ /* Read the initial input fuzzer state from the standard input. */
+ uint32_t cbFuzzCtxState;
+ rc = RTVfsIoStrmRead(pThis->hVfsStdIn, &cbFuzzCtxState, sizeof(cbFuzzCtxState), true /*fBlocking*/, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvFuzzCtxState = RTMemAllocZ(cbFuzzCtxState);
+ if (RT_LIKELY(pvFuzzCtxState))
+ {
+ rc = RTVfsIoStrmRead(pThis->hVfsStdIn, pvFuzzCtxState, cbFuzzCtxState, true /*fBlocking*/, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzCtxCreateFromState(&pThis->hFuzzCtx, pvFuzzCtxState, cbFuzzCtxState);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdClientMainloop(pThis);
+ }
+
+ RTMemFree(pvFuzzCtxState);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ return RTEXITCODE_SUCCESS;
+
+ return RTEXITCODE_FAILURE;
+}
+
+
+RTR3DECL(RTEXITCODE) RTFuzzCmdFuzzingClient(unsigned cArgs, char **papszArgs, PFNFUZZCLIENTCONSUME pfnConsume, void *pvUser)
+{
+ /*
+ * Parse the command line.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--help", 'h', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
+ RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ if (RT_SUCCESS(rc))
+ {
+ /* Option variables: */
+ RTFUZZCMDCLIENT This;
+
+ This.pfnConsume = pfnConsume;
+ This.pvUser = pvUser;
+
+ /* Argument parsing loop. */
+ bool fContinue = true;
+ do
+ {
+ RTGETOPTUNION ValueUnion;
+ int chOpt = RTGetOpt(&GetState, &ValueUnion);
+ switch (chOpt)
+ {
+ case 0:
+ fContinue = false;
+ break;
+
+ case 'h':
+ RTPrintf("Usage: to be written\nOption dump:\n");
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
+ RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
+ fContinue = false;
+ break;
+
+ case 'V':
+ RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
+ fContinue = false;
+ break;
+
+ default:
+ rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
+ fContinue = false;
+ break;
+ }
+ } while (fContinue);
+
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = rtFuzzCmdClientRun(&This);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
+ return rcExit;
+}
+
diff --git a/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp b/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp
new file mode 100644
index 00000000..b73ac07d
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp
@@ -0,0 +1,1351 @@
+/* $Id: fuzzmastercmd.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, master command.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/json.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/tcp.h>
+#include <iprt/thread.h>
+#include <iprt/vfs.h>
+#include <iprt/zip.h>
+
+
+/**
+ * A running fuzzer state.
+ */
+typedef struct RTFUZZRUN
+{
+ /** List node. */
+ RTLISTNODE NdFuzzed;
+ /** Identifier. */
+ char *pszId;
+ /** Number of processes. */
+ uint32_t cProcs;
+ /** The fuzzing observer state handle. */
+ RTFUZZOBS hFuzzObs;
+ /** Flag whether fuzzing was started. */
+ bool fStarted;
+} RTFUZZRUN;
+/** Pointer to a running fuzzer state. */
+typedef RTFUZZRUN *PRTFUZZRUN;
+
+
+/**
+ * Fuzzing master command state.
+ */
+typedef struct RTFUZZCMDMASTER
+{
+ /** List of running fuzzers. */
+ RTLISTANCHOR LstFuzzed;
+ /** The port to listen on. */
+ uint16_t uPort;
+ /** The TCP server for requests. */
+ PRTTCPSERVER hTcpSrv;
+ /** The root temp directory. */
+ const char *pszTmpDir;
+ /** The root results directory. */
+ const char *pszResultsDir;
+ /** Flag whether to shutdown. */
+ bool fShutdown;
+ /** The response message. */
+ char *pszResponse;
+} RTFUZZCMDMASTER;
+/** Pointer to a fuzzing master command state. */
+typedef RTFUZZCMDMASTER *PRTFUZZCMDMASTER;
+
+
+/**
+ * Wrapper around RTErrInfoSetV / RTMsgErrorV.
+ *
+ * @returns @a rc
+ * @param pErrInfo Extended error info.
+ * @param rc The return code.
+ * @param pszFormat The message format.
+ * @param ... The message format arguments.
+ */
+static int rtFuzzCmdMasterErrorRc(PRTERRINFO pErrInfo, int rc, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ if (pErrInfo)
+ RTErrInfoSetV(pErrInfo, rc, pszFormat, va);
+ else
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Returns a running fuzzer state by the given ID.
+ *
+ * @returns Pointer to the running fuzzer state or NULL if not found.
+ * @param pThis The fuzzing master command state.
+ * @param pszId The ID to look for.
+ */
+static PRTFUZZRUN rtFuzzCmdMasterGetFuzzerById(PRTFUZZCMDMASTER pThis, const char *pszId)
+{
+ PRTFUZZRUN pIt = NULL;
+ RTListForEach(&pThis->LstFuzzed, pIt, RTFUZZRUN, NdFuzzed)
+ {
+ if (!RTStrCmp(pIt->pszId, pszId))
+ return pIt;
+ }
+
+ return NULL;
+}
+
+
+#if 0 /* unused */
+/**
+ * Processes and returns the value of the given config item in the JSON request.
+ *
+ * @returns IPRT status code.
+ * @param ppszStr Where to store the pointer to the string on success.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessCfgString(char **ppszStr, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
+{
+ int rc = RTJsonValueQueryStringByName(hJsonCfg, pszCfgItem, ppszStr);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query string value of \"%s\"", pszCfgItem);
+
+ return rc;
+}
+
+
+/**
+ * Processes and returns the value of the given config item in the JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pfVal Where to store the config value on success.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessCfgBool(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
+{
+ int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
+
+ return rc;
+}
+
+
+/**
+ * Processes and returns the value of the given config item in the JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pfVal Where to store the config value on success.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param fDef Default value if the item wasn't found.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessCfgBoolDef(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, bool fDef, PRTERRINFO pErrInfo)
+{
+ int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
+ if (rc == VERR_NOT_FOUND)
+ {
+ *pfVal = fDef;
+ rc = VINF_SUCCESS;
+ }
+ else if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
+
+ return rc;
+}
+#endif
+
+
+/**
+ * Processes and returns the value of the given config item in the JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pcbVal Where to store the config value on success.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param cbDef Default value if the item wasn't found.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(size_t *pcbVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, size_t cbDef, PRTERRINFO pErrInfo)
+{
+ *pcbVal = cbDef; /* Make GCC 6.3.0 happy. */
+
+ int64_t i64Val = 0;
+ int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ else if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query size_t value of \"%s\"", pszCfgItem);
+ else if (i64Val < 0 || (size_t)i64Val != (uint64_t)i64Val)
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
+ else
+ *pcbVal = (size_t)i64Val;
+
+ return rc;
+}
+
+
+/**
+ * Processes and returns the value of the given config item in the JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pcbVal Where to store the config value on success.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param cbDef Default value if the item wasn't found.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessCfgU32Def(uint32_t *pu32Val, const char *pszCfgItem, RTJSONVAL hJsonCfg, uint32_t u32Def, PRTERRINFO pErrInfo)
+{
+ int64_t i64Val = 0;
+ int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
+ if (rc == VERR_NOT_FOUND)
+ {
+ *pu32Val = u32Def;
+ rc = VINF_SUCCESS;
+ }
+ else if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query uint32_t value of \"%s\"", pszCfgItem);
+ else if (i64Val < 0 || (uint32_t)i64Val != (uint64_t)i64Val)
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
+ else
+ *pu32Val = (uint32_t)i64Val;
+
+ return rc;
+}
+
+
+/**
+ * Returns the configured input channel for the binary under test.
+ *
+ * @returns Selected input channel or RTFUZZOBSINPUTCHAN_INVALID if an error occurred.
+ * @param pszCfgItem The config item to resolve.
+ * @param hJsonCfg The JSON object containing the item.
+ * @param enmChanDef Default value if the item wasn't found.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static RTFUZZOBSINPUTCHAN rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan(const char *pszCfgItem, RTJSONVAL hJsonCfg, RTFUZZOBSINPUTCHAN enmChanDef, PRTERRINFO pErrInfo)
+{
+ RTFUZZOBSINPUTCHAN enmInputChan = RTFUZZOBSINPUTCHAN_INVALID;
+
+ RTJSONVAL hJsonVal;
+ int rc = RTJsonValueQueryByName(hJsonCfg, pszCfgItem, &hJsonVal);
+ if (rc == VERR_NOT_FOUND)
+ enmInputChan = enmChanDef;
+ else if (RT_SUCCESS(rc))
+ {
+ const char *pszBinary = RTJsonValueGetString(hJsonVal);
+ if (pszBinary)
+ {
+ if (!RTStrCmp(pszBinary, "File"))
+ enmInputChan = RTFUZZOBSINPUTCHAN_FILE;
+ else if (!RTStrCmp(pszBinary, "Stdin"))
+ enmInputChan = RTFUZZOBSINPUTCHAN_STDIN;
+ else if (!RTStrCmp(pszBinary, "FuzzingAware"))
+ enmInputChan = RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT;
+ else
+ rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "JSON request malformed: \"%s\" for \"%s\" is not known", pszCfgItem, pszBinary);
+ }
+ else
+ rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"%s\" is not a string", pszCfgItem);
+
+ RTJsonValueRelease(hJsonVal);
+ }
+ else
+ rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"%s\"", pszCfgItem);
+
+ return enmInputChan;
+}
+
+
+/**
+ * Processes binary related configs for the given fuzzing run.
+ *
+ * @returns IPRT status code.
+ * @param pFuzzRun The fuzzing run.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessBinaryCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonVal;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "BinaryPath", &hJsonVal);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszBinary = RTJsonValueGetString(hJsonVal);
+ if (RT_LIKELY(pszBinary))
+ {
+ RTFUZZOBSINPUTCHAN enmInputChan = rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan("InputChannel", hJsonRoot, RTFUZZOBSINPUTCHAN_STDIN, pErrInfo);
+ if (enmInputChan != RTFUZZOBSINPUTCHAN_INVALID)
+ {
+ rc = RTFuzzObsSetTestBinary(pFuzzRun->hFuzzObs, pszBinary, enmInputChan);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to add the binary path for the fuzzing run");
+ }
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"BinaryPath\" is not a string");
+ RTJsonValueRelease(hJsonVal);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query value of \"BinaryPath\"");
+
+ return rc;
+}
+
+
+/**
+ * Processes argument related configs for the given fuzzing run.
+ *
+ * @returns IPRT status code.
+ * @param pFuzzRun The fuzzing run.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessArgCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValArgArray;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Arguments", &hJsonValArgArray);
+ if (RT_SUCCESS(rc))
+ {
+ unsigned cArgs = 0;
+ rc = RTJsonValueQueryArraySize(hJsonValArgArray, &cArgs);
+ if (RT_SUCCESS(rc))
+ {
+ if (cArgs > 0)
+ {
+ const char **papszArgs = (const char **)RTMemAllocZ(cArgs * sizeof(const char *));
+ RTJSONVAL *pahJsonVal = (RTJSONVAL *)RTMemAllocZ(cArgs * sizeof(RTJSONVAL));
+ if (RT_LIKELY(papszArgs && pahJsonVal))
+ {
+ unsigned idx = 0;
+
+ for (idx = 0; idx < cArgs && RT_SUCCESS(rc); idx++)
+ {
+ rc = RTJsonValueQueryByIndex(hJsonValArgArray, idx, &pahJsonVal[idx]);
+ if (RT_SUCCESS(rc))
+ {
+ papszArgs[idx] = RTJsonValueGetString(pahJsonVal[idx]);
+ if (RT_UNLIKELY(!papszArgs[idx]))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Argument %u is not a string", idx);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsSetTestBinaryArgs(pFuzzRun->hFuzzObs, papszArgs, cArgs);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to set arguments for the fuzzing run");
+ }
+
+ /* Release queried values. */
+ while (idx > 0)
+ {
+ RTJsonValueRelease(pahJsonVal[idx - 1]);
+ idx--;
+ }
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Out of memory allocating memory for the argument vector");
+
+ if (papszArgs)
+ RTMemFree(papszArgs);
+ if (pahJsonVal)
+ RTMemFree(pahJsonVal);
+ }
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Arguments\" is not an array");
+ RTJsonValueRelease(hJsonValArgArray);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes the given seed and adds it to the input corpus.
+ *
+ * @returns IPRT status code.
+ * @param hFuzzCtx The fuzzing context handle.
+ * @param pszCompression Compression used for the seed.
+ * @param pszSeed The seed as a base64 encoded string.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessSeed(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszSeed, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ ssize_t cbSeedDecoded = RTBase64DecodedSize(pszSeed, NULL);
+ if (cbSeedDecoded > 0)
+ {
+ uint8_t *pbSeedDecoded = (uint8_t *)RTMemAllocZ(cbSeedDecoded);
+ if (RT_LIKELY(pbSeedDecoded))
+ {
+ rc = RTBase64Decode(pszSeed, pbSeedDecoded, cbSeedDecoded, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Decompress if applicable. */
+ if (!RTStrICmp(pszCompression, "None"))
+ rc = RTFuzzCtxCorpusInputAdd(hFuzzCtx, pbSeedDecoded, cbSeedDecoded);
+ else
+ {
+ RTVFSIOSTREAM hVfsIosSeed;
+ rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbSeedDecoded, cbSeedDecoded, &hVfsIosSeed);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
+
+ if (!RTStrICmp(pszCompression, "Gzip"))
+ rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSFILE hVfsFile;
+ rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* The VFS file contains the buffer for the seed now. */
+ rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
+ RTVfsFileRelease(hVfsFile);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
+
+ RTVfsIoStrmRelease(hVfsDecomp);
+ }
+
+ RTVfsIoStrmRelease(hVfsIosSeed);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
+ }
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to decode the seed string");
+
+ RTMemFree(pbSeedDecoded);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Failed to allocate %zd bytes of memory for the seed", cbSeedDecoded);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: Couldn't find \"Seed\" doesn't contain a base64 encoded value");
+
+ return rc;
+}
+
+
+/**
+ * Processes a signle input seed for the given fuzzing run.
+ *
+ * @returns IPRT status code.
+ * @param pFuzzRun The fuzzing run.
+ * @param hJsonSeed The seed node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
+{
+ RTFUZZCTX hFuzzCtx;
+ int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonValComp;
+ rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszCompression = RTJsonValueGetString(hJsonValComp);
+ if (RT_LIKELY(pszCompression))
+ {
+ RTJSONVAL hJsonValSeed;
+ rc = RTJsonValueQueryByName(hJsonSeed, "Seed", &hJsonValSeed);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszSeed = RTJsonValueGetString(hJsonValSeed);
+ if (RT_LIKELY(pszSeed))
+ rc = rtFuzzCmdMasterFuzzRunProcessSeed(hFuzzCtx, pszCompression, pszSeed, pErrInfo);
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Seed\" value is not a string");
+
+ RTJsonValueRelease(hJsonValSeed);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Seed\" value");
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
+
+ RTJsonValueRelease(hJsonValComp);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
+
+ RTFuzzCtxRelease(hFuzzCtx);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
+
+ return rc;
+}
+
+
+/**
+ * Processes input seed related configs for the given fuzzing run.
+ *
+ * @returns IPRT status code.
+ * @param pFuzzRun The fuzzing run.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessInputSeeds(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValSeedArray;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "InputSeeds", &hJsonValSeedArray);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONIT hIt;
+ rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
+ if (RT_SUCCESS(rc))
+ {
+ RTJSONVAL hJsonInpSeed;
+ while ( RT_SUCCESS(rc)
+ && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
+ {
+ rc = rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
+ RTJsonValueRelease(hJsonInpSeed);
+ if (RT_FAILURE(rc))
+ break;
+ rc = RTJsonIteratorNext(hIt);
+ }
+
+ if (rc == VERR_JSON_ITERATOR_END)
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
+
+ RTJsonValueRelease(hJsonValSeedArray);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes miscellaneous config items.
+ *
+ * @returns IPRT status code.
+ * @param pFuzzRun The fuzzing run.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunProcessMiscCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ size_t cbTmp;
+ int rc = rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(&cbTmp, "InputSeedMax", hJsonRoot, 0, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZCTX hFuzzCtx;
+ rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
+ AssertRC(rc);
+
+ rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbTmp);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set maximum input seed size to %zu", cbTmp);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, 0, pErrInfo);
+
+ return rc;
+}
+
+
+/**
+ * Creates a new fuzzing run with the given ID.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param pszId The ID to use.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterCreateFuzzRunWithId(PRTFUZZCMDMASTER pThis, const char *pszId, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZRUN pFuzzRun = (PRTFUZZRUN)RTMemAllocZ(sizeof(*pFuzzRun));
+ if (RT_LIKELY(pFuzzRun))
+ {
+ pFuzzRun->pszId = RTStrDup(pszId);
+ if (RT_LIKELY(pFuzzRun->pszId))
+ {
+ rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessInputSeeds(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessMiscCfg(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create temp directories. */
+ char szTmpDir[RTPATH_MAX];
+ rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszTmpDir, pFuzzRun->pszId);
+ AssertRC(rc);
+ rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
+ | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsSetTmpDirectory(pFuzzRun->hFuzzObs, szTmpDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszResultsDir, pFuzzRun->pszId);
+ AssertRC(rc);
+ rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
+ | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
+ if (RT_SUCCESS(rc))
+ {
+ /* Start fuzzing. */
+ RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
+ rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
+ if (RT_SUCCESS(rc))
+ pFuzzRun->fStarted = true;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set results directory to %s", szTmpDir);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create results directory %s", szTmpDir);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set temporary directory to %s", szTmpDir);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create temporary directory %s", szTmpDir);
+ }
+ }
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Out of memory allocating the fuzzer state");
+
+ return rc;
+}
+
+
+/**
+ * Resolves the fuzzing run from the given ID config item and the given JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pszIdItem The JSON item which contains the ID of the fuzzing run.
+ * @param ppFuzzRun Where to store the pointer to the fuzzing run on success.
+ */
+static int rtFuzzCmdMasterQueryFuzzRunFromJson(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, const char *pszIdItem, PRTERRINFO pErrInfo,
+ PRTFUZZRUN *ppFuzzRun)
+{
+ RTJSONVAL hJsonValId;
+ int rc = RTJsonValueQueryByName(hJsonRoot, pszIdItem, &hJsonValId);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszId = RTJsonValueGetString(hJsonValId);
+ if (pszId)
+ {
+ PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
+ if (pFuzzRun)
+ *ppFuzzRun = pFuzzRun;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "Request error: The ID \"%s\" wasn't found", pszId);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
+
+ RTJsonValueRelease(hJsonValId);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
+ return rc;
+}
+
+
+/**
+ * Processes the "StartFuzzing" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqStart(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValId;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszId = RTJsonValueGetString(hJsonValId);
+ if (pszId)
+ {
+ PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
+ if (!pFuzzRun)
+ rc = rtFuzzCmdMasterCreateFuzzRunWithId(pThis, pszId, hJsonRoot, pErrInfo);
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_ALREADY_EXISTS, "Request error: The ID \"%s\" is already registered", pszId);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
+
+ RTJsonValueRelease(hJsonValId);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
+ return rc;
+}
+
+
+/**
+ * Processes the "StopFuzzing" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqStop(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ PRTFUZZRUN pFuzzRun;
+ int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pFuzzRun->NdFuzzed);
+ RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
+ RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
+ RTStrFree(pFuzzRun->pszId);
+ RTMemFree(pFuzzRun);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes the "SuspendFuzzing" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqSuspend(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ PRTFUZZRUN pFuzzRun;
+ int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFuzzRun->fStarted)
+ {
+ rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
+ if (RT_SUCCESS(rc))
+ pFuzzRun->fStarted = false;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes the "ResumeFuzzing" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqResume(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ PRTFUZZRUN pFuzzRun;
+ int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pFuzzRun->fStarted)
+ {
+ rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, pFuzzRun->cProcs, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
+ if (RT_SUCCESS(rc))
+ pFuzzRun->fStarted = true;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Resuming the fuzzing process failed");
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes the "SaveFuzzingState" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqSaveState(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ PRTFUZZRUN pFuzzRun;
+ int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ {
+ /* Suspend fuzzing, save and resume if not stopped. */
+ if (pFuzzRun->fStarted)
+ {
+ rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZCTX hFuzzCtx;
+ rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
+ AssertRC(rc);
+
+ void *pvState = NULL;
+ size_t cbState = 0;
+ rc = RTFuzzCtxStateExport(hFuzzCtx, &pvState, &cbState);
+ if (RT_SUCCESS(rc))
+ {
+ /* Encode to base64. */
+ size_t cbStateStr = RTBase64EncodedLength(cbState) + 1;
+ char *pszState = (char *)RTMemAllocZ(cbStateStr);
+ if (pszState)
+ {
+ rc = RTBase64Encode(pvState, cbState, pszState, cbStateStr, &cbStateStr);
+ if (RT_SUCCESS(rc))
+ {
+ /* Strip all new lines from the srting. */
+ size_t offStr = 0;
+ while (offStr < cbStateStr)
+ {
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ char *pszEol = strchr(&pszState[offStr], '\r');
+#else
+ char *pszEol = strchr(&pszState[offStr], '\n');
+#endif
+ if (pszEol)
+ {
+ offStr += pszEol - &pszState[offStr];
+ memmove(pszEol, &pszEol[RTBASE64_EOL_SIZE], cbStateStr - offStr - RTBASE64_EOL_SIZE);
+ cbStateStr -= RTBASE64_EOL_SIZE;
+ }
+ else
+ break;
+ }
+
+ const char s_szState[] = "{ \"State\": %s }";
+ pThis->pszResponse = RTStrAPrintf2(s_szState, pszState);
+ if (RT_UNLIKELY(!pThis->pszResponse))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to encode the state as a base64 string");
+ RTMemFree(pszState);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "Request error: Failed to allocate a state string for the response");
+ RTMemFree(pvState);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Exporting the state failed");
+ }
+
+ if (pFuzzRun->fStarted)
+ {
+ int rc2 = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
+ if (RT_FAILURE(rc2))
+ rtFuzzCmdMasterErrorRc(pErrInfo, rc2, "Request error: Resuming the fuzzing process failed");
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes the "QueryStats" request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ PRTFUZZRUN pFuzzRun;
+ int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZOBSSTATS Stats;
+ rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &Stats);
+ if (RT_SUCCESS(rc))
+ {
+ const char s_szStats[] = "{ \"FuzzedInputsPerSec\": %u\n"
+ " \"FuzzedInputs\": %u\n"
+ " \"FuzzedInputsHang\": %u\n"
+ " \"FuzzedInputsCrash\": %u\n}";
+ pThis->pszResponse = RTStrAPrintf2(s_szStats, Stats.cFuzzedInputsPerSec,
+ Stats.cFuzzedInputs, Stats.cFuzzedInputsHang, Stats.cFuzzedInputsCrash);
+ if (RT_UNLIKELY(!pThis->pszResponse))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes a JSON request.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param hJsonValRoot The root node of the JSON request.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessJsonReq(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValReq;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Request", &hJsonValReq);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszReq = RTJsonValueGetString(hJsonValReq);
+ if (pszReq)
+ {
+ if (!RTStrCmp(pszReq, "StartFuzzing"))
+ rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "StopFuzzing"))
+ rc = rtFuzzCmdMasterProcessJsonReqStop(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "SuspendFuzzing"))
+ rc = rtFuzzCmdMasterProcessJsonReqSuspend(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "ResumeFuzzing"))
+ rc = rtFuzzCmdMasterProcessJsonReqResume(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "SaveFuzzingState"))
+ rc = rtFuzzCmdMasterProcessJsonReqSaveState(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "QueryStats"))
+ rc = rtFuzzCmdMasterProcessJsonReqQueryStats(pThis, hJsonRoot, pErrInfo);
+ else if (!RTStrCmp(pszReq, "Shutdown"))
+ pThis->fShutdown = true;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" contains unknown value \"%s\"", pszReq);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" is not a string value");
+
+ RTJsonValueRelease(hJsonValReq);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Request\" value");
+
+ return rc;
+}
+
+
+/**
+ * Loads a fuzzing configuration for immediate startup from the given file.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param pszFuzzCfg The fuzzing config to load.
+ */
+static int rtFuzzCmdMasterFuzzCfgLoadFromFile(PRTFUZZCMDMASTER pThis, const char *pszFuzzCfg)
+{
+ RTJSONVAL hJsonRoot;
+ int rc = RTJsonParseFromFile(&hJsonRoot, pszFuzzCfg, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, NULL);
+ RTJsonValueRelease(hJsonRoot);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(NULL, rc, "JSON request malformed: Couldn't load file \"%s\"", pszFuzzCfg);
+
+ return rc;
+}
+
+
+/**
+ * Destroys all running fuzzers for the given master state.
+ *
+ * @returns nothing.
+ * @param pThis The fuzzing master command state.
+ */
+static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/**
+ * Sends an ACK response to the client.
+ *
+ * @returns nothing.
+ * @param hSocket The socket handle to send the ACK to.
+ * @param pszResponse Additional response data.
+ */
+static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
+{
+ const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
+ const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n \"Response\":\n";
+ const char s_szSuccRespClose[] = "\n }\n";
+ if (pszResponse)
+ {
+ RTSGSEG aSegs[3];
+ RTSGBUF SgBuf;
+ aSegs[0].pvSeg = (void *)s_szSuccResp;
+ aSegs[0].cbSeg = sizeof(s_szSuccResp) - 1;
+ aSegs[1].pvSeg = (void *)pszResponse;
+ aSegs[1].cbSeg = strlen(pszResponse);
+ aSegs[2].pvSeg = (void *)s_szSuccRespClose;
+ aSegs[2].cbSeg = sizeof(s_szSuccRespClose) - 1;
+
+ RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
+ RTTcpSgWrite(hSocket, &SgBuf);
+ }
+ else
+ RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
+}
+
+
+/**
+ * Sends an NACK response to the client.
+ *
+ * @returns nothing.
+ * @param hSocket The socket handle to send the ACK to.
+ * @param pErrInfo Optional error information to send along.
+ */
+static void rtFuzzCmdMasterTcpSendNAck(RTSOCKET hSocket, PRTERRINFO pErrInfo)
+{
+ const char s_szFail[] = "{ \"Status\": \"NACK\" }\n";
+ const char s_szFailInfo[] = "{ \"Status\": \"NACK\"\n \"Information\": \"%s\" }\n";
+
+ if (pErrInfo)
+ {
+ char szTmp[_1K];
+ ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
+ if (cchResp > 0)
+ RTTcpWrite(hSocket, szTmp, cchResp);
+ else
+ RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
+ }
+ else
+ RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
+}
+
+
+/**
+ * TCP server serving callback for a single connection.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle of the connection.
+ * @param pvUser Opaque user data.
+ */
+static DECLCALLBACK(int) rtFuzzCmdMasterTcpServe(RTSOCKET hSocket, void *pvUser)
+{
+ PRTFUZZCMDMASTER pThis = (PRTFUZZCMDMASTER)pvUser;
+ size_t cbReqMax = _32K;
+ size_t cbReq = 0;
+ uint8_t *pbReq = (uint8_t *)RTMemAllocZ(cbReqMax);
+
+ if (RT_LIKELY(pbReq))
+ {
+ uint8_t *pbCur = pbReq;
+
+ for (;;)
+ {
+ size_t cbThisRead = cbReqMax - cbReq;
+ int rc = RTTcpRead(hSocket, pbCur, cbThisRead, &cbThisRead);
+ if (RT_SUCCESS(rc))
+ {
+ cbReq += cbThisRead;
+
+ /* Check for a zero terminator marking the end of the request. */
+ uint8_t *pbEnd = (uint8_t *)memchr(pbCur, 0, cbThisRead);
+ if (pbEnd)
+ {
+ /* Adjust request size, data coming after the zero terminiator is ignored right now. */
+ cbReq -= cbThisRead - (pbEnd - pbCur) + 1;
+
+ RTJSONVAL hJsonReq;
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+
+ rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
+ if (RT_SUCCESS(rc))
+ rtFuzzCmdMasterTcpSendAck(hSocket, pThis->pszResponse);
+ else
+ rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
+ RTJsonValueRelease(hJsonReq);
+ }
+ else
+ rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
+
+ if (pThis->pszResponse)
+ {
+ RTStrFree(pThis->pszResponse);
+ pThis->pszResponse = NULL;
+ }
+ break;
+ }
+ else if (cbReq == cbReqMax)
+ {
+ /* Try to increase the buffer. */
+ uint8_t *pbReqNew = (uint8_t *)RTMemRealloc(pbReq, cbReqMax + _32K);
+ if (RT_LIKELY(pbReqNew))
+ {
+ cbReqMax += _32K;
+ pbReq = pbReqNew;
+ pbCur = pbReq + cbReq;
+ }
+ else
+ rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
+ }
+ else
+ pbCur += cbThisRead;
+ }
+ else
+ break;
+ }
+ }
+ else
+ rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
+
+ if (pbReq)
+ RTMemFree(pbReq);
+
+ return pThis->fShutdown ? VERR_TCP_SERVER_STOP : VINF_SUCCESS;
+}
+
+
+/**
+ * Mainloop for the fuzzing master.
+ *
+ * @returns Process exit code.
+ * @param pThis The fuzzing master command state.
+ * @param pszLoadCfg Initial config to load.
+ */
+static RTEXITCODE rtFuzzCmdMasterRun(PRTFUZZCMDMASTER pThis, const char *pszLoadCfg)
+{
+ if (pszLoadCfg)
+ {
+ int rc = rtFuzzCmdMasterFuzzCfgLoadFromFile(pThis, pszLoadCfg);
+ if (RT_FAILURE(rc))
+ return RTEXITCODE_FAILURE;
+ }
+
+ /* Start up the control server. */
+ int rc = RTTcpServerCreateEx(NULL, pThis->uPort, &pThis->hTcpSrv);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ rc = RTTcpServerListen(pThis->hTcpSrv, rtFuzzCmdMasterTcpServe, pThis);
+ } while (rc != VERR_TCP_SERVER_STOP);
+ }
+
+ RTTcpServerDestroy(pThis->hTcpSrv);
+ rtFuzzCmdMasterDestroy(pThis);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTR3DECL(RTEXITCODE) RTFuzzCmdMaster(unsigned cArgs, char **papszArgs)
+{
+ /*
+ * Parse the command line.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--fuzz-config", 'c', RTGETOPT_REQ_STRING },
+ { "--temp-dir", 't', RTGETOPT_REQ_STRING },
+ { "--results-dir", 'r', RTGETOPT_REQ_STRING },
+ { "--listen-port", 'p', RTGETOPT_REQ_UINT16 },
+ { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
+ { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING },
+ { "--version", 'V', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
+ RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ if (RT_SUCCESS(rc))
+ {
+ /* Option variables: */
+ bool fDaemonize = false;
+ bool fDaemonized = false;
+ const char *pszLoadCfg = NULL;
+ RTFUZZCMDMASTER This;
+
+ RTListInit(&This.LstFuzzed);
+ This.hTcpSrv = NIL_RTTCPSERVER;
+ This.uPort = 4242;
+ This.pszTmpDir = NULL;
+ This.pszResultsDir = NULL;
+ This.fShutdown = false;
+ This.pszResponse = NULL;
+
+ /* Argument parsing loop. */
+ bool fContinue = true;
+ do
+ {
+ RTGETOPTUNION ValueUnion;
+ int chOpt = RTGetOpt(&GetState, &ValueUnion);
+ switch (chOpt)
+ {
+ case 0:
+ fContinue = false;
+ break;
+
+ case 'c':
+ pszLoadCfg = ValueUnion.psz;
+ break;
+
+ case 'p':
+ This.uPort = ValueUnion.u16;
+ break;
+
+ case 't':
+ This.pszTmpDir = ValueUnion.psz;
+ break;
+
+ case 'r':
+ This.pszResultsDir = ValueUnion.psz;
+ break;
+
+ case 'd':
+ fDaemonize = true;
+ break;
+
+ case 'Z':
+ fDaemonized = true;
+ fDaemonize = false;
+ break;
+
+ case 'h':
+ RTPrintf("Usage: to be written\nOption dump:\n");
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
+ RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
+ fContinue = false;
+ break;
+
+ case 'V':
+ RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
+ fContinue = false;
+ break;
+
+ default:
+ rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
+ fContinue = false;
+ break;
+ }
+ } while (fContinue);
+
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Daemonize ourselves if asked to.
+ */
+ if (fDaemonize)
+ {
+ rc = RTProcDaemonize(papszArgs, "--daemonized");
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
+ }
+ else
+ rcExit = rtFuzzCmdMasterRun(&This, pszLoadCfg);
+ }
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
+ return rcExit;
+}
+