summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz-observer.cpp1331
1 files changed, 1331 insertions, 0 deletions
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;
+}
+