summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/fuzz')
-rw-r--r--src/VBox/Runtime/common/fuzz/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz-config.cpp651
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz-observer.cpp1402
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz-target-recorder.cpp797
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzz.cpp2322
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp329
-rw-r--r--src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp1877
7 files changed, 7378 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-config.cpp b/src/VBox/Runtime/common/fuzz/fuzz-config.cpp
new file mode 100644
index 00000000..9bf27e08
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz-config.cpp
@@ -0,0 +1,651 @@
+/* $Id: fuzz-config.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, config API.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/json.h>
+#include <iprt/vfs.h>
+#include <iprt/zip.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The index filename used to get all the other content. */
+#define RTFUZZ_CFG_INDEX_FILE_NAME "index.json"
+/** The custom config object member name. */
+#define RTFUZZ_CFG_JSON_CUSTOM_CFG "CustomCfg"
+/** The input corpus array member name. */
+#define RTFUZZ_CFG_JSON_INPUT_CORPUS "InputCorpus"
+/** The input name. */
+#define RTFUZZ_CFG_JSON_INPUT_NAME "Name"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal fuzzer config state.
+ */
+typedef struct RTFUZZCFGINT
+{
+ /** Magic value identifying the struct. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The VFS file handle we get the config from. */
+ RTVFSFILE hVfsFile;
+ /** The JSON root handle of the config. */
+ RTJSONVAL hJsonRoot;
+ /** The custom config file handle if existing. */
+ RTVFSFILE hVfsFileCustomCfg;
+} RTFUZZCFGINT;
+/** Pointer to the internal fuzzer config state. */
+typedef RTFUZZCFGINT *PRTFUZZCFGINT;
+/** Pointer to a const internal fuzzer config state. */
+typedef const RTFUZZCFGINT *PCRTFUZZCFGINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Creates a filesystem stream from the given VFS file object.
+ *
+ * @returns IPRT status code.
+ * @param phVfsFss Where to store the handle to the filesystem stream on success.
+ * @param hVFsFile The VFS file handle.
+ */
+static int rtFuzzCfgTarFssFromVfsFile(PRTVFSFSSTREAM phVfsFss, RTVFSFILE hVfsFile)
+{
+ int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSIOSTREAM hVfsFileIos = RTVfsFileToIoStream(hVfsFile);
+ if (hVfsFileIos != NIL_RTVFSIOSTREAM)
+ {
+ RTVFSIOSTREAM hGunzipIos;
+ rc = RTZipGzipDecompressIoStream(hVfsFileIos, 0 /*fFlags*/, &hGunzipIos);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSFSSTREAM hTarFss;
+ rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
+ if (RT_SUCCESS(rc))
+ {
+ RTVfsIoStrmRelease(hGunzipIos);
+ RTVfsIoStrmRelease(hVfsFileIos);
+ *phVfsFss = hTarFss;
+ return VINF_SUCCESS;
+ }
+
+ RTVfsIoStrmRelease(hGunzipIos);
+ }
+
+ RTVfsIoStrmRelease(hVfsFileIos);
+ }
+ else
+ rc = VERR_INVALID_STATE; /** @todo */
+ }
+
+ return rc;
+}
+
+
+/**
+ * Finds a given file in the filesystem stream.
+ *
+ * @returns IPRT status code.
+ * @param hVfsFss The filesystem stream handle.
+ * @param pszFilename The filename to look for.
+ * @param fValidateUtf8 Flag whether tpo validate the content as UTF-8.
+ * @param phVfsFile Where to store the VFS file handle on success (content is completely in memory).
+ */
+static int rtFuzzCfgFindFile(RTVFSFSSTREAM hVfsFss, const char *pszFilename, bool fValidateUtf8,
+ PRTVFSFILE phVfsFile)
+{
+ int rc = VINF_SUCCESS;
+
+ *phVfsFile = NIL_RTVFSFILE;
+ for (;;)
+ {
+ /*
+ * Get the next stream object.
+ */
+ char *pszName;
+ RTVFSOBJ hVfsObj;
+ RTVFSOBJTYPE enmType;
+ rc = RTVfsFsStrmNext(hVfsFss, &pszName, &enmType, &hVfsObj);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_EOF)
+ rc = VERR_NOT_FOUND;
+ break;
+ }
+ const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
+
+ if ( !strcmp(pszAdjName, pszFilename)
+ && ( enmType == RTVFSOBJTYPE_FILE
+ || enmType == RTVFSOBJTYPE_IO_STREAM))
+ {
+ RTStrFree(pszName);
+
+ RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
+ rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsFile);
+ if ( RT_SUCCESS(rc)
+ && fValidateUtf8)
+ rc = RTVfsIoStrmValidateUtf8Encoding(hVfsIos,
+ RTVFS_VALIDATE_UTF8_BY_RTC_3629 | RTVFS_VALIDATE_UTF8_NO_NULL,
+ NULL);
+
+ RTVfsObjRelease(hVfsObj);
+ RTVfsIoStrmRelease(hVfsIos);
+ if ( RT_FAILURE(rc)
+ && *phVfsFile != NIL_RTVFSFILE)
+ {
+ RTVfsFileRelease(*phVfsFile);
+ *phVfsFile = NIL_RTVFSFILE;
+ }
+ return rc;
+ }
+
+ /*
+ * Clean up.
+ */
+ RTVfsObjRelease(hVfsObj);
+ RTStrFree(pszName);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Returns the memorized file handle for the given name from the given tarball VFS file handle.
+ *
+ * @returns IPRT status code.
+ * @param hVfsTarball The VFS file handle of the tarball containing the object.
+ * @param pszFilename The filename to look for.
+ * @param fValidateUtf8 Flag whether tpo validate the content as UTF-8.
+ * @param phVfsFile Where to store the VFS file handle on success (content is completely in memory).
+ */
+static int rtFuzzCfgGrabFileFromTarball(RTVFSFILE hVfsTarball, const char *pszFilename, bool fValidateUtf8, PRTVFSFILE phVfsFile)
+{
+ RTVFSFSSTREAM hVfsFss;
+ int rc = rtFuzzCfgTarFssFromVfsFile(&hVfsFss, hVfsTarball);
+ if (RT_SUCCESS(rc))
+ {
+ /* Search for the index file and parse it. */
+ RTVFSFILE hVfsJson;
+ rc = rtFuzzCfgFindFile(hVfsFss, pszFilename, fValidateUtf8, &hVfsJson);
+ RTVfsFsStrmRelease(hVfsFss);
+ if (RT_SUCCESS(rc))
+ *phVfsFile = hVfsJson;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Loads the given fuzzing config.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing config instance.
+ * @param pErrInfo Additional error information, optional.
+ */
+static int rtFuzzCfgLoad(PRTFUZZCFGINT pThis, PRTERRINFO pErrInfo)
+{
+ /* Search for the index file and parse it. */
+ RTVFSFILE hVfsJson;
+ int rc = rtFuzzCfgGrabFileFromTarball(pThis->hVfsFile, RTFUZZ_CFG_INDEX_FILE_NAME, true /*fValidateUtf8*/, &hVfsJson);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTJsonParseFromVfsFile(&pThis->hJsonRoot, hVfsJson, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Look for the custom config in the JSON and find it in the VFS file. */
+ char *pszCustomCfgFilename = NULL;
+ rc = RTJsonValueQueryStringByName(pThis->hJsonRoot, RTFUZZ_CFG_JSON_CUSTOM_CFG, &pszCustomCfgFilename);
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS; /* The custom config is optional. */
+ if ( RT_SUCCESS(rc)
+ && pszCustomCfgFilename)
+ {
+ rc = rtFuzzCfgGrabFileFromTarball(pThis->hVfsFile, pszCustomCfgFilename, false /*fValidateUtf8*/, &pThis->hVfsFileCustomCfg);
+ RTStrFree(pszCustomCfgFilename);
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTJsonValueRelease(pThis->hJsonRoot);
+ pThis->hJsonRoot = NIL_RTJSONVAL;
+ }
+ }
+
+ RTVfsFileRelease(hVfsJson);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Searches for the given object name in the given JSON array, returning the object on success.
+ *
+ * @returns IPRT status code.
+ * @param hJsonValArr JSON value handle containing the input corpus objects.
+ * @param pszName The name to look for.
+ * @param phJsonVal Where to store the referenced JSON value on success.
+ */
+static int rtFuzzCfgQueryInputCorpusEntryFromArray(RTJSONVAL hJsonValArr, const char *pszName, PRTJSONVAL phJsonVal)
+{
+ int rc = VERR_NOT_FOUND;
+ uint32_t cEntries = RTJsonValueGetArraySize(hJsonValArr);
+
+ for (uint32_t i = 0; i < cEntries; i++)
+ {
+ RTJSONVAL hJsonVal;
+ int rc2 = RTJsonValueQueryByIndex(hJsonValArr, i, &hJsonVal);
+ if (RT_SUCCESS(rc2))
+ {
+ char *pszObjName;
+ rc2 = RTJsonValueQueryStringByName(hJsonVal, RTFUZZ_CFG_JSON_INPUT_NAME, &pszObjName);
+ if (RT_SUCCESS(rc2))
+ {
+ if (!strcmp(pszObjName, pszName))
+ {
+ RTStrFree(pszObjName);
+ *phJsonVal = hJsonVal;
+ return VINF_SUCCESS;
+ }
+
+ RTStrFree(pszObjName);
+ }
+
+ RTJsonValueRelease(hJsonVal);
+ }
+
+ if (RT_FAILURE(rc2))
+ {
+ rc = rc2;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries a 64bit unsigned integer.
+ *
+ * @returns IPRT status code.
+ * @param hJsonInp JSON object handle to search in.
+ * @param pszName Value name to look for.
+ * @param pu64Val Where to store the value on success.
+ */
+static int rtFuzzCfgInputQueryU64(RTJSONVAL hJsonInp, const char *pszName, uint64_t *pu64Val)
+{
+ int64_t i64Val;
+ int rc = RTJsonValueQueryIntegerByName(hJsonInp, pszName, &i64Val);
+ if (RT_SUCCESS(rc))
+ {
+ if (i64Val >= 0)
+ *pu64Val = (uint64_t)i64Val;
+ else
+ rc = VERR_OUT_OF_RANGE;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries a 64bit unsigned integer, supplying a default value if the name is not found in the
+ * given JSON object.
+ *
+ * @returns IPRT status code.
+ * @param hJsonInp JSON object handle to search in.
+ * @param pszName Value name to look for.
+ * @param pu64Val Where to store the value on success.
+ * @param u64Def The value to set if the value is not found.
+ */
+static int rtFuzzCfgInputQueryU64Def(RTJSONVAL hJsonInp, const char *pszName, uint64_t *pu64Val, uint64_t u64Def)
+{
+ int rc = rtFuzzCfgInputQueryU64(hJsonInp, pszName, pu64Val);
+ if (rc == VERR_NOT_FOUND)
+ {
+ *pu64Val = u64Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Adds the given input to the given fuzzing contexts input corpus.
+ *
+ * @returns IPRT status code.
+ * @param hFuzzCtx The fuzzing context to add the input to.
+ * @param hJsonInp The JSON input object with further parameters.
+ * @param hVfsIos The VFS I/O stream of the input data to add.
+ */
+static int rtFuzzCfgAddInputToCtx(RTFUZZCTX hFuzzCtx, RTJSONVAL hJsonInp, RTVFSIOSTREAM hVfsIos)
+{
+ uint64_t offMutStart = 0;
+ int rc = rtFuzzCfgInputQueryU64Def(hJsonInp, "MutationStartOffset", &offMutStart, 0);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t cbMutRange = UINT64_MAX;
+ rc = rtFuzzCfgInputQueryU64Def(hJsonInp, "MutationRangeSize", &cbMutRange, UINT64_MAX);
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzCtxCorpusInputAddFromVfsIoStrmEx(hFuzzCtx, hVfsIos, offMutStart, cbMutRange);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Sets the global fuzzer config form the given JSON object.
+ *
+ * @returns IPRT status code.
+ * @param hJsonRoot The JSON object handle for the fuzzer config.
+ * @param hFuzzCtx The fuzzing context to configure.
+ */
+static int rtFuzzCfgSetFuzzCtxCfg(RTJSONVAL hJsonRoot, RTFUZZCTX hFuzzCtx)
+{
+ uint64_t u64Tmp;
+ int rc = rtFuzzCfgInputQueryU64(hJsonRoot, "Seed", &u64Tmp);
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzCtxReseed(hFuzzCtx, u64Tmp);
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCfgInputQueryU64(hJsonRoot, "InputSizeMax", &u64Tmp);
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, (size_t)u64Tmp);
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t offMutateStart = 0;
+ uint64_t cbMutateRange = UINT64_MAX;
+ rc = rtFuzzCfgInputQueryU64(hJsonRoot, "MutationStartOffset", &offMutateStart);
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCfgInputQueryU64(hJsonRoot, "MutationRangeSize", &cbMutateRange);
+ if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzCtxCfgSetMutationRange(hFuzzCtx, offMutateStart, cbMutateRange);
+ }
+
+ /** @todo More here */
+ return rc;
+}
+
+
+/**
+ * Adds all inputs in the iven config file to the given fuzzer context.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing config instance.
+ * @param hJsonValCorpusArr The JSON array handle containing the input corpus configuration.
+ * @param hFuzzCtx The fuzzing context to configure.
+ */
+static int rtFuzzCfgAddFuzzCtxInputs(PRTFUZZCFGINT pThis, RTJSONVAL hJsonValCorpusArr, RTFUZZCTX hFuzzCtx)
+{
+ /*
+ * Go through the tarball sequentially and search the corresponding entries in the JSON array
+ * instead of the other way around because reopening the tarball and seeking around
+ * each time (filesystem stream) is much more expensive.
+ */
+ RTVFSFSSTREAM hVfsFss;
+ int rc = rtFuzzCfgTarFssFromVfsFile(&hVfsFss, pThis->hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /*
+ * Get the next stream object.
+ */
+ char *pszName;
+ RTVFSOBJ hVfsObj;
+ RTVFSOBJTYPE enmType;
+ rc = RTVfsFsStrmNext(hVfsFss, &pszName, &enmType, &hVfsObj);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_EOF)
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ if ( enmType == RTVFSOBJTYPE_FILE
+ || enmType == RTVFSOBJTYPE_IO_STREAM)
+ {
+ const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
+
+ /* Skip the index.json. */
+ if (strcmp(pszAdjName, RTFUZZ_CFG_INDEX_FILE_NAME))
+ {
+ /* Look for a JSON object with the matching filename and process it. */
+ RTJSONVAL hJsonInp = NIL_RTJSONVAL;
+ rc = rtFuzzCfgQueryInputCorpusEntryFromArray(hJsonValCorpusArr, pszAdjName, &hJsonInp);
+ if (RT_SUCCESS(rc))
+ {
+ RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
+ rc = rtFuzzCfgAddInputToCtx(hFuzzCtx, hJsonInp, hVfsIos);
+ RTVfsIoStrmRelease(hVfsIos);
+ RTJsonValueRelease(hJsonInp);
+ }
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ RTVfsObjRelease(hVfsObj);
+ RTStrFree(pszName);
+ if (RT_FAILURE(rc))
+ break; /* Abort on error. */
+ }
+
+ RTVfsFsStrmRelease(hVfsFss);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Destroys the given fuzzing config.
+ *
+ * @param pThis The fuzzing config instance to destroy.
+ */
+static void rtFuzzCfgDestroy(PRTFUZZCFGINT pThis)
+{
+ RTJsonValueRelease(pThis->hJsonRoot);
+ RTVfsFileRelease(pThis->hVfsFile);
+ if (pThis->hVfsFileCustomCfg != NIL_RTVFSFILE)
+ RTVfsFileRelease(pThis->hVfsFileCustomCfg);
+ pThis->hVfsFile = NIL_RTVFSFILE;
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTFuzzCfgCreateFromVfsFile(PRTFUZZCFG phFuzzCfg, RTVFSFILE hVfsFile, PRTERRINFO pErrInfo)
+{
+ AssertPtrReturn(phFuzzCfg, VERR_INVALID_POINTER);
+
+ int rc;
+ PRTFUZZCFGINT pThis = (PRTFUZZCFGINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = 0; /** @todo */
+ pThis->cRefs = 1;
+ RTVfsFileRetain(hVfsFile);
+ pThis->hVfsFile = hVfsFile;
+ pThis->hVfsFileCustomCfg = NIL_RTVFSFILE;
+
+ rc = rtFuzzCfgLoad(pThis, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *phFuzzCfg = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTVfsFileRelease(hVfsFile);
+ pThis->hVfsFile = NULL;
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCfgCreateFromFile(PRTFUZZCFG phFuzzCfg, const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ RTVFSFILE hVfsFile;
+ int rc = RTVfsFileOpenNormal(pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzCfgCreateFromVfsFile(phFuzzCfg, hVfsFile, pErrInfo);
+ RTVfsFileRelease(hVfsFile);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTFuzzCfgRetain(RTFUZZCFG hFuzzCfg)
+{
+ PRTFUZZCFGINT pThis = hFuzzCfg;
+
+ 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) RTFuzzCfgRelease(RTFUZZCFG hFuzzCfg)
+{
+ PRTFUZZCFGINT pThis = hFuzzCfg;
+ if (pThis == NIL_RTFUZZCFG)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtFuzzCfgDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTFuzzCfgImport(RTFUZZCFG hFuzzCfg, RTFUZZCTX hFuzzCtx, uint32_t fFlags)
+{
+ AssertReturn(hFuzzCfg != NIL_RTFUZZCFG, VERR_INVALID_HANDLE);
+ AssertReturn(hFuzzCtx != NIL_RTFUZZCTX, VERR_INVALID_HANDLE);
+ AssertReturn(!(fFlags & ~RTFUZZCFG_IMPORT_F_VALID), VERR_INVALID_PARAMETER);
+
+ /* Get the input corpus array. */
+ PRTFUZZCFGINT pThis = hFuzzCfg;
+ RTJSONVAL hJsonValCorpusArr;
+ int rc = RTJsonValueQueryByName(pThis->hJsonRoot, RTFUZZ_CFG_JSON_INPUT_CORPUS, &hJsonValCorpusArr);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTJsonValueGetType(hJsonValCorpusArr) == RTJSONVALTYPE_ARRAY)
+ {
+ /* If not ommitted set the global fuzzing context config now. */
+ if (!(fFlags & RTFUZZCFG_IMPORT_F_ONLY_INPUT))
+ rc = rtFuzzCfgSetFuzzCtxCfg(pThis->hJsonRoot, hFuzzCtx);
+
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCfgAddFuzzCtxInputs(pThis, hJsonValCorpusArr, hFuzzCtx);
+ }
+ else
+ rc = VERR_JSON_VALUE_INVALID_TYPE;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCfgQueryCustomCfg(RTFUZZCFG hFuzzCfg, PRTVFSFILE phVfsFile)
+{
+ PRTFUZZCFGINT pThis = hFuzzCfg;
+
+ if (pThis->hVfsFileCustomCfg != NIL_RTVFSFILE)
+ {
+ RTVfsFileRetain(pThis->hVfsFileCustomCfg);
+ *phVfsFile = pThis->hVfsFileCustomCfg;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_FOUND;
+}
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..1022f8f6
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp
@@ -0,0 +1,1402 @@
+/* $Id: fuzz-observer.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, observer.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* 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
+
+/** Length of the input queue for an observer thread. */
+# define RTFUZZOBS_THREAD_INPUT_QUEUE_MAX UINT32_C(5)
+
+
+/*********************************************************************************************************************************
+* 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;
+ /** Number of inputs in the queue. */
+ volatile uint32_t cInputs;
+ /** Where to insert the next input. */
+ volatile uint32_t offQueueInputW;
+ /** Where to retrieve the next input from. */
+ volatile uint32_t offQueueInputR;
+ /** The input queue for this thread. */
+ RTFUZZINPUT ahQueueInput[RTFUZZOBS_THREAD_INPUT_QUEUE_MAX];
+} 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;
+ /** The target state recorder. */
+ RTFUZZTGTREC hTgtRec;
+ /** Temp directory for input files. */
+ char *pszTmpDir;
+ /** Results directory. */
+ char *pszResultsDir;
+ /** The binary to run. */
+ char *pszBinary;
+ /** The filename path of the binary. */
+ const char *pszBinaryFilename;
+ /** Arguments to run the binary with, terminated by a NULL entry. */
+ char **papszArgs;
+ /** The environment to use for the target. */
+ RTENV hEnv;
+ /** Any configured sanitizers. */
+ uint32_t fSanitizers;
+ /** Sanitizer related options set in the environment block. */
+ char *pszSanitizerOpts;
+ /** 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;
+
+
+/**
+ * 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 environment block to use. */
+ RTENV hEnv;
+ /** The process to monitor. */
+ RTPROCESS hProc;
+ /** Execution time of the process. */
+ RTMSINTERVAL msExec;
+ /** The recording state handle. */
+ RTFUZZTGTSTATE hTgtState;
+ /** Current input data pointer. */
+ uint8_t *pbInputCur;
+ /** Number of bytes left for the input. */
+ size_t cbInputLeft;
+ /** 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;
+
+
+
+/**
+ * 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;
+
+ rc = RTEnvClone(&pExecCtx->hEnv, pThis->hEnv);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzTgtRecorderCreateNewState(pThis->hTgtRec, &pExecCtx->hTgtState);
+ if (RT_SUCCESS(rc))
+ {
+ 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);
+ }
+
+ RTFuzzTgtStateRelease(pExecCtx->hTgtState);
+ }
+
+ RTEnvDestroy(pExecCtx->hEnv);
+ }
+
+ RTMemFree(pExecCtx);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Destroys the given execution context.
+ *
+ * @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++;
+ }
+
+ if (pExecCtx->hTgtState != NIL_RTFUZZTGTSTATE)
+ RTFuzzTgtStateRelease(pExecCtx->hTgtState);
+ RTEnvDestroy(pExecCtx->hEnv);
+ 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)
+{
+ int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], pExecCtx->hEnv, 0 /*fFlags*/, &pExecCtx->StdinHandle,
+ &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, 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 = RTFuzzTgtStateAppendStdoutFromPipe(pExecCtx->hTgtState, pExecCtx->hPipeStdoutR);
+ AssertRC(rc);
+ }
+ else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
+ {
+ Assert(fEvtsRecv & RTPOLL_EVT_READ);
+
+ rc = RTFuzzTgtStateAppendStderrFromPipe(pExecCtx->hTgtState, 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))
+ {
+ /* Add the coverage report to the sanitizer if enabled. */
+ if (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_SANCOV)
+ {
+ char szSanCovReport[RTPATH_MAX];
+ ssize_t cch = RTStrPrintf2(&szSanCovReport[0], sizeof(szSanCovReport),
+ "%s%c%s.%u.sancov",
+ pThis->pszTmpDir, RTPATH_SLASH,
+ pThis->pszBinaryFilename, pExecCtx->hProc);
+ Assert(cch > 0); RT_NOREF(cch);
+ rc = RTFuzzTgtStateAddSanCovReportFromFile(pExecCtx->hTgtState, &szSanCovReport[0]);
+ RTFileDelete(&szSanCovReport[0]);
+ }
+ 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)
+{
+ int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], pExecCtx->hEnv, 0 /*fFlags*/, &pExecCtx->StdinHandle,
+ &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, 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 = RTFuzzCtxStateExportToMem(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 = RTFuzzTgtStateAppendStderrFromPipe(pExecCtx->hTgtState, 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))
+ rc = RTFuzzTgtStateDumpToDir(pExecCtx->hTgtState, &szPath[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;
+
+ char szInput[RTPATH_MAX]; RT_ZERO(szInput);
+ 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);
+
+ rc = RTPathJoin(szInput, sizeof(szInput), pThis->pszTmpDir, &szFilename[0]);
+ AssertRC(rc);
+
+ RTFUZZOBSVARIABLE aVar[2] =
+ {
+ { "${INPUT}", sizeof("${INPUT}") - 1, &szInput[0] },
+ { NULL, 0, NULL }
+ };
+ rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, &aVar[0]);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ while (!pObsThrd->fShutdown)
+ {
+ /* Wait for work. */
+ if (!ASMAtomicReadU32(&pObsThrd->cInputs))
+ {
+ rc = RTThreadUserWait(hThrd, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+
+ if (pObsThrd->fShutdown)
+ break;
+
+ if (!ASMAtomicReadU32(&pObsThrd->cInputs))
+ continue;
+
+ uint32_t offRead = ASMAtomicReadU32(&pObsThrd->offQueueInputR);
+ RTFUZZINPUT hFuzzInput = pObsThrd->ahQueueInput[offRead];
+
+ ASMAtomicDecU32(&pObsThrd->cInputs);
+ offRead = (offRead + 1) % RT_ELEMENTS(pObsThrd->ahQueueInput);
+ ASMAtomicWriteU32(&pObsThrd->offQueueInputR, offRead);
+ if (!ASMAtomicBitTestAndSet(&pThis->bmEvt, pObsThrd->idObs))
+ RTSemEventSignal(pThis->hEvtGlobal);
+
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
+ rc = RTFuzzInputWriteToFile(hFuzzInput, &szInput[0]);
+ else if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN)
+ {
+ rc = RTFuzzInputQueryBlobData(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))
+ {
+ rc = RTFuzzTgtStateAddProcSts(pExecCtx->hTgtState, &ProcSts);
+ AssertRC(rc);
+
+ if (ProcSts.enmReason != RTPROCEXITREASON_NORMAL)
+ {
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsCrash);
+ rc = rtFuzzObsAddInputToResults(pThis, hFuzzInput, pExecCtx);
+ }
+ }
+ else if (rc == VERR_TIMEOUT)
+ {
+ ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsHang);
+ rc = rtFuzzObsAddInputToResults(pThis, hFuzzInput, pExecCtx);
+ }
+ else
+ AssertFailed();
+
+ /*
+ * Check whether we reached an unknown target state and add the input to the
+ * corpus in that case.
+ */
+ rc = RTFuzzTgtStateAddToRecorder(pExecCtx->hTgtState);
+ if (RT_SUCCESS(rc))
+ {
+ /* Add to corpus and create a new target state for the next run. */
+ RTFuzzInputAddToCtxCorpus(hFuzzInput);
+ RTFuzzTgtStateRelease(pExecCtx->hTgtState);
+ pExecCtx->hTgtState = NIL_RTFUZZTGTSTATE;
+ rc = RTFuzzTgtRecorderCreateNewState(pThis->hTgtRec, &pExecCtx->hTgtState);
+ AssertRC(rc);
+ }
+ else
+ {
+ Assert(rc == VERR_ALREADY_EXISTS);
+ /* Reset the state for the next run. */
+ rc = RTFuzzTgtStateReset(pExecCtx->hTgtState);
+ AssertRC(rc);
+ }
+ RTFuzzInputRelease(hFuzzInput);
+
+ if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
+ RTFileDelete(&szInput[0]);
+ }
+ }
+
+ rtFuzzObsExecCtxDestroy(pThis, pExecCtx);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Fills the input queue of the given observer thread until it is full.
+ *
+ * @returns IPRT status code.
+ * @param pThis Pointer to the observer instance data.
+ * @param pObsThrd The observer thread instance to fill.
+ */
+static int rtFuzzObsMasterInputQueueFill(PRTFUZZOBSINT pThis, PRTFUZZOBSTHRD pObsThrd)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t cInputsAdded = 0;
+ uint32_t cInputsAdd = RTFUZZOBS_THREAD_INPUT_QUEUE_MAX - ASMAtomicReadU32(&pObsThrd->cInputs);
+ uint32_t offW = ASMAtomicReadU32(&pObsThrd->offQueueInputW);
+
+ while ( cInputsAdded < cInputsAdd
+ && RT_SUCCESS(rc))
+ {
+ RTFUZZINPUT hFuzzInput = NIL_RTFUZZINPUT;
+ rc = RTFuzzCtxInputGenerate(pThis->hFuzzCtx, &hFuzzInput);
+ if (RT_SUCCESS(rc))
+ {
+ pObsThrd->ahQueueInput[offW] = hFuzzInput;
+ offW = (offW + 1) % RTFUZZOBS_THREAD_INPUT_QUEUE_MAX;
+ cInputsAdded++;
+ }
+ }
+
+ ASMAtomicWriteU32(&pObsThrd->offQueueInputW, offW);
+ ASMAtomicAddU32(&pObsThrd->cInputs, cInputsAdded);
+
+ return rc;
+}
+
+
+/**
+ * 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];
+
+ rc = rtFuzzObsMasterInputQueueFill(pThis, pObsThrd);
+ if (RT_SUCCESS(rc))
+ 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->idObs = idObs;
+ pObsThrd->fShutdown = false;
+ pObsThrd->cInputs = 0;
+ pObsThrd->offQueueInputW = 0;
+ pObsThrd->offQueueInputR = 0;
+
+ 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;
+}
+
+
+/**
+ * Sets up any configured sanitizers to cooperate with the observer.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal fuzzing observer state.
+ */
+static int rtFuzzObsSetupSanitizerCfg(PRTFUZZOBSINT pThis)
+{
+ int rc = VINF_SUCCESS;
+ bool fSep = false;
+
+ if (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_ASAN)
+ {
+ /*
+ * Need to set abort_on_error=1 in ASAN_OPTIONS or
+ * the sanitizer will call exit() instead of abort() and we
+ * don't catch invalid memory accesses.
+ */
+ rc = RTStrAAppend(&pThis->pszSanitizerOpts, "abort_on_error=1");
+ fSep = true;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_SANCOV))
+ {
+ /*
+ * The coverage sanitizer will dump coverage information into a file
+ * on process exit. Need to configure the directory where to dump it.
+ */
+ char aszSanCovCfg[_4K];
+ ssize_t cch = RTStrPrintf2(&aszSanCovCfg[0], sizeof(aszSanCovCfg),
+ "%scoverage=1:coverage_dir=%s",
+ fSep ? ":" : "", pThis->pszTmpDir);
+ if (cch > 0)
+ rc = RTStrAAppend(&pThis->pszSanitizerOpts, &aszSanCovCfg[0]);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ fSep = true;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pThis->pszSanitizerOpts)
+ {
+ /* Add it to the environment. */
+ if (pThis->hEnv == RTENV_DEFAULT)
+ {
+ /* Clone the environment to keep the default one untouched. */
+ rc = RTEnvClone(&pThis->hEnv, RTENV_DEFAULT);
+ }
+ if (RT_SUCCESS(rc))
+ rc = RTEnvSetEx(pThis->hEnv, "ASAN_OPTIONS", pThis->pszSanitizerOpts);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzObsCreate(PRTFUZZOBS phFuzzObs, RTFUZZCTXTYPE enmType, uint32_t fTgtRecFlags)
+{
+ AssertPtrReturn(phFuzzObs, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->pszBinary = NULL;
+ pThis->pszBinaryFilename = NULL;
+ pThis->papszArgs = NULL;
+ pThis->hEnv = RTENV_DEFAULT;
+ 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, enmType);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzTgtRecorderCreate(&pThis->hTgtRec, fTgtRecFlags);
+ if (RT_SUCCESS(rc))
+ {
+ *phFuzzObs = pThis;
+ return VINF_SUCCESS;
+ }
+ RTFuzzCtxRelease(pThis->hFuzzCtx);
+ }
+
+ 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);
+ if (pThis->pszSanitizerOpts)
+ RTStrFree(pThis->pszSanitizerOpts);
+ if (pThis->hEnv != RTENV_DEFAULT)
+ {
+ RTEnvDestroy(pThis->hEnv);
+ pThis->hEnv = RTENV_DEFAULT;
+ }
+ RTFuzzTgtRecorderRelease(pThis->hTgtRec);
+ 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;
+ else
+ pThis->pszBinaryFilename = RTPathFilename(pThis->pszBinary);
+ 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) RTFuzzObsSetTestBinaryEnv(RTFUZZOBS hFuzzObs, RTENV hEnv)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ pThis->hEnv = hEnv;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzObsSetTestBinarySanitizers(RTFUZZOBS hFuzzObs, uint32_t fSanitizers)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ pThis->fSanitizers = fSanitizers;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzObsSetTestBinaryTimeout(RTFUZZOBS hFuzzObs, RTMSINTERVAL msTimeoutMax)
+{
+ PRTFUZZOBSINT pThis = hFuzzObs;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ pThis->msWaitMax = msTimeoutMax;
+ return VINF_SUCCESS;
+}
+
+
+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);
+
+ rc = rtFuzzObsSetupSanitizerCfg(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /* 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);
+ }
+
+ 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-target-recorder.cpp b/src/VBox/Runtime/common/fuzz/fuzz-target-recorder.cpp
new file mode 100644
index 00000000..36651427
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz-target-recorder.cpp
@@ -0,0 +1,797 @@
+/* $Id: fuzz-target-recorder.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, target state recorder.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/crc.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to the internal fuzzed target recorder state. */
+typedef struct RTFUZZTGTRECINT *PRTFUZZTGTRECINT;
+
+
+/**
+ * Stdout/Stderr buffer.
+ */
+typedef struct RTFUZZTGTSTDOUTERRBUF
+{
+ /** Current amount buffered. */
+ size_t cbBuf;
+ /** Maxmium amount to buffer. */
+ size_t cbBufMax;
+ /** Base pointer to the data buffer. */
+ uint8_t *pbBase;
+} RTFUZZTGTSTDOUTERRBUF;
+/** Pointer to a stdout/stderr buffer. */
+typedef RTFUZZTGTSTDOUTERRBUF *PRTFUZZTGTSTDOUTERRBUF;
+
+
+/**
+ * Internal fuzzed target state.
+ */
+typedef struct RTFUZZTGTSTATEINT
+{
+ /** Node for the list of states. */
+ RTLISTNODE NdStates;
+ /** Checksum for the state. */
+ uint64_t uChkSum;
+ /** Magic identifying the structure. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The owning recorder instance. */
+ PRTFUZZTGTRECINT pTgtRec;
+ /** Flag whether the state is finalized. */
+ bool fFinalized;
+ /** Flag whether the state is contained in the recorded set. */
+ bool fInRecSet;
+ /** The stdout data buffer. */
+ RTFUZZTGTSTDOUTERRBUF StdOutBuf;
+ /** The stderr data buffer. */
+ RTFUZZTGTSTDOUTERRBUF StdErrBuf;
+ /** Process status. */
+ RTPROCSTATUS ProcSts;
+ /** Coverage report buffer. */
+ void *pvCovReport;
+ /** Size of the coverage report in bytes. */
+ size_t cbCovReport;
+ /** Number of traced edges. */
+ size_t cEdges;
+} RTFUZZTGTSTATEINT;
+/** Pointer to an internal fuzzed target state. */
+typedef RTFUZZTGTSTATEINT *PRTFUZZTGTSTATEINT;
+
+
+/**
+ * Recorder states node in the AVL tree.
+ */
+typedef struct RTFUZZTGTRECNODE
+{
+ /** The AVL tree core (keyed by checksum). */
+ AVLU64NODECORE Core;
+ /** The list anchor for the individual states. */
+ RTLISTANCHOR LstStates;
+} RTFUZZTGTRECNODE;
+/** Pointer to a recorder states node. */
+typedef RTFUZZTGTRECNODE *PRTFUZZTGTRECNODE;
+
+
+/**
+ * Edge information node.
+ */
+typedef struct RTFUZZTGTEDGE
+{
+ /** The AVL tree core (keyed by offset). */
+ AVLU64NODECORE Core;
+ /** Number of times the edge was hit. */
+ volatile uint64_t cHits;
+} RTFUZZTGTEDGE;
+/** Pointer to a edge information node. */
+typedef RTFUZZTGTEDGE *PRTFUZZTGTEDGE;
+
+
+/**
+ * Internal fuzzed target recorder state.
+ */
+typedef struct RTFUZZTGTRECINT
+{
+ /** Magic value for identification. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Flags passed when the recorder was created. */
+ uint32_t fRecFlags;
+ /** Semaphore protecting the states tree. */
+ RTSEMRW hSemRwStates;
+ /** The AVL tree for indexing the recorded state (keyed by stdout/stderr buffer size). */
+ AVLU64TREE TreeStates;
+ /** Semaphore protecting the edges tree. */
+ RTSEMRW hSemRwEdges;
+ /** The AVL tree for discovered edges when coverage reports are collected. */
+ AVLU64TREE TreeEdges;
+ /** Number of edges discovered so far. */
+ volatile uint64_t cEdges;
+ /** The discovered offset width. */
+ volatile uint32_t cbCovOff;
+} RTFUZZTGTRECINT;
+
+
+/** SanCov magic for 64bit offsets. */
+#define SANCOV_MAGIC_64 UINT64_C(0xc0bfffffffffff64)
+/** SanCov magic for 32bit offsets. */
+#define SANCOV_MAGIC_32 UINT64_C(0xc0bfffffffffff32)
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Initializes the given stdout/stderr buffer.
+ *
+ * @param pBuf The buffer to initialize.
+ */
+static void rtFuzzTgtStdOutErrBufInit(PRTFUZZTGTSTDOUTERRBUF pBuf)
+{
+ pBuf->cbBuf = 0;
+ pBuf->cbBufMax = 0;
+ pBuf->pbBase = NULL;
+}
+
+
+/**
+ * Frees all allocated resources in the given stdout/stderr buffer.
+ *
+ * @param pBuf The buffer to free.
+ */
+static void rtFuzzTgtStdOutErrBufFree(PRTFUZZTGTSTDOUTERRBUF pBuf)
+{
+ if (pBuf->pbBase)
+ RTMemFree(pBuf->pbBase);
+}
+
+
+/**
+ * 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 rtFuzzTgtStdOutErrBufFillFromPipe(PRTFUZZTGTSTDOUTERRBUF 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 buffer to the given file.
+ *
+ * @returns IPRT status code.
+ * @param pBuf The buffer to write.
+ * @param pszFilename Where to write the buffer.
+ */
+static int rtFuzzTgtStateStdOutErrBufWriteToFile(PRTFUZZTGTSTDOUTERRBUF 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;
+}
+
+
+/**
+ * Scans the given target state for newly discovered edges in the coverage report.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer target recorder instance.
+ * @param pTgtState The target state to check.
+ */
+static int rtFuzzTgtRecScanStateForNewEdges(PRTFUZZTGTRECINT pThis, PRTFUZZTGTSTATEINT pTgtState)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pTgtState->pvCovReport)
+ {
+ rc = RTSemRWRequestRead(pThis->hSemRwEdges, RT_INDEFINITE_WAIT); AssertRC(rc);
+
+ uint32_t cbCovOff = ASMAtomicReadU32(&pThis->cbCovOff);
+ Assert(cbCovOff != 0);
+
+ uint8_t *pbCovCur = (uint8_t *)pTgtState->pvCovReport;
+ size_t cEdgesLeft = pTgtState->cbCovReport / cbCovOff;
+ while (cEdgesLeft)
+ {
+ uint64_t offCur = cbCovOff == sizeof(uint64_t)
+ ? *(uint64_t *)pbCovCur
+ : *(uint32_t *)pbCovCur;
+
+ PRTFUZZTGTEDGE pEdge = (PRTFUZZTGTEDGE)RTAvlU64Get(&pThis->TreeEdges, offCur);
+ if (!pEdge)
+ {
+ /* New edge discovered, allocate and add. */
+ rc = RTSemRWReleaseRead(pThis->hSemRwEdges); AssertRC(rc);
+
+ pEdge = (PRTFUZZTGTEDGE)RTMemAllocZ(sizeof(RTFUZZTGTEDGE));
+ if (RT_LIKELY(pEdge))
+ {
+ pEdge->Core.Key = offCur;
+ pEdge->cHits = 1;
+ rc = RTSemRWRequestWrite(pThis->hSemRwEdges, RT_INDEFINITE_WAIT); AssertRC(rc);
+
+ bool fIns = RTAvlU64Insert(&pThis->TreeEdges, &pEdge->Core);
+ if (!fIns)
+ {
+ /* Someone raced us, free and query again. */
+ RTMemFree(pEdge);
+ pEdge = (PRTFUZZTGTEDGE)RTAvlU64Get(&pThis->TreeEdges, offCur);
+ AssertPtr(pEdge);
+
+ ASMAtomicIncU64(&pEdge->cHits);
+ }
+ else
+ ASMAtomicIncU64(&pThis->cEdges);
+
+ rc = RTSemRWReleaseWrite(pThis->hSemRwEdges); AssertRC(rc);
+ rc = RTSemRWRequestRead(pThis->hSemRwEdges, RT_INDEFINITE_WAIT); AssertRC(rc);
+ }
+ else
+ {
+ rc = RTSemRWRequestRead(pThis->hSemRwEdges, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ else
+ ASMAtomicIncU64(&pEdge->cHits);
+
+ pbCovCur += cbCovOff;
+ cEdgesLeft--;
+ }
+
+ rc = RTSemRWReleaseRead(pThis->hSemRwEdges); AssertRC(rc);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Destorys the given fuzzer target recorder freeing all allocated resources.
+ *
+ * @param pThis The fuzzer target recorder instance.
+ */
+static void rtFuzzTgtRecDestroy(PRTFUZZTGTRECINT pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/**
+ * Destroys the given fuzzer target state freeing all allocated resources.
+ *
+ * @param pThis The fuzzed target state instance.
+ */
+static void rtFuzzTgtStateDestroy(PRTFUZZTGTSTATEINT pThis)
+{
+ pThis->u32Magic = ~(uint32_t)0; /** @todo Dead magic */
+ rtFuzzTgtStdOutErrBufFree(&pThis->StdOutBuf);
+ rtFuzzTgtStdOutErrBufFree(&pThis->StdErrBuf);
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Compares two given target states, checking whether they match.
+ *
+ * @returns Flag whether the states are identical.
+ * @param pThis Target state 1.
+ * @param pThat Target state 2.
+ */
+static bool rtFuzzTgtStateDoMatch(PRTFUZZTGTSTATEINT pThis, PRTFUZZTGTSTATEINT pThat)
+{
+ PRTFUZZTGTRECINT pTgtRec = pThis->pTgtRec;
+ Assert(pTgtRec == pThat->pTgtRec);
+
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_STDOUT)
+ && ( pThis->StdOutBuf.cbBuf != pThat->StdOutBuf.cbBuf
+ || ( pThis->StdOutBuf.cbBuf > 0
+ && memcmp(pThis->StdOutBuf.pbBase, pThat->StdOutBuf.pbBase, pThis->StdOutBuf.cbBuf))))
+ return false;
+
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_STDERR)
+ && ( pThis->StdErrBuf.cbBuf != pThat->StdErrBuf.cbBuf
+ || ( pThis->StdErrBuf.cbBuf > 0
+ && memcmp(pThis->StdErrBuf.pbBase, pThat->StdErrBuf.pbBase, pThis->StdErrBuf.cbBuf))))
+ return false;
+
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_PROCSTATUS)
+ && memcmp(&pThis->ProcSts, &pThat->ProcSts, sizeof(RTPROCSTATUS)))
+ return false;
+
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_SANCOV)
+ && ( pThis->cbCovReport != pThat->cbCovReport
+ || ( pThis->cbCovReport > 0
+ && memcmp(pThis->pvCovReport, pThat->pvCovReport, pThis->cbCovReport))))
+ return false;
+
+ return true;
+}
+
+
+RTDECL(int) RTFuzzTgtRecorderCreate(PRTFUZZTGTREC phFuzzTgtRec, uint32_t fRecFlags)
+{
+ AssertPtrReturn(phFuzzTgtRec, VERR_INVALID_POINTER);
+ AssertReturn(!(fRecFlags & ~RTFUZZTGT_REC_STATE_F_VALID), VERR_INVALID_PARAMETER);
+
+ int rc;
+ PRTFUZZTGTRECINT pThis = (PRTFUZZTGTRECINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = 0; /** @todo */
+ pThis->cRefs = 1;
+ pThis->TreeStates = NULL;
+ pThis->TreeEdges = NULL;
+ pThis->cbCovOff = 0;
+ pThis->fRecFlags = fRecFlags;
+
+ rc = RTSemRWCreate(&pThis->hSemRwStates);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemRWCreate(&pThis->hSemRwEdges);
+ if (RT_SUCCESS(rc))
+ {
+ *phFuzzTgtRec = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTSemRWDestroy(pThis->hSemRwStates);
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTFuzzTgtRecorderRetain(RTFUZZTGTREC hFuzzTgtRec)
+{
+ PRTFUZZTGTRECINT pThis = hFuzzTgtRec;
+
+ 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) RTFuzzTgtRecorderRelease(RTFUZZTGTREC hFuzzTgtRec)
+{
+ PRTFUZZTGTRECINT pThis = hFuzzTgtRec;
+ if (pThis == NIL_RTFUZZTGTREC)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtFuzzTgtRecDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTFuzzTgtRecorderCreateNewState(RTFUZZTGTREC hFuzzTgtRec, PRTFUZZTGTSTATE phFuzzTgtState)
+{
+ PRTFUZZTGTRECINT pThis = hFuzzTgtRec;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phFuzzTgtState, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PRTFUZZTGTSTATEINT pState = (PRTFUZZTGTSTATEINT)RTMemAllocZ(sizeof(*pState));
+ if (RT_LIKELY(pState))
+ {
+ pState->u32Magic = 0; /** @todo */
+ pState->cRefs = 1;
+ pState->pTgtRec = pThis;
+ pState->fFinalized = false;
+ rtFuzzTgtStdOutErrBufInit(&pState->StdOutBuf);
+ rtFuzzTgtStdOutErrBufInit(&pState->StdErrBuf);
+ *phFuzzTgtState = pState;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTFuzzTgtStateRetain(RTFUZZTGTSTATE hFuzzTgtState)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+
+ 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) RTFuzzTgtStateRelease(RTFUZZTGTSTATE hFuzzTgtState)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ if (pThis == NIL_RTFUZZTGTSTATE)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0 && !pThis->fInRecSet)
+ rtFuzzTgtStateDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTFuzzTgtStateReset(RTFUZZTGTSTATE hFuzzTgtState)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ /* Clear the buffers. */
+ pThis->StdOutBuf.cbBuf = 0;
+ pThis->StdErrBuf.cbBuf = 0;
+ RT_ZERO(pThis->ProcSts);
+ if (pThis->pvCovReport)
+ RTMemFree(pThis->pvCovReport);
+ pThis->pvCovReport = NULL;
+ pThis->fFinalized = false;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzTgtStateFinalize(RTFUZZTGTSTATE hFuzzTgtState)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ /* Create the checksum. */
+ PRTFUZZTGTRECINT pTgtRec = pThis->pTgtRec;
+ uint64_t uChkSum = RTCrc64Start();
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_STDOUT)
+ && pThis->StdOutBuf.cbBuf)
+ uChkSum = RTCrc64Process(uChkSum, pThis->StdOutBuf.pbBase, pThis->StdOutBuf.cbBuf);
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_STDERR)
+ && pThis->StdErrBuf.cbBuf)
+ uChkSum = RTCrc64Process(uChkSum, pThis->StdErrBuf.pbBase, pThis->StdErrBuf.cbBuf);
+ if (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_PROCSTATUS)
+ uChkSum = RTCrc64Process(uChkSum, &pThis->ProcSts, sizeof(RTPROCSTATUS));
+ if ( (pTgtRec->fRecFlags & RTFUZZTGT_REC_STATE_F_SANCOV)
+ && pThis->pvCovReport)
+ uChkSum = RTCrc64Process(uChkSum, pThis->pvCovReport, pThis->cbCovReport);
+
+ pThis->uChkSum = RTCrc64Finish(uChkSum);
+ pThis->fFinalized = true;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzTgtStateAddToRecorder(RTFUZZTGTSTATE hFuzzTgtState)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ if (!pThis->fFinalized)
+ {
+ int rc = RTFuzzTgtStateFinalize(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ PRTFUZZTGTRECINT pTgtRec = pThis->pTgtRec;
+
+ /* Try to find a node matching the stdout and sterr sizes first. */
+ int rc = RTSemRWRequestRead(pTgtRec->hSemRwStates, RT_INDEFINITE_WAIT); AssertRC(rc);
+ PRTFUZZTGTRECNODE pNode = (PRTFUZZTGTRECNODE)RTAvlU64Get(&pTgtRec->TreeStates, pThis->uChkSum);
+ if (pNode)
+ {
+ /* Traverse the states and check if any matches the stdout and stderr buffers exactly. */
+ PRTFUZZTGTSTATEINT pIt;
+ bool fMatchFound = false;
+ RTListForEach(&pNode->LstStates, pIt, RTFUZZTGTSTATEINT, NdStates)
+ {
+ if (rtFuzzTgtStateDoMatch(pThis, pIt))
+ {
+ fMatchFound = true;
+ break;
+ }
+ }
+
+ rc = RTSemRWReleaseRead(pTgtRec->hSemRwStates); AssertRC(rc);
+ if (!fMatchFound)
+ {
+ rc = RTSemRWRequestWrite(pTgtRec->hSemRwStates, RT_INDEFINITE_WAIT); AssertRC(rc);
+ RTListAppend(&pNode->LstStates, &pThis->NdStates);
+ rc = RTSemRWReleaseWrite(pTgtRec->hSemRwStates); AssertRC(rc);
+ pThis->fInRecSet = true;
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ }
+ else
+ {
+ rc = RTSemRWReleaseRead(pTgtRec->hSemRwStates); AssertRC(rc);
+
+ /* No node found, create new one and insert in to the tree right away. */
+ pNode = (PRTFUZZTGTRECNODE)RTMemAllocZ(sizeof(*pNode));
+ if (RT_LIKELY(pNode))
+ {
+ pNode->Core.Key = pThis->uChkSum;
+ RTListInit(&pNode->LstStates);
+ RTListAppend(&pNode->LstStates, &pThis->NdStates);
+ rc = RTSemRWRequestWrite(pTgtRec->hSemRwStates, RT_INDEFINITE_WAIT); AssertRC(rc);
+ bool fIns = RTAvlU64Insert(&pTgtRec->TreeStates, &pNode->Core);
+ if (!fIns)
+ {
+ /* Someone raced us, get the new node and append there. */
+ RTMemFree(pNode);
+ pNode = (PRTFUZZTGTRECNODE)RTAvlU64Get(&pTgtRec->TreeStates, pThis->uChkSum);
+ AssertPtr(pNode);
+ RTListAppend(&pNode->LstStates, &pThis->NdStates);
+ }
+ rc = RTSemRWReleaseWrite(pTgtRec->hSemRwStates); AssertRC(rc);
+ pThis->fInRecSet = true;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pThis->fInRecSet)
+ rc = rtFuzzTgtRecScanStateForNewEdges(pTgtRec, pThis);
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzTgtStateAppendStdoutFromBuf(RTFUZZTGTSTATE hFuzzTgtState, const void *pvStdOut, size_t cbStdOut)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ RT_NOREF(pvStdOut, cbStdOut);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(int) RTFuzzTgtStateAppendStderrFromBuf(RTFUZZTGTSTATE hFuzzTgtState, const void *pvStdErr, size_t cbStdErr)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ RT_NOREF(pvStdErr, cbStdErr);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(int) RTFuzzTgtStateAppendStdoutFromPipe(RTFUZZTGTSTATE hFuzzTgtState, RTPIPE hPipe)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ return rtFuzzTgtStdOutErrBufFillFromPipe(&pThis->StdOutBuf, hPipe);
+}
+
+
+RTDECL(int) RTFuzzTgtStateAppendStderrFromPipe(RTFUZZTGTSTATE hFuzzTgtState, RTPIPE hPipe)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ return rtFuzzTgtStdOutErrBufFillFromPipe(&pThis->StdErrBuf, hPipe);
+}
+
+
+RTDECL(int) RTFuzzTgtStateAddSanCovReportFromFile(RTFUZZTGTSTATE hFuzzTgtState, const char *pszFilename)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ uint8_t *pbSanCov = NULL;
+ size_t cbSanCov = 0;
+ int rc = RTFileReadAll(pszFilename, (void **)&pbSanCov, &cbSanCov);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check for the magic identifying whether the offsets are 32bit or 64bit. */
+ if ( cbSanCov >= sizeof(uint64_t)
+ && ( *(uint64_t *)pbSanCov == SANCOV_MAGIC_64
+ || *(uint64_t *)pbSanCov == SANCOV_MAGIC_32))
+ {
+ uint32_t cbCovOff = sizeof(uint32_t);
+ if (*(uint64_t *)pbSanCov == SANCOV_MAGIC_64)
+ cbCovOff = sizeof(uint64_t);
+
+ uint32_t cbCovDet = ASMAtomicReadU32(&pThis->pTgtRec->cbCovOff);
+ if (!cbCovDet)
+ {
+ /* Set the detected offset width. */
+ if (!ASMAtomicCmpXchgU32(&pThis->pTgtRec->cbCovOff, cbCovOff, 0))
+ {
+ /* Someone raced us, check again. */
+ cbCovDet = ASMAtomicReadU32(&pThis->pTgtRec->cbCovOff);
+ Assert(cbCovDet != 0);
+ }
+ else
+ cbCovDet = cbCovOff;
+ }
+
+ if (cbCovDet == cbCovOff)
+ {
+ /*
+ * Just copy the offsets into the state for now. Now further analysis
+ * is happening right now, just checking whether the content changed for
+ * the states.to spot newly discovered edges.
+ */
+ pThis->cbCovReport = cbSanCov - sizeof(uint64_t);
+ pThis->pvCovReport = RTMemDup(pbSanCov + sizeof(uint64_t), pThis->cbCovReport);
+ if (!pThis->pvCovReport)
+ {
+ pThis->cbCovReport = 0;
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_INVALID_STATE; /* Mixing 32bit and 64bit offsets shouldn't happen, is not supported. */
+ }
+ else
+ rc = VERR_INVALID_STATE;
+ RTFileReadAllFree(pbSanCov, cbSanCov);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzTgtStateAddProcSts(RTFUZZTGTSTATE hFuzzTgtState, PCRTPROCSTATUS pProcSts)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pProcSts, VERR_INVALID_POINTER);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ pThis->ProcSts = *pProcSts;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFuzzTgtStateDumpToDir(RTFUZZTGTSTATE hFuzzTgtState, const char *pszDirPath)
+{
+ PRTFUZZTGTSTATEINT pThis = hFuzzTgtState;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszDirPath, VERR_INVALID_POINTER);
+ AssertReturn(!pThis->fFinalized, VERR_WRONG_ORDER);
+
+ int rc = VINF_SUCCESS;
+ char szPath[RTPATH_MAX];
+ if (pThis->StdOutBuf.cbBuf)
+ {
+ rc = RTPathJoin(szPath, sizeof(szPath), pszDirPath, "stdout"); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzTgtStateStdOutErrBufWriteToFile(&pThis->StdOutBuf, &szPath[0]);
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pThis->StdErrBuf.cbBuf)
+ {
+ rc = RTPathJoin(szPath, sizeof(szPath), pszDirPath, "stderr"); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzTgtStateStdOutErrBufWriteToFile(&pThis->StdErrBuf, &szPath[0]);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/fuzz/fuzz.cpp b/src/VBox/Runtime/common/fuzz/fuzz.cpp
new file mode 100644
index 00000000..db68ddc0
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzz.cpp
@@ -0,0 +1,2322 @@
+/* $Id: fuzz.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, core.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/critsect.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/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/vfs.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define RTFUZZCTX_MAGIC UINT32_C(0xdeadc0de) /** @todo */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to the internal fuzzer state. */
+typedef struct RTFUZZCTXINT *PRTFUZZCTXINT;
+/** Pointer to a fuzzed mutation. */
+typedef struct RTFUZZMUTATION *PRTFUZZMUTATION;
+/** Pointer to a fuzzed mutation pointer. */
+typedef PRTFUZZMUTATION *PPRTFUZZMUTATION;
+/** Pointer to a const mutation. */
+typedef const struct RTFUZZMUTATION *PCRTFUZZMUTATION;
+
+
+/**
+ * Mutator class.
+ */
+typedef enum RTFUZZMUTATORCLASS
+{
+ /** Invalid class, do not use. */
+ RTFUZZMUTATORCLASS_INVALID = 0,
+ /** Mutator operates on single bits. */
+ RTFUZZMUTATORCLASS_BITS,
+ /** Mutator operates on bytes (single or multiple). */
+ RTFUZZMUTATORCLASS_BYTES,
+ /** Mutator interpretes data as integers and operates on them. */
+ RTFUZZMUTATORCLASS_INTEGERS,
+ /** Mutator uses multiple mutations to create new mutations. */
+ RTFUZZMUTATORCLASS_MUTATORS,
+ /** 32bit hack. */
+ RTFUZZMUTATORCLASS_32BIT_HACK = 0x7fffffff
+} RTFUZZMUTATORCLASS;
+
+
+/**
+ * Mutator preparation callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param offStart Where the mutation should start.
+ * @param pMutationParent The parent mutation to start working from.
+ * @param ppMutation Where to store the created mutation on success.
+ */
+typedef DECLCALLBACKTYPE(int, FNRTFUZZCTXMUTATORPREP,(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation));
+/** Pointer to a mutator preparation callback. */
+typedef FNRTFUZZCTXMUTATORPREP *PFNRTFUZZCTXMUTATORPREP;
+
+
+/**
+ * Mutator execution callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to work on.
+ * @param pvMutation Mutation dependent data.
+ * @param pbBuf The buffer to work on.
+ * @param cbBuf Size of the remaining buffer.
+ */
+typedef DECLCALLBACKTYPE(int, FNRTFUZZCTXMUTATOREXEC,(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf));
+/** Pointer to a mutator execution callback. */
+typedef FNRTFUZZCTXMUTATOREXEC *PFNRTFUZZCTXMUTATOREXEC;
+
+
+/**
+ * Mutator export callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to work on.
+ * @param pvMutation Mutation dependent data.
+ * @param pfnExport The export callback.
+ * @param pvUser Opaque user data to pass to the export callback.
+ */
+typedef DECLCALLBACKTYPE(int, FNRTFUZZCTXMUTATOREXPORT,(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ PFNRTFUZZCTXEXPORT pfnExport, void *pvUser));
+/** Pointer to a mutator export callback. */
+typedef FNRTFUZZCTXMUTATOREXPORT *PFNRTFUZZCTXMUTATOREXPORT;
+
+
+/**
+ * Mutator import callback.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to work on.
+ * @param pvMutation Mutation dependent data.
+ * @param pfnExport The import callback.
+ * @param pvUser Opaque user data to pass to the import callback.
+ */
+typedef DECLCALLBACKTYPE(int, FNRTFUZZCTXMUTATORIMPORT,(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, void *pvMutation,
+ PFNRTFUZZCTXIMPORT pfnImport, void *pvUser));
+/** Pointer to a mutator import callback. */
+typedef FNRTFUZZCTXMUTATORIMPORT *PFNRTFUZZCTXMUTATORIMPORT;
+
+
+/**
+ * A fuzzing mutator descriptor.
+ */
+typedef struct RTFUZZMUTATOR
+{
+ /** Id of the mutator. */
+ const char *pszId;
+ /** Mutator description. */
+ const char *pszDesc;
+ /** Mutator index. */
+ uint32_t uMutator;
+ /** Mutator class. */
+ RTFUZZMUTATORCLASS enmClass;
+ /** Additional flags for the mutator, controlling the behavior. */
+ uint64_t fFlags;
+ /** The preparation callback. */
+ PFNRTFUZZCTXMUTATORPREP pfnPrep;
+ /** The execution callback. */
+ PFNRTFUZZCTXMUTATOREXEC pfnExec;
+ /** The export callback. */
+ PFNRTFUZZCTXMUTATOREXPORT pfnExport;
+ /** The import callback. */
+ PFNRTFUZZCTXMUTATORIMPORT pfnImport;
+} RTFUZZMUTATOR;
+/** Pointer to a fuzzing mutator descriptor. */
+typedef RTFUZZMUTATOR *PRTFUZZMUTATOR;
+/** Pointer to a const fuzzing mutator descriptor. */
+typedef const RTFUZZMUTATOR *PCRTFUZZMUTATOR;
+
+/** The special corpus mutator. */
+#define RTFUZZMUTATOR_ID_CORPUS UINT32_C(0xffffffff)
+
+/** Mutator always works from the end of the buffer (no starting offset generation). */
+#define RTFUZZMUTATOR_F_END_OF_BUF RT_BIT_64(0)
+/** Default flags. */
+#define RTFUZZMUTATOR_F_DEFAULT (0)
+
+
+/**
+ * A fuzzed mutation.
+ */
+typedef struct RTFUZZMUTATION
+{
+ /** The AVL tree core. */
+ AVLU64NODECORE Core;
+ /** The list node if the mutation has the mutated
+ * data allocated. */
+ RTLISTNODE NdAlloc;
+ /** Magic identifying this structure. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The fuzzer this mutation belongs to. */
+ PRTFUZZCTXINT pFuzzer;
+ /** Parent mutation (no reference is held), NULL means root or original data. */
+ PRTFUZZMUTATION pMutationParent;
+ /** Start offset where new mutations are allowed to start. */
+ uint64_t offMutStartNew;
+ /** Size of the range in bytes where mutations are allowed to happen. */
+ uint64_t cbMutNew;
+ /** Mutation level. */
+ uint32_t iLvl;
+ /** The mutator causing this mutation, NULL if original input data. */
+ PCRTFUZZMUTATOR pMutator;
+ /** Byte offset where the mutation starts. */
+ uint64_t offMutation;
+ /** Size of the generated input data in bytes after the mutation was applied. */
+ size_t cbInput;
+ /** Size of the mutation dependent data. */
+ size_t cbMutation;
+ /** Size allocated for the input. */
+ size_t cbAlloc;
+ /** Pointer to the input data if created. */
+ void *pvInput;
+ /** Flag whether the mutation is contained in the tree of the context. */
+ bool fInTree;
+ /** Flag whether the mutation input data is cached. */
+ bool fCached;
+ /** Mutation dependent data, variable in size. */
+ uint8_t abMutation[1];
+} RTFUZZMUTATION;
+
+
+/**
+ * A fuzzing input seed.
+ */
+typedef struct RTFUZZINPUTINT
+{
+ /** Magic identifying this structure. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The fuzzer this input belongs to. */
+ PRTFUZZCTXINT pFuzzer;
+ /** The top mutation to work from (reference held). */
+ PRTFUZZMUTATION pMutationTop;
+ /** Fuzzer context type dependent data. */
+ union
+ {
+ /** Blob data. */
+ struct
+ {
+ /** Pointer to the input data if created. */
+ void *pvInput;
+ } Blob;
+ /** Stream state. */
+ struct
+ {
+ /** Number of bytes seen so far. */
+ size_t cbSeen;
+ } Stream;
+ } u;
+} RTFUZZINPUTINT;
+/** Pointer to the internal input state. */
+typedef RTFUZZINPUTINT *PRTFUZZINPUTINT;
+/** Pointer to an internal input state pointer. */
+typedef PRTFUZZINPUTINT *PPRTFUZZINPUTINT;
+
+
+/**
+ * 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;
+ /** Fuzzing context type. */
+ RTFUZZCTXTYPE enmType;
+ /** Semaphore protecting the mutations tree. */
+ RTSEMRW hSemRwMutations;
+ /** The AVL tree for indexing the mutations (keyed by counter). */
+ AVLU64TREE TreeMutations;
+ /** Number of inputs currently in the tree. */
+ volatile uint64_t cMutations;
+ /** The maximum size of one input seed to generate. */
+ size_t cbInputMax;
+ /** Behavioral flags. */
+ uint32_t fFlagsBehavioral;
+ /** Number of enabled mutators. */
+ uint32_t cMutators;
+ /** Pointer to the mutator descriptors. */
+ PRTFUZZMUTATOR paMutators;
+ /** Maximum amount of bytes of mutated inputs to cache. */
+ size_t cbMutationsAllocMax;
+ /** Current amount of bytes of cached mutated inputs. */
+ size_t cbMutationsAlloc;
+ /** List of mutators having data allocated currently. */
+ RTLISTANCHOR LstMutationsAlloc;
+ /** Critical section protecting the allocation list. */
+ RTCRITSECT CritSectAlloc;
+ /** Total number of bytes of memory currently allocated in total for this context. */
+ volatile size_t cbMemTotal;
+ /** Start offset in the input where a mutation is allowed to happen. */
+ uint64_t offMutStart;
+ /** size of the range where a mutation can happen. */
+ uint64_t cbMutRange;
+} RTFUZZCTXINT;
+
+
+/**
+ * The fuzzer state to be exported - all members are stored in little endian form.
+ */
+typedef struct RTFUZZCTXSTATE
+{
+ /** Magic value for identification. */
+ uint32_t u32Magic;
+ /** Context type. */
+ uint32_t uCtxType;
+ /** Size of the PRNG state following in bytes. */
+ uint32_t cbPrng;
+ /** Number of mutator descriptors following. */
+ uint32_t cMutators;
+ /** Number of mutation descriptors following. */
+ uint32_t cMutations;
+ /** Behavioral flags. */
+ uint32_t fFlagsBehavioral;
+ /** Maximum input size to generate. */
+ uint64_t cbInputMax;
+} RTFUZZCTXSTATE;
+/** Pointer to a fuzzing context state. */
+typedef RTFUZZCTXSTATE *PRTFUZZCTXSTATE;
+
+/** BLOB context type. */
+#define RTFUZZCTX_STATE_TYPE_BLOB UINT32_C(0)
+/** Stream context type. */
+#define RTFUZZCTX_STATE_TYPE_STREAM UINT32_C(1)
+
+
+/**
+ * The fuzzer mutation state to be exported - all members are stored in little endian form.
+ */
+typedef struct RTFUZZMUTATIONSTATE
+{
+ /** The mutation identifier. */
+ uint64_t u64Id;
+ /** The mutation identifier of the parent, 0 for no parent. */
+ uint64_t u64IdParent;
+ /** The byte offset where the mutation starts. */
+ uint64_t u64OffMutation;
+ /** Size of input data after mutation was applied. */
+ uint64_t cbInput;
+ /** Size of mutation dependent data following. */
+ uint64_t cbMutation;
+ /** The mutator ID. */
+ uint32_t u32IdMutator;
+ /** The mutation level. */
+ uint32_t iLvl;
+ /** Magic value for identification. */
+ uint32_t u32Magic;
+} RTFUZZMUTATIONSTATE;
+
+
+/**
+ * Fuzzing context memory header.
+ */
+typedef struct RTFUZZMEMHDR
+{
+ /** Size of the memory area following. */
+ size_t cb;
+#if HC_ARCH_BITS == 32
+ /** Some padding. */
+ uint32_t uPadding0;
+#elif HC_ARCH_BITS == 64
+ /** Some padding. */
+ uint64_t uPadding0;
+#else
+# error "Port me"
+#endif
+} RTFUZZMEMHDR;
+/** Pointer to a memory header. */
+typedef RTFUZZMEMHDR *PRTFUZZMEMHDR;
+
+
+/**
+ * Fuzzing context export AVL arguments.
+ */
+typedef struct RTFUZZEXPORTARGS
+{
+ /** Pointer to the export callback. */
+ PFNRTFUZZCTXEXPORT pfnExport;
+ /** Opaque user data to pass to the callback. */
+ void *pvUser;
+} RTFUZZEXPORTARGS;
+/** Pointer to the export arguments. */
+typedef RTFUZZEXPORTARGS *PRTFUZZEXPORTARGS;
+/** Pointer to the constant export arguments. */
+typedef const RTFUZZEXPORTARGS *PCRTFUZZEXPORTARGS;
+
+
+/**
+ * Integer replacing mutator additional data.
+ */
+typedef struct RTFUZZMUTATORINTEGER
+{
+ /** The integer class. */
+ uint8_t uIntClass;
+ /** Flag whether to do a byte swap. */
+ bool fByteSwap;
+ /** The index into the class specific array. */
+ uint16_t idxInt;
+} RTFUZZMUTATORINTEGER;
+/** Pointer to additional integer replacing mutator data. */
+typedef RTFUZZMUTATORINTEGER *PRTFUZZMUTATORINTEGER;
+/** Pointer to constant additional integer replacing mutator data. */
+typedef const RTFUZZMUTATORINTEGER *PCRTFUZZMUTATORINTEGER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlipPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplacePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteInsertPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsertAppendPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDeletePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDeletePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorIntegerReplacePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation);
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCorpusExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlipExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplaceExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteInsertExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsertAppendExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDeleteExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDeleteExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorIntegerReplaceExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf);
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorExportDefault(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ PFNRTFUZZCTXEXPORT pfnExport, void *pvUser);
+static DECLCALLBACK(int) rtFuzzCtxMutatorImportDefault(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, void *pvMutation,
+ PFNRTFUZZCTXIMPORT pfnImport, void *pvUser);
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverExport(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ PFNRTFUZZCTXEXPORT pfnExport, void *pvUser);
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverImport(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, void *pvMutation,
+ PFNRTFUZZCTXIMPORT pfnImport, void *pvUser);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+
+/** Signed 8bit interesting values. */
+static int8_t s_ai8Interesting[] = { INT8_MIN, INT8_MIN + 1, -1, 0, 1, INT8_MAX - 1, INT8_MAX };
+/** Unsigned 8bit interesting values. */
+static uint8_t s_au8Interesting[] = { 0, 1, UINT8_MAX - 1, UINT8_MAX };
+/** Signed 16bit interesting values. */
+static int16_t s_ai16Interesting[] = { INT16_MIN, INT16_MIN + 1, -1, 0, 1, INT16_MAX - 1, INT16_MAX };
+/** Unsigned 16bit interesting values. */
+static uint16_t s_au16Interesting[] = { 0, 1, UINT16_MAX - 1, UINT16_MAX };
+/** Signed 32bit interesting values. */
+static int32_t s_ai32Interesting[] = { INT32_MIN, INT32_MIN + 1, -1, 0, 1, INT32_MAX - 1, INT32_MAX };
+/** Unsigned 32bit interesting values. */
+static uint32_t s_au32Interesting[] = { 0, 1, UINT32_MAX - 1, UINT32_MAX };
+/** Signed 64bit interesting values. */
+static int64_t s_ai64Interesting[] = { INT64_MIN, INT64_MIN + 1, -1, 0, 1, INT64_MAX - 1, INT64_MAX };
+/** Unsigned 64bit interesting values. */
+static uint64_t s_au64Interesting[] = { 0, 1, UINT64_MAX - 1, UINT64_MAX };
+
+
+/**
+ * The special corpus mutator for the original data.
+ */
+static RTFUZZMUTATOR const g_MutatorCorpus =
+{
+ /** pszId */
+ "Corpus",
+ /** pszDesc */
+ "Special mutator, which is assigned to the initial corpus",
+ /** uMutator. */
+ RTFUZZMUTATOR_ID_CORPUS,
+ /** enmClass. */
+ RTFUZZMUTATORCLASS_BYTES,
+ /** fFlags */
+ RTFUZZMUTATOR_F_DEFAULT,
+ /** pfnPrep */
+ NULL,
+ /** pfnExec */
+ rtFuzzCtxMutatorCorpusExec,
+ /** pfnExport */
+ rtFuzzCtxMutatorExportDefault,
+ /** pfnImport */
+ rtFuzzCtxMutatorImportDefault
+};
+
+/**
+ * Array of all available mutators.
+ */
+static RTFUZZMUTATOR const g_aMutators[] =
+{
+ /* pszId pszDesc uMutator enmClass fFlags pfnPrep pfnExec pfnExport pfnImport */
+ { "BitFlip", "Flips a single bit in the input", 0, RTFUZZMUTATORCLASS_BITS, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorBitFlipPrep, rtFuzzCtxMutatorBitFlipExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "ByteReplace", "Replaces a single byte in the input", 1, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorByteReplacePrep, rtFuzzCtxMutatorByteReplaceExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "ByteInsert", "Inserts a single byte sequence into the input", 2, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorByteInsertPrep, rtFuzzCtxMutatorByteInsertExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "ByteSeqIns", "Inserts a byte sequence in the input", 3, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorByteSequenceInsertAppendPrep, rtFuzzCtxMutatorByteSequenceInsertAppendExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "ByteSeqApp", "Appends a byte sequence to the input", 4, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_END_OF_BUF, rtFuzzCtxMutatorByteSequenceInsertAppendPrep, rtFuzzCtxMutatorByteSequenceInsertAppendExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "ByteDelete", "Deletes a single byte sequence from the input", 5, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorByteDeletePrep, rtFuzzCtxMutatorByteDeleteExec, NULL, NULL },
+ { "ByteSeqDel", "Deletes a byte sequence from the input", 6, RTFUZZMUTATORCLASS_BYTES, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorByteSequenceDeletePrep, rtFuzzCtxMutatorByteSequenceDeleteExec, NULL, NULL },
+ { "IntReplace", "Replaces a possible integer with an interesting one", 7, RTFUZZMUTATORCLASS_INTEGERS, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorIntegerReplacePrep, rtFuzzCtxMutatorIntegerReplaceExec, rtFuzzCtxMutatorExportDefault, rtFuzzCtxMutatorImportDefault },
+ { "MutCrossover", "Creates a crossover of two other mutations", 8, RTFUZZMUTATORCLASS_MUTATORS, RTFUZZMUTATOR_F_DEFAULT, rtFuzzCtxMutatorCrossoverPrep, rtFuzzCtxMutatorCrossoverExec, rtFuzzCtxMutatorCrossoverExport, rtFuzzCtxMutatorCrossoverImport }
+};
+
+
+/**
+ * Allocates the given number of bytes.
+ *
+ * @returns Pointer to the allocated memory
+ * @param pThis The fuzzer context instance.
+ * @param cb How much to allocate.
+ */
+static void *rtFuzzCtxMemoryAlloc(PRTFUZZCTXINT pThis, size_t cb)
+{
+ AssertReturn(cb > 0, NULL);
+
+ PRTFUZZMEMHDR pMemHdr = (PRTFUZZMEMHDR)RTMemAllocZ(cb + sizeof(RTFUZZMEMHDR));
+ if (RT_LIKELY(pMemHdr))
+ {
+ pMemHdr->cb = cb;
+ size_t cbIgn = ASMAtomicAddZ(&pThis->cbMemTotal, cb + sizeof(RTFUZZMEMHDR)); RT_NOREF(cbIgn);
+ return pMemHdr + 1;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Frees the given memory.
+ *
+ * @param pThis The fuzzer context instance.
+ * @param pv Pointer to the memory area to free.
+ */
+static void rtFuzzCtxMemoryFree(PRTFUZZCTXINT pThis, void *pv)
+{
+ AssertReturnVoid(pv != NULL);
+ PRTFUZZMEMHDR pMemHdr = ((PRTFUZZMEMHDR)pv) - 1;
+
+ size_t cbIgn = ASMAtomicSubZ(&pThis->cbMemTotal, pMemHdr->cb + sizeof(RTFUZZMEMHDR)); RT_NOREF(cbIgn);
+ RTMemFree(pMemHdr);
+}
+
+
+/**
+ * Frees the cached inputs until the given amount is free.
+ *
+ * @returns Whether the amount of memory is free.
+ * @param pThis The fuzzer context instance.
+ * @param cb How many bytes to reclaim
+ */
+static bool rtFuzzCtxMutationAllocReclaim(PRTFUZZCTXINT pThis, size_t cb)
+{
+ while ( !RTListIsEmpty(&pThis->LstMutationsAlloc)
+ && pThis->cbMutationsAlloc + cb > pThis->cbMutationsAllocMax)
+ {
+ PRTFUZZMUTATION pMutation = RTListGetLast(&pThis->LstMutationsAlloc, RTFUZZMUTATION, NdAlloc);
+ AssertPtr(pMutation);
+ AssertPtr(pMutation->pvInput);
+
+ rtFuzzCtxMemoryFree(pThis, pMutation->pvInput);
+ pThis->cbMutationsAlloc -= pMutation->cbAlloc;
+ pMutation->pvInput = NULL;
+ pMutation->cbAlloc = 0;
+ pMutation->fCached = false;
+ RTListNodeRemove(&pMutation->NdAlloc);
+ }
+
+ return pThis->cbMutationsAlloc + cb <= pThis->cbMutationsAllocMax;
+}
+
+
+/**
+ * Updates the cache status of the given mutation.
+ *
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to update.
+ */
+static void rtFuzzCtxMutationMaybeEnterCache(PRTFUZZCTXINT pThis, PRTFUZZMUTATION pMutation)
+{
+ RTCritSectEnter(&pThis->CritSectAlloc);
+
+ /* Initial corpus mutations are not freed. */
+ if ( pMutation->pvInput
+ && pMutation->pMutator != &g_MutatorCorpus)
+ {
+ Assert(!pMutation->fCached);
+
+ if (rtFuzzCtxMutationAllocReclaim(pThis, pMutation->cbAlloc))
+ {
+ RTListPrepend(&pThis->LstMutationsAlloc, &pMutation->NdAlloc);
+ pThis->cbMutationsAlloc += pMutation->cbAlloc;
+ pMutation->fCached = true;
+ }
+ else
+ {
+ rtFuzzCtxMemoryFree(pThis, pMutation->pvInput);
+ pMutation->pvInput = NULL;
+ pMutation->cbAlloc = 0;
+ pMutation->fCached = false;
+ }
+ }
+ RTCritSectLeave(&pThis->CritSectAlloc);
+}
+
+
+/**
+ * Removes a cached mutation from the cache.
+ *
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to remove.
+ */
+static void rtFuzzCtxMutationCacheRemove(PRTFUZZCTXINT pThis, PRTFUZZMUTATION pMutation)
+{
+ RTCritSectEnter(&pThis->CritSectAlloc);
+ if (pMutation->fCached)
+ {
+ RTListNodeRemove(&pMutation->NdAlloc);
+ pThis->cbMutationsAlloc -= pMutation->cbAlloc;
+ pMutation->fCached = false;
+ }
+ RTCritSectLeave(&pThis->CritSectAlloc);
+}
+
+
+/**
+ * Destroys the given mutation.
+ *
+ * @param pMutation The mutation to destroy.
+ */
+static void rtFuzzMutationDestroy(PRTFUZZMUTATION pMutation)
+{
+ if (pMutation->pvInput)
+ {
+ rtFuzzCtxMemoryFree(pMutation->pFuzzer, pMutation->pvInput);
+ if (pMutation->fCached)
+ {
+ RTCritSectEnter(&pMutation->pFuzzer->CritSectAlloc);
+ RTListNodeRemove(&pMutation->NdAlloc);
+ pMutation->pFuzzer->cbMutationsAlloc -= pMutation->cbAlloc;
+ RTCritSectLeave(&pMutation->pFuzzer->CritSectAlloc);
+ }
+ pMutation->pvInput = NULL;
+ pMutation->cbAlloc = 0;
+ pMutation->fCached = false;
+ }
+ rtFuzzCtxMemoryFree(pMutation->pFuzzer, pMutation);
+}
+
+
+/**
+ * Retains an external reference to the given mutation.
+ *
+ * @returns New reference count on success.
+ * @param pMutation The mutation to retain.
+ */
+static uint32_t rtFuzzMutationRetain(PRTFUZZMUTATION pMutation)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pMutation->cRefs);
+ AssertMsg( ( cRefs > 1
+ || pMutation->fInTree)
+ && cRefs < _1M, ("%#x %p\n", cRefs, pMutation));
+
+ if (cRefs == 1)
+ rtFuzzCtxMutationCacheRemove(pMutation->pFuzzer, pMutation);
+ return cRefs;
+}
+
+
+/**
+ * Releases an external reference from the given mutation.
+ *
+ * @returns New reference count on success.
+ * @param pMutation The mutation to retain.
+ */
+static uint32_t rtFuzzMutationRelease(PRTFUZZMUTATION pMutation)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pMutation->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pMutation));
+
+ if (cRefs == 0)
+ {
+ if (!pMutation->fInTree)
+ rtFuzzMutationDestroy(pMutation);
+ else
+ rtFuzzCtxMutationMaybeEnterCache(pMutation->pFuzzer, pMutation);
+ }
+
+ return cRefs;
+}
+
+
+/**
+ * Adds the given mutation to the corpus of the given fuzzer context.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzer context instance.
+ * @param pMutation The mutation to add.
+ */
+static int rtFuzzCtxMutationAdd(PRTFUZZCTXINT pThis, PRTFUZZMUTATION pMutation)
+{
+ int rc = VINF_SUCCESS;
+
+ pMutation->Core.Key = ASMAtomicIncU64(&pThis->cMutations);
+ rc = RTSemRWRequestWrite(pThis->hSemRwMutations, RT_INDEFINITE_WAIT);
+ AssertRC(rc); RT_NOREF(rc);
+ bool fIns = RTAvlU64Insert(&pThis->TreeMutations, &pMutation->Core);
+ Assert(fIns); RT_NOREF(fIns);
+ rc = RTSemRWReleaseWrite(pThis->hSemRwMutations);
+ AssertRC(rc); RT_NOREF(rc);
+
+ pMutation->fInTree = true;
+ return rc;
+}
+
+
+/**
+ * Locates the mutation with the given key.
+ *
+ * @returns Pointer to the mutation if found or NULL otherwise.
+ * @param pThis The fuzzer context instance.
+ * @param uKey The key to locate.
+ */
+static PRTFUZZMUTATION rtFuzzCtxMutationLocate(PRTFUZZCTXINT pThis, uint64_t uKey)
+{
+ int rc = RTSemRWRequestRead(pThis->hSemRwMutations, RT_INDEFINITE_WAIT);
+ AssertRC(rc); RT_NOREF(rc);
+
+ /*
+ * Using best fit getter here as there might be a racing mutation insertion and the mutation counter has increased
+ * already but the mutation is not yet in the tree.
+ */
+ PRTFUZZMUTATION pMutation = (PRTFUZZMUTATION)RTAvlU64GetBestFit(&pThis->TreeMutations, uKey, false /*fAbove*/);
+ if (RT_LIKELY(pMutation))
+ rtFuzzMutationRetain(pMutation);
+
+ rc = RTSemRWReleaseRead(pThis->hSemRwMutations);
+ AssertRC(rc); RT_NOREF(rc);
+
+ return pMutation;
+}
+
+
+/**
+ * Returns a random mutation from the corpus of the given fuzzer context.
+ *
+ * @returns Pointer to a randomly picked mutation (reference count is increased).
+ * @param pThis The fuzzer context instance.
+ */
+static PRTFUZZMUTATION rtFuzzCtxMutationPickRnd(PRTFUZZCTXINT pThis)
+{
+ uint64_t idxMutation = RTRandAdvU64Ex(pThis->hRand, 1, ASMAtomicReadU64(&pThis->cMutations));
+ return rtFuzzCtxMutationLocate(pThis, idxMutation);
+}
+
+
+/**
+ * Creates a new mutation capable of holding the additional number of bytes - extended version.
+ *
+ * @returns Pointer to the newly created mutation or NULL if out of memory.
+ * @param pThis The fuzzer context instance.
+ * @param offMutation The starting offset for the mutation.
+ * @param pMutationParent The parent mutation, can be NULL.
+ * @param offMuStartNew Offset where descendants of the created mutation can start to mutate.
+ * @param cbMutNew Range in bytes where descendants of the created mutation can mutate.c
+ * @param cbAdditional Additional number of bytes to allocate after the core structure.
+ * @param ppvMutation Where to store the pointer to the mutation dependent data on success.
+ */
+static PRTFUZZMUTATION rtFuzzMutationCreateEx(PRTFUZZCTXINT pThis, uint64_t offMutation, PRTFUZZMUTATION pMutationParent,
+ uint64_t offMutStartNew, uint64_t cbMutNew, size_t cbAdditional, void **ppvMutation)
+{
+ PRTFUZZMUTATION pMutation = (PRTFUZZMUTATION)rtFuzzCtxMemoryAlloc(pThis, sizeof(RTFUZZMUTATION) + cbAdditional);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->u32Magic = 0; /** @todo */
+ pMutation->pFuzzer = pThis;
+ pMutation->cRefs = 1;
+ pMutation->iLvl = 0;
+ pMutation->offMutation = offMutation;
+ pMutation->pMutationParent = pMutationParent;
+ pMutation->offMutStartNew = offMutStartNew;
+ pMutation->cbMutNew = cbMutNew;
+ pMutation->cbMutation = cbAdditional;
+ pMutation->fInTree = false;
+ pMutation->fCached = false;
+ pMutation->pvInput = NULL;
+ pMutation->cbInput = 0;
+ pMutation->cbAlloc = 0;
+
+ if (pMutationParent)
+ pMutation->iLvl = pMutationParent->iLvl + 1;
+ if (ppvMutation)
+ *ppvMutation = &pMutation->abMutation[0];
+ }
+
+ return pMutation;
+}
+
+
+/**
+ * Creates a new mutation capable of holding the additional number of bytes.
+ *
+ * @returns Pointer to the newly created mutation or NULL if out of memory.
+ * @param pThis The fuzzer context instance.
+ * @param offMutation The starting offset for the mutation.
+ * @param pMutationParent The parent mutation, can be NULL.
+ * @param cbAdditional Additional number of bytes to allocate after the core structure.
+ * @param ppvMutation Where to store the pointer to the mutation dependent data on success.
+ */
+DECLINLINE(PRTFUZZMUTATION) rtFuzzMutationCreate(PRTFUZZCTXINT pThis, uint64_t offMutation, PRTFUZZMUTATION pMutationParent,
+ size_t cbAdditional, void **ppvMutation)
+{
+ uint64_t offMutNew = pMutationParent ? pMutationParent->offMutStartNew : pThis->offMutStart;
+ uint64_t cbMutNew = pMutationParent ? pMutationParent->cbMutNew : pThis->cbMutRange;
+
+ return rtFuzzMutationCreateEx(pThis, offMutation, pMutationParent, offMutNew, cbMutNew, cbAdditional, ppvMutation);
+}
+
+
+/**
+ * Destroys the given fuzzer context freeing all allocated resources.
+ *
+ * @param pThis The fuzzer context instance.
+ */
+static void rtFuzzCtxDestroy(PRTFUZZCTXINT pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/**
+ * Creates the final input data applying all accumulated mutations.
+ *
+ * @returns IPRT status code.
+ * @param pMutation The mutation to finalize.
+ */
+static int rtFuzzMutationDataFinalize(PRTFUZZMUTATION pMutation)
+{
+ if (pMutation->pvInput)
+ return VINF_SUCCESS;
+
+ /* Traverse the mutations top to bottom and insert into the array. */
+ int rc = VINF_SUCCESS;
+ uint32_t idx = pMutation->iLvl + 1;
+ PRTFUZZMUTATION *papMutations = (PRTFUZZMUTATION *)RTMemTmpAlloc(idx * sizeof(PCRTFUZZMUTATION));
+ if (RT_LIKELY(papMutations))
+ {
+ PRTFUZZMUTATION pMutationCur = pMutation;
+ size_t cbAlloc = 0;
+
+ /*
+ * As soon as a mutation with allocated input data is encountered the insertion is
+ * stopped as it contains all necessary mutated inputs we can start from.
+ */
+ while (idx > 0)
+ {
+ rtFuzzMutationRetain(pMutationCur);
+ papMutations[idx - 1] = pMutationCur;
+ cbAlloc = RT_MAX(cbAlloc, pMutationCur->cbInput);
+ if (pMutationCur->pvInput)
+ {
+ idx--;
+ break;
+ }
+ pMutationCur = pMutationCur->pMutationParent;
+ idx--;
+ }
+
+ pMutation->cbAlloc = cbAlloc;
+ uint8_t *pbBuf = (uint8_t *)rtFuzzCtxMemoryAlloc(pMutation->pFuzzer, cbAlloc);
+ if (RT_LIKELY(pbBuf))
+ {
+ pMutation->pvInput = pbBuf;
+
+ /* Copy the initial input data. */
+ size_t cbInputNow = papMutations[idx]->cbInput;
+ memcpy(pbBuf, papMutations[idx]->pvInput, cbInputNow);
+ rtFuzzMutationRelease(papMutations[idx]);
+
+ for (uint32_t i = idx + 1; i < pMutation->iLvl + 1; i++)
+ {
+ PRTFUZZMUTATION pCur = papMutations[i];
+ pCur->pMutator->pfnExec(pCur->pFuzzer, pCur, (void *)&pCur->abMutation[0],
+ pbBuf + pCur->offMutation,
+ cbInputNow - pCur->offMutation);
+
+ cbInputNow = pCur->cbInput;
+ rtFuzzMutationRelease(pCur);
+ }
+
+ Assert(cbInputNow == pMutation->cbInput);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ RTMemTmpFree(papMutations);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Default mutator export callback (just writing the raw data).
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorExportDefault(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ PFNRTFUZZCTXEXPORT pfnExport, void *pvUser)
+{
+ return pfnExport(pThis, pvMutation, pMutation->cbMutation, pvUser);
+}
+
+
+/**
+ * Default mutator import callback (just reading the raw data).
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorImportDefault(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, void *pvMutation,
+ PFNRTFUZZCTXIMPORT pfnImport, void *pvUser)
+{
+ return pfnImport(pThis, pvMutation, pMutation->cbMutation, NULL, pvUser);
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCorpusExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, cbBuf, pvMutation);
+ memcpy(pbBuf, pvMutation, pMutation->cbInput);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - flips a single bit in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlipPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t *pidxBitFlip = 0;
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, sizeof(*pidxBitFlip), (void **)&pidxBitFlip);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = pMutationParent->cbInput; /* Bit flips don't change the input size. */
+ *pidxBitFlip = (uint8_t)RTRandAdvU32Ex(pThis->hRand, 0, sizeof(uint8_t) * 8 - 1);
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorBitFlipExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, cbBuf, pMutation);
+ uint8_t idxBitFlip = *(uint8_t *)pvMutation;
+ ASMBitToggle(pbBuf, idxBitFlip);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - replaces a single byte in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplacePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t *pbReplace = 0;
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, sizeof(*pbReplace), (void **)&pbReplace);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = pMutationParent->cbInput; /* Byte replacements don't change the input size. */
+ RTRandAdvBytes(pThis->hRand, pbReplace, 1); /** @todo Filter out same values. */
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteReplaceExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, cbBuf, pMutation);
+ uint8_t bReplace = *(uint8_t *)pvMutation;
+ *pbBuf = bReplace;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - inserts a single byte into the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteInsertPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t *pbInsert = 0;
+ if (pMutationParent->cbInput < pThis->cbInputMax)
+ {
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, 1 /*cbAdditional*/, (void **)&pbInsert);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = pMutationParent->cbInput + 1;
+ RTRandAdvBytes(pThis->hRand, pbInsert, 1);
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteInsertExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, pMutation, pvMutation);
+
+ /* Just move the residual data one byte to the back. */
+ memmove(pbBuf + 1, pbBuf, cbBuf);
+ *pbBuf = *(uint8_t *)pvMutation;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - inserts a byte sequence into the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsertAppendPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ if (pMutationParent->cbInput < pThis->cbInputMax)
+ {
+ size_t cbInputMutated = (size_t)RTRandAdvU64Ex(pThis->hRand, pMutationParent->cbInput + 1, pThis->cbInputMax);
+ size_t cbInsert = cbInputMutated - pMutationParent->cbInput;
+ uint8_t *pbAdd = NULL;
+
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, cbInsert, (void **)&pbAdd);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = cbInputMutated;
+ RTRandAdvBytes(pThis->hRand, pbAdd, cbInsert);
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceInsertAppendExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis);
+ size_t cbInsert = pMutation->cbInput - pMutation->pMutationParent->cbInput;
+
+ /* Move any remaining data to the end. */
+ if (cbBuf)
+ memmove(pbBuf + cbInsert, pbBuf, cbBuf);
+
+ memcpy(pbBuf, pvMutation, cbInsert);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - deletes a single byte in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDeletePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ if (pMutationParent->cbInput - offStart >= 1)
+ {
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, 0 /*cbAdditional*/, NULL);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = pMutationParent->cbInput - 1;
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteDeleteExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, pMutation, pvMutation);
+
+ /* Just move the residual data to the front. */
+ memmove(pbBuf, pbBuf + 1, cbBuf - 1);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - deletes a byte sequence in the input.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDeletePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ if ( pMutationParent->cbInput > offStart
+ && pMutationParent->cbInput > 1)
+ {
+ size_t cbInputMutated = (size_t)RTRandAdvU64Ex(pThis->hRand, offStart, pMutationParent->cbInput - 1);
+
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, 0 /*cbAdditional*/, NULL);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->cbInput = cbInputMutated;
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorByteSequenceDeleteExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, pvMutation);
+ Assert(pMutation->pMutationParent->cbInput > pMutation->cbInput);
+ size_t cbDel = pMutation->pMutationParent->cbInput - pMutation->cbInput;
+
+ /* Just move the residual data to the front. */
+ memmove(pbBuf, pbBuf + cbDel, cbBuf - cbDel);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - replaces a possible integer with something interesting.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorIntegerReplacePrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+ PRTFUZZMUTATORINTEGER pMutInt = NULL;
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, sizeof(*pMutInt), (void **)&pMutInt);
+ if (RT_LIKELY(pMutation))
+ {
+ size_t cbLeft = pMutationParent->cbInput - offStart;
+ uint32_t uClassMax = 0;
+
+ switch (cbLeft)
+ {
+ case 1:
+ uClassMax = 1;
+ break;
+ case 2:
+ case 3:
+ uClassMax = 3;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ uClassMax = 5;
+ break;
+ default:
+ uClassMax = 7;
+ break;
+ }
+
+ pMutInt->uIntClass = (uint8_t)RTRandAdvU32Ex(pThis->hRand, 0, uClassMax);
+ pMutInt->fByteSwap = RT_BOOL(RTRandAdvU32Ex(pThis->hRand, 0, 1));
+
+ switch (pMutInt->uIntClass)
+ {
+ case 0:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_ai8Interesting) - 1);
+ break;
+ case 1:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_au8Interesting) - 1);
+ break;
+ case 2:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_ai16Interesting) - 1);
+ break;
+ case 3:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_au16Interesting) - 1);
+ break;
+ case 4:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_ai32Interesting) - 1);
+ break;
+ case 5:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_au32Interesting) - 1);
+ break;
+ case 6:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_ai64Interesting) - 1);
+ break;
+ case 7:
+ pMutInt->idxInt = (uint16_t)RTRandAdvU32Ex(pThis->hRand, 0, RT_ELEMENTS(s_au64Interesting) - 1);
+ break;
+ default:
+ AssertReleaseFailed();
+ }
+
+ pMutation->cbInput = pMutationParent->cbInput;
+ *ppMutation = pMutation;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorIntegerReplaceExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(pThis, pMutation, cbBuf);
+ union
+ {
+ int8_t i8;
+ uint8_t u8;
+ int16_t i16;
+ uint16_t u16;
+ int32_t i32;
+ uint32_t u32;
+ int64_t i64;
+ uint64_t u64;
+ } Int;
+ PCRTFUZZMUTATORINTEGER pMutInt = (PCRTFUZZMUTATORINTEGER)pvMutation;
+ size_t cb = 0;
+
+ switch (pMutInt->uIntClass)
+ {
+ case 0:
+ Int.i8 = s_ai8Interesting[pMutInt->idxInt];
+ cb = 1;
+ break;
+ case 1:
+ Int.u8 = s_au8Interesting[pMutInt->idxInt];
+ cb = 1;
+ break;
+ case 2:
+ Int.i16 = s_ai16Interesting[pMutInt->idxInt];
+ cb = 2;
+ if (pMutInt->fByteSwap)
+ Int.u16 = RT_BSWAP_U16(Int.u16);
+ break;
+ case 3:
+ Int.u16 = s_au16Interesting[pMutInt->idxInt];
+ cb = 2;
+ if (pMutInt->fByteSwap)
+ Int.u16 = RT_BSWAP_U16(Int.u16);
+ break;
+ case 4:
+ Int.i32 = s_ai32Interesting[pMutInt->idxInt];
+ cb = 4;
+ if (pMutInt->fByteSwap)
+ Int.u32 = RT_BSWAP_U32(Int.u32);
+ break;
+ case 5:
+ Int.u32 = s_au32Interesting[pMutInt->idxInt];
+ cb = 4;
+ if (pMutInt->fByteSwap)
+ Int.u32 = RT_BSWAP_U32(Int.u32);
+ break;
+ case 6:
+ Int.i64 = s_ai64Interesting[pMutInt->idxInt];
+ cb = 8;
+ if (pMutInt->fByteSwap)
+ Int.u64 = RT_BSWAP_U64(Int.u64);
+ break;
+ case 7:
+ Int.u64 = s_au64Interesting[pMutInt->idxInt];
+ cb = 8;
+ if (pMutInt->fByteSwap)
+ Int.u64 = RT_BSWAP_U64(Int.u64);
+ break;
+ default:
+ AssertReleaseFailed();
+ }
+
+ memcpy(pbBuf, &Int, cb);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Mutator callback - crosses over two mutations at the given point.
+ */
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverPrep(PRTFUZZCTXINT pThis, uint64_t offStart, PRTFUZZMUTATION pMutationParent,
+ PPRTFUZZMUTATION ppMutation)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pThis->cMutations > 1)
+ {
+ uint64_t *pidxMutCrossover = NULL;
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreate(pThis, offStart, pMutationParent, sizeof(*pidxMutCrossover), (void **)&pidxMutCrossover);
+ if (RT_LIKELY(pMutation))
+ {
+ uint32_t cTries = 10;
+ PRTFUZZMUTATION pMutCrossover = NULL;
+ /*
+ * Pick a random mutation to crossover with (making sure it is not the current one
+ * or the crossover point is beyond the end of input).
+ */
+ do
+ {
+ if (pMutCrossover)
+ rtFuzzMutationRelease(pMutCrossover);
+ pMutCrossover = rtFuzzCtxMutationPickRnd(pThis);
+ cTries--;
+ } while ( ( pMutCrossover == pMutationParent
+ || offStart >= pMutCrossover->cbInput)
+ && cTries > 0);
+
+ if (cTries)
+ {
+ pMutation->cbInput = pMutCrossover->cbInput;
+ *pidxMutCrossover = pMutCrossover->Core.Key;
+ *ppMutation = pMutation;
+ }
+ else
+ rtFuzzMutationDestroy(pMutation);
+
+ rtFuzzMutationRelease(pMutCrossover);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverExec(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ uint8_t *pbBuf, size_t cbBuf)
+{
+ RT_NOREF(cbBuf);
+ uint64_t idxMutCrossover = *(uint64_t *)pvMutation;
+
+ PRTFUZZMUTATION pMutCrossover = rtFuzzCtxMutationLocate(pThis, idxMutCrossover);
+ int rc = rtFuzzMutationDataFinalize(pMutCrossover);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pbBuf, (uint8_t *)pMutCrossover->pvInput + pMutation->offMutation,
+ pMutCrossover->cbInput - pMutation->offMutation);
+ rtFuzzMutationRelease(pMutCrossover);
+ }
+
+ return rc;
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverExport(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, const void *pvMutation,
+ PFNRTFUZZCTXEXPORT pfnExport, void *pvUser)
+{
+ RT_NOREF(pMutation);
+
+ uint64_t idxMutCrossover = *(uint64_t *)pvMutation;
+ idxMutCrossover = RT_H2LE_U64(idxMutCrossover);
+ return pfnExport(pThis, &idxMutCrossover, sizeof(idxMutCrossover), pvUser);
+}
+
+
+static DECLCALLBACK(int) rtFuzzCtxMutatorCrossoverImport(PRTFUZZCTXINT pThis, PCRTFUZZMUTATION pMutation, void *pvMutation,
+ PFNRTFUZZCTXIMPORT pfnImport, void *pvUser)
+{
+ RT_NOREF(pMutation);
+
+ uint64_t uKey = 0;
+ int rc = pfnImport(pThis, &uKey, sizeof(uKey), NULL, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ uKey = RT_LE2H_U64(uKey);
+ *(uint64_t *)pvMutation = uKey;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Creates an empty fuzzing context.
+ *
+ * @returns IPRT status code.
+ * @param ppThis Where to store the pointer to the internal fuzzing context instance on success.
+ * @param enmType Fuzzing context type.
+ */
+static int rtFuzzCtxCreateEmpty(PRTFUZZCTXINT *ppThis, RTFUZZCTXTYPE enmType)
+{
+ int rc;
+ PRTFUZZCTXINT pThis = (PRTFUZZCTXINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = RTFUZZCTX_MAGIC;
+ pThis->cRefs = 1;
+ pThis->enmType = enmType;
+ pThis->TreeMutations = NULL;
+ pThis->cbInputMax = UINT32_MAX;
+ pThis->cMutations = 0;
+ pThis->fFlagsBehavioral = 0;
+ pThis->cbMutationsAllocMax = _1G;
+ pThis->cbMemTotal = 0;
+ pThis->offMutStart = 0;
+ pThis->cbMutRange = UINT64_MAX;
+ RTListInit(&pThis->LstMutationsAlloc);
+
+ /* Copy the default mutator descriptors over. */
+ pThis->paMutators = (PRTFUZZMUTATOR)RTMemAllocZ(RT_ELEMENTS(g_aMutators) * sizeof(RTFUZZMUTATOR));
+ if (RT_LIKELY(pThis->paMutators))
+ {
+ pThis->cMutators = RT_ELEMENTS(g_aMutators);
+ memcpy(&pThis->paMutators[0], &g_aMutators[0], RT_ELEMENTS(g_aMutators) * sizeof(RTFUZZMUTATOR));
+
+ rc = RTSemRWCreate(&pThis->hSemRwMutations);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThis->CritSectAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTRandAdvCreateParkMiller(&pThis->hRand);
+ if (RT_SUCCESS(rc))
+ {
+ RTRandAdvSeed(pThis->hRand, RTTimeSystemNanoTS());
+ *ppThis = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectDelete(&pThis->CritSectAlloc);
+ }
+
+ RTSemRWDestroy(pThis->hSemRwMutations);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Destroys the given fuzzing input.
+ *
+ * @param pThis The fuzzing input to destroy.
+ */
+static void rtFuzzInputDestroy(PRTFUZZINPUTINT pThis)
+{
+ PRTFUZZCTXINT pFuzzer = pThis->pFuzzer;
+
+ rtFuzzMutationRelease(pThis->pMutationTop);
+ rtFuzzCtxMemoryFree(pFuzzer, pThis);
+ RTFuzzCtxRelease(pFuzzer);
+}
+
+
+RTDECL(int) RTFuzzCtxCreate(PRTFUZZCTX phFuzzCtx, RTFUZZCTXTYPE enmType)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+
+ return rtFuzzCtxCreateEmpty(phFuzzCtx, enmType);
+}
+
+
+RTDECL(int) RTFuzzCtxCreateFromState(PRTFUZZCTX phFuzzCtx, PFNRTFUZZCTXIMPORT pfnImport, void *pvUser)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnImport, VERR_INVALID_POINTER);
+
+#if 0
+ 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;
+#else
+ RT_NOREF(pvUser);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+RTDECL(int) RTFuzzCtxCreateFromStateMem(PRTFUZZCTX phFuzzCtx, const void *pvState, size_t cbState)
+{
+ AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvState, VERR_INVALID_POINTER);
+ AssertPtrReturn(cbState, VERR_INVALID_POINTER);
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+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 = RTFuzzCtxCreateFromStateMem(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) RTFuzzCtxQueryStats(RTFUZZCTX hFuzzCtx, PRTFUZZCTXSTATS pStats)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pStats, VERR_INVALID_POINTER);
+
+ pStats->cbMemory = ASMAtomicReadZ(&pThis->cbMemTotal);
+ pStats->cMutations = ASMAtomicReadU64(&pThis->cMutations);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Fuzzing context export callback for a single mutation.
+ */
+static DECLCALLBACK(int) rtFuzzCtxStateExportMutations(PAVLU64NODECORE pCore, void *pvParam)
+{
+ PRTFUZZMUTATION pMutation = (PRTFUZZMUTATION)pCore;
+ PCRTFUZZMUTATOR pMutator = pMutation->pMutator;
+ PCRTFUZZEXPORTARGS pArgs = (PCRTFUZZEXPORTARGS)pvParam;
+ RTFUZZMUTATIONSTATE MutationState;
+
+ MutationState.u64Id = RT_H2LE_U64(pMutation->Core.Key);
+ if (pMutation->pMutationParent)
+ MutationState.u64IdParent = RT_H2LE_U64(pMutation->pMutationParent->Core.Key);
+ else
+ MutationState.u64IdParent = 0;
+ MutationState.u64OffMutation = RT_H2LE_U64(pMutation->offMutation);
+ MutationState.cbInput = RT_H2LE_U64((uint64_t)pMutation->cbInput);
+ MutationState.cbMutation = RT_H2LE_U64((uint64_t)pMutation->cbMutation);
+ MutationState.u32IdMutator = RT_H2LE_U32(pMutator->uMutator);
+ MutationState.iLvl = RT_H2LE_U32(pMutation->iLvl);
+ MutationState.u32Magic = RT_H2LE_U32(pMutation->u32Magic);
+
+ int rc = pArgs->pfnExport(pMutation->pFuzzer, &MutationState, sizeof(MutationState), pArgs->pvUser);
+ if ( RT_SUCCESS(rc)
+ && pMutator->pfnExport)
+ rc = pMutator->pfnExport(pMutation->pFuzzer, pMutation, &pMutation->abMutation[0], pArgs->pfnExport, pArgs->pvUser);
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxStateExport(RTFUZZCTX hFuzzCtx, PFNRTFUZZCTXEXPORT pfnExport, void *pvUser)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnExport, 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);
+ switch (pThis->enmType)
+ {
+ case RTFUZZCTXTYPE_BLOB:
+ StateExport.uCtxType = RT_H2LE_U32(RTFUZZCTX_STATE_TYPE_BLOB);
+ break;
+ case RTFUZZCTXTYPE_STREAM:
+ StateExport.uCtxType = RT_H2LE_U32(RTFUZZCTX_STATE_TYPE_STREAM);
+ break;
+ default:
+ AssertFailed();
+ break;
+ }
+ StateExport.cbPrng = RT_H2LE_U32((uint32_t)cbPrng);
+ StateExport.cMutations = RT_H2LE_U32(pThis->cMutations);
+ StateExport.cMutators = RT_H2LE_U32(pThis->cMutators);
+ StateExport.fFlagsBehavioral = RT_H2LE_U32(pThis->fFlagsBehavioral);
+ StateExport.cbInputMax = RT_H2LE_U64(pThis->cbInputMax);
+
+ /* Write the context state and PRNG state first. */
+ rc = pfnExport(pThis, &StateExport, sizeof(StateExport), pvUser);
+ if (RT_SUCCESS(rc))
+ rc = pfnExport(pThis, &aszPrngExport[0], cbPrng, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ /* Write the mutator descriptors next. */
+ for (uint32_t i = 0; i < pThis->cMutators && RT_SUCCESS(rc); i++)
+ {
+ PRTFUZZMUTATOR pMutator = &pThis->paMutators[i];
+ uint32_t cchId = (uint32_t)strlen(pMutator->pszId) + 1;
+ uint32_t cchIdW = RT_H2LE_U32(cchId);
+
+ rc = pfnExport(pThis, &cchIdW, sizeof(cchIdW), pvUser);
+ if (RT_SUCCESS(rc))
+ rc = pfnExport(pThis, &pMutator->pszId[0], cchId, pvUser);
+ }
+ }
+
+ /* Write the mutations last. */
+ if (RT_SUCCESS(rc))
+ {
+ RTFUZZEXPORTARGS Args;
+
+ Args.pfnExport = pfnExport;
+ Args.pvUser = pvUser;
+ rc = RTAvlU64DoWithAll(&pThis->TreeMutations, true /*fFromLeft*/, rtFuzzCtxStateExportMutations, &Args);
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxStateExportToMem(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);
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Export to file callback.
+ */
+static DECLCALLBACK(int) rtFuzzCtxStateExportFile(RTFUZZCTX hFuzzCtx, const void *pvBuf, size_t cbWrite, void *pvUser)
+{
+ RT_NOREF(hFuzzCtx);
+
+ RTFILE hFile = (RTFILE)pvUser;
+ return RTFileWrite(hFile, pvBuf, cbWrite, NULL);
+}
+
+
+RTDECL(int) RTFuzzCtxStateExportToFile(RTFUZZCTX hFuzzCtx, const char *pszFilename)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ 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 = RTFuzzCtxStateExport(hFuzzCtx, rtFuzzCtxStateExportFile, hFile);
+ RTFileClose(hFile);
+ if (RT_FAILURE(rc))
+ RTFileDelete(pszFilename);
+ }
+
+ 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);
+
+ return RTFuzzCtxCorpusInputAddEx(hFuzzCtx, pvInput, cbInput, pThis->offMutStart, pThis->cbMutRange);
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddEx(RTFUZZCTX hFuzzCtx, const void *pvInput, size_t cbInput,
+ uint64_t offMutStart, uint64_t cbMutRange)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvInput, VERR_INVALID_POINTER);
+ AssertReturn(cbInput, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ void *pvCorpus = NULL;
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreateEx(pThis, 0, NULL, offMutStart, cbMutRange,
+ cbInput, &pvCorpus);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->pMutator = &g_MutatorCorpus;
+ pMutation->cbInput = cbInput;
+ pMutation->pvInput = pvCorpus;
+ memcpy(pvCorpus, pvInput, cbInput);
+ rc = rtFuzzCtxMutationAdd(pThis, pMutation);
+ if (RT_FAILURE(rc))
+ rtFuzzMutationDestroy(pMutation);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromFile(RTFUZZCTX hFuzzCtx, const char *pszFilename)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ return RTFuzzCtxCorpusInputAddFromFileEx(hFuzzCtx, pszFilename, pThis->offMutStart, pThis->cbMutRange);
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromFileEx(RTFUZZCTX hFuzzCtx, const char *pszFilename,
+ uint64_t offMutStart, uint64_t cbMutRange)
+{
+ 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 = RTFuzzCtxCorpusInputAddEx(hFuzzCtx, pv, cb, offMutStart, cbMutRange);
+ 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);
+
+ return RTFuzzCtxCorpusInputAddFromVfsFileEx(hFuzzCtx, hVfsFile, pThis->offMutStart, pThis->cbMutRange);
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromVfsFileEx(RTFUZZCTX hFuzzCtx, RTVFSFILE hVfsFile,
+ uint64_t offMutStart, uint64_t cbMutRange)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(hVfsFile != NIL_RTVFSFILE, VERR_INVALID_HANDLE);
+
+ uint64_t cbFile = 0;
+ void *pvCorpus = NULL;
+ int rc = RTVfsFileQuerySize(hVfsFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreateEx(pThis, 0, NULL, offMutStart, cbMutRange,
+ cbFile, &pvCorpus);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->pMutator = &g_MutatorCorpus;
+ pMutation->cbInput = cbFile;
+ pMutation->pvInput = pvCorpus;
+ rc = RTVfsFileRead(hVfsFile, pvCorpus, cbFile, NULL);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCtxMutationAdd(pThis, pMutation);
+
+ if (RT_FAILURE(rc))
+ rtFuzzMutationDestroy(pMutation);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromVfsIoStrm(RTFUZZCTX hFuzzCtx, RTVFSIOSTREAM hVfsIos)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
+
+ return RTFuzzCtxCorpusInputAddFromVfsIoStrmEx(hFuzzCtx, hVfsIos, pThis->offMutStart, pThis->cbMutRange);
+}
+
+RTDECL(int) RTFuzzCtxCorpusInputAddFromVfsIoStrmEx(RTFUZZCTX hFuzzCtx, RTVFSIOSTREAM hVfsIos,
+ uint64_t offMutStart, uint64_t cbMutRange)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
+
+ void *pvCorpus = NULL;
+ RTFSOBJINFO ObjInfo;
+ int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
+ if (RT_SUCCESS(rc))
+ {
+ PRTFUZZMUTATION pMutation = rtFuzzMutationCreateEx(pThis, 0, NULL, offMutStart, cbMutRange,
+ ObjInfo.cbObject, &pvCorpus);
+ if (RT_LIKELY(pMutation))
+ {
+ pMutation->pMutator = &g_MutatorCorpus;
+ pMutation->cbInput = ObjInfo.cbObject;
+ pMutation->pvInput = pvCorpus;
+ rc = RTVfsIoStrmRead(hVfsIos, pvCorpus, ObjInfo.cbObject, true /*fBlocking*/, NULL);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCtxMutationAdd(pThis, pMutation);
+
+ if (RT_FAILURE(rc))
+ rtFuzzMutationDestroy(pMutation);
+ }
+ 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) RTFuzzCtxCfgSetMutationRange(RTFUZZCTX hFuzzCtx, uint64_t offStart, uint64_t cbRange)
+{
+ PRTFUZZCTXINT pThis = hFuzzCtx;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+ pThis->offMutStart = offStart;
+ pThis->cbMutRange = cbRange;
+ return VINF_SUCCESS;
+}
+
+
+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;
+ PRTFUZZMUTATION pMutationParent = rtFuzzCtxMutationPickRnd(pThis);
+ do
+ {
+ uint32_t idxMutator = RTRandAdvU32Ex(pThis->hRand, 0, pThis->cMutators - 1);
+ PCRTFUZZMUTATOR pMutator = &pThis->paMutators[idxMutator];
+ PRTFUZZMUTATION pMutation = NULL;
+
+ uint64_t offStart = 0;
+ if (!(pMutator->fFlags & RTFUZZMUTATOR_F_END_OF_BUF))
+ {
+ uint64_t offMax = pMutationParent->cbInput - 1;
+ if ( pMutationParent->cbMutNew != UINT64_MAX
+ && pMutationParent->offMutStartNew + pMutationParent->cbMutNew < offMax)
+ offMax = pMutationParent->offMutStartNew + pMutationParent->cbMutNew - 1;
+
+ offMax = RT_MAX(pMutationParent->offMutStartNew, offMax);
+ offStart = RTRandAdvU64Ex(pThis->hRand, pMutationParent->offMutStartNew, offMax);
+ }
+ else
+ offStart = pMutationParent->cbInput;
+
+ rc = pMutator->pfnPrep(pThis, offStart, pMutationParent, &pMutation);
+ if ( RT_SUCCESS(rc)
+ && RT_VALID_PTR(pMutation))
+ {
+ pMutation->pMutator = pMutator;
+
+ if (pThis->fFlagsBehavioral & RTFUZZCTX_F_BEHAVIORAL_ADD_INPUT_AUTOMATICALLY_TO_CORPUS)
+ rtFuzzCtxMutationAdd(pThis, pMutation);
+
+ /* Create a new input. */
+ PRTFUZZINPUTINT pInput = (PRTFUZZINPUTINT)rtFuzzCtxMemoryAlloc(pThis, sizeof(RTFUZZINPUTINT));
+ if (RT_LIKELY(pInput))
+ {
+ pInput->u32Magic = 0; /** @todo */
+ pInput->cRefs = 1;
+ pInput->pFuzzer = pThis;
+ pInput->pMutationTop = pMutation;
+ RTFuzzCtxRetain(pThis);
+
+ rtFuzzMutationRelease(pMutationParent);
+ *phFuzzInput = pInput;
+ return rc;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ } while (++cTries <= 50);
+
+ rtFuzzMutationRelease(pMutationParent);
+ if (RT_SUCCESS(rc))
+ rc = VERR_INVALID_STATE;
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzInputQueryBlobData(RTFUZZINPUT hFuzzInput, void **ppv, size_t *pcb)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pFuzzer->enmType == RTFUZZCTXTYPE_BLOB, VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (!pThis->pMutationTop->pvInput)
+ rc = rtFuzzMutationDataFinalize(pThis->pMutationTop);
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppv = pThis->pMutationTop->pvInput;
+ *pcb = pThis->pMutationTop->cbInput;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzInputMutateStreamData(RTFUZZINPUT hFuzzInput, void *pvBuf, size_t cbBuf)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pFuzzer->enmType == RTFUZZCTXTYPE_STREAM, VERR_INVALID_STATE);
+
+ RT_NOREF(pvBuf, cbBuf);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+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) RTFuzzInputQueryDigestString(RTFUZZINPUT hFuzzInput, char *pszDigest, size_t cchDigest)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pFuzzer->enmType == RTFUZZCTXTYPE_BLOB, VERR_INVALID_STATE);
+ AssertPtrReturn(pszDigest, VERR_INVALID_POINTER);
+ AssertReturn(cchDigest >= RTMD5_STRING_LEN + 1, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ if (!pThis->pMutationTop->pvInput)
+ rc = rtFuzzMutationDataFinalize(pThis->pMutationTop);
+
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t abHash[RTMD5_HASH_SIZE];
+ RTMd5(pThis->pMutationTop->pvInput, pThis->pMutationTop->cbInput, &abHash[0]);
+ rc = RTMd5ToString(&abHash[0], pszDigest, cchDigest);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFuzzInputWriteToFile(RTFUZZINPUT hFuzzInput, const char *pszFilename)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pFuzzer->enmType == RTFUZZCTXTYPE_BLOB, VERR_INVALID_STATE);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pThis->pMutationTop->pvInput)
+ rc = rtFuzzMutationDataFinalize(pThis->pMutationTop);
+
+ 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, pThis->pMutationTop->pvInput, pThis->pMutationTop->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 rtFuzzCtxMutationAdd(pThis->pFuzzer, pThis->pMutationTop);
+}
+
+
+RTDECL(int) RTFuzzInputRemoveFromCtxCorpus(RTFUZZINPUT hFuzzInput)
+{
+ PRTFUZZINPUTINT pThis = hFuzzInput;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+#if 0
+ 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;
+#endif
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
diff --git a/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp b/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp
new file mode 100644
index 00000000..d32f579b
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzzclientcmd.cpp
@@ -0,0 +1,329 @@
+/* $Id: fuzzclientcmd.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, fuzzed client command.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fuzz.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/ldr.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>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef DECLCALLBACKTYPE(int, FNLLVMFUZZERTESTONEINPUT,(const uint8_t *pbData, size_t cbData));
+typedef FNLLVMFUZZERTESTONEINPUT *PFNLLVMFUZZERTESTONEINPUT;
+
+
+/**
+ * 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;
+ /** The LLVM libFuzzer compatible entry point if configured */
+ PFNLLVMFUZZERTESTONEINPUT pfnLlvmFuzzerTestOneInput;
+ /** The selected input channel. */
+ RTFUZZOBSINPUTCHAN enmInputChan;
+ /** Standard input VFS handle. */
+ RTVFSIOSTREAM hVfsStdIn;
+ /** Standard output VFS handle. */
+ RTVFSIOSTREAM hVfsStdOut;
+} RTFUZZCMDCLIENT;
+/** Pointer to a fuzzing client command state. */
+typedef RTFUZZCMDCLIENT *PRTFUZZCMDCLIENT;
+
+
+
+/**
+ * Runs the appropriate consumption callback with the provided data.
+ *
+ * @returns Status code, 0 for success.
+ * @param pThis The fuzzing client command state.
+ * @param pvData The data to consume.
+ * @param cbData Size of the data in bytes.
+ */
+static int rtFuzzCmdClientConsume(PRTFUZZCMDCLIENT pThis, const void *pvData, size_t cbData)
+{
+ if (pThis->pfnLlvmFuzzerTestOneInput)
+ return pThis->pfnLlvmFuzzerTestOneInput((const uint8_t *)pvData, cbData);
+ else
+ return pThis->pfnConsume(pvData, cbData, pThis->pvUser);
+}
+
+
+/**
+ * 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 = RTFuzzInputQueryBlobData(hFuzzInput, &pv, &cb);
+ if (RT_SUCCESS(rc))
+ {
+ char bResp = '.';
+ int rc2 = rtFuzzCmdClientConsume(pThis, pv, cb);
+ 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 = RTFuzzCtxCreateFromStateMem(&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;
+}
+
+
+/**
+ * Run a single iteration of the fuzzing client and return.
+ *
+ * @returns Process exit status.
+ * @param pThis The fuzzing client command state.
+ */
+static RTEXITCODE rtFuzzCmdClientRunFile(PRTFUZZCMDCLIENT pThis, const char *pszFilename)
+{
+ void *pv = NULL;
+ size_t cbFile = 0;
+ int rc = RTFileReadAll(pszFilename, &pv, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ rtFuzzCmdClientConsume(pThis, pv, cbFile);
+ RTFileReadAllFree(pv, cbFile);
+ 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 },
+ { "--llvm-input", 'l', RTGETOPT_REQ_STRING },
+ { "--file", 'f', RTGETOPT_REQ_STRING },
+ };
+
+ 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;
+ RTLDRMOD hLlvmMod = NIL_RTLDRMOD;
+ const char *pszFilename = NULL;
+
+ This.pfnConsume = pfnConsume;
+ This.pvUser = pvUser;
+ This.enmInputChan = RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT;
+
+ /* Argument parsing loop. */
+ bool fContinue = true;
+ bool fExit = false;
+ do
+ {
+ RTGETOPTUNION ValueUnion;
+ int chOpt = RTGetOpt(&GetState, &ValueUnion);
+ switch (chOpt)
+ {
+ case 0:
+ fContinue = false;
+ break;
+
+ case 'f':
+ {
+ pszFilename = ValueUnion.psz;
+ This.enmInputChan = RTFUZZOBSINPUTCHAN_FILE;
+ break;
+ }
+
+ case 'l':
+ {
+ /*
+ * Load the indicated library and try to resolve LLVMFuzzerTestOneInput,
+ * which will act as the input callback.
+ */
+ rc = RTLdrLoad(ValueUnion.psz, &hLlvmMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLlvmMod, "LLVMFuzzerTestOneInput", (void **)&This.pfnLlvmFuzzerTestOneInput);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to query '%s' from '%s': %Rrc",
+ "LLVMFuzzerTestOneInput",
+ ValueUnion.psz,
+ rc);
+ }
+ 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;
+ fExit = true;
+ break;
+
+ case 'V':
+ RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
+ fContinue = false;
+ fExit = true;
+ break;
+
+ default:
+ rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
+ fContinue = false;
+ break;
+ }
+ } while (fContinue);
+
+ if ( rcExit == RTEXITCODE_SUCCESS
+ && !fExit)
+ {
+ switch (This.enmInputChan)
+ {
+ case RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT:
+ rcExit = rtFuzzCmdClientRun(&This);
+ break;
+ case RTFUZZOBSINPUTCHAN_FILE:
+ rcExit = rtFuzzCmdClientRunFile(&This, pszFilename);
+ break;
+ default:
+ rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Input channel unknown/not implemented yet");
+ }
+ }
+
+ if (hLlvmMod != NIL_RTLDRMOD)
+ RTLdrClose(hLlvmMod);
+ }
+ 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..562218e1
--- /dev/null
+++ b/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp
@@ -0,0 +1,1877 @@
+/* $Id: fuzzmastercmd.cpp $ */
+/** @file
+ * IPRT - Fuzzing framework API, master command.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* 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/env.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/time.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;
+ /** Target recorder flags. */
+ uint32_t fTgtRecFlags;
+ /** The fuzzing observer state handle. */
+ RTFUZZOBS hFuzzObs;
+ /** Flag whether fuzzing was started. */
+ bool fStarted;
+ /** Time when this run was created. */
+ RTTIME TimeCreated;
+ /** Millisecond timestamp when the run was created. */
+ uint64_t tsCreatedMs;
+} 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 process environment 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 rtFuzzCmdMasterFuzzRunProcessEnvironment(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValEnv;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Env", &hJsonValEnv);
+ if (RT_SUCCESS(rc))
+ {
+ bool fReplaceEnv = false; /* false means to append everything to the default block. */
+
+ rc = RTJsonValueQueryBooleanByName(hJsonRoot, "EnvReplace", &fReplaceEnv);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_NOT_FOUND)
+ {
+ RTJSONIT hEnvIt;
+ RTENV hEnv = NULL;
+
+ if (fReplaceEnv)
+ rc = RTEnvCreate(&hEnv);
+ else
+ rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTJsonIteratorBeginArray(hJsonValEnv, &hEnvIt);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTJSONVAL hVal;
+ rc = RTJsonIteratorQueryValue(hEnvIt, &hVal, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszVar = RTJsonValueGetString(hVal);
+ if (RT_LIKELY(pszVar))
+ rc = RTEnvPutEx(hEnv, pszVar);
+ RTJsonValueRelease(hVal);
+ }
+ rc = RTJsonIteratorNext(hEnvIt);
+ } while (RT_SUCCESS(rc));
+
+ if ( rc == VERR_JSON_IS_EMPTY
+ || rc == VERR_JSON_ITERATOR_END)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse environment");
+
+ RTJsonIteratorFree(hEnvIt);
+ }
+ else if (rc == VERR_JSON_IS_EMPTY)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Environment\" is not an array");
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsSetTestBinaryEnv(pFuzzRun->hFuzzObs, hEnv);
+ AssertRC(rc);
+ }
+ else if (hEnv)
+ RTEnvDestroy(hEnv);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create environment block");
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"EnvReplace\"");
+
+ RTJsonValueRelease(hJsonValEnv);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS; /* Just keep using the default environment. */
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Environment\"");
+
+ return rc;
+}
+
+
+/**
+ * Processes process environment 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 rtFuzzCmdMasterFuzzRunProcessSanitizers(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValSan;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Sanitizers", &hJsonValSan);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t fSanitizers = 0;
+ RTJSONIT hSanIt;
+ rc = RTJsonIteratorBeginArray(hJsonValSan, &hSanIt);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTJSONVAL hVal;
+ rc = RTJsonIteratorQueryValue(hSanIt, &hVal, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszSan = RTJsonValueGetString(hVal);
+ if (!RTStrICmp(pszSan, "Asan"))
+ fSanitizers |= RTFUZZOBS_SANITIZER_F_ASAN;
+ else if (!RTStrICmp(pszSan, "SanCov"))
+ fSanitizers |= RTFUZZOBS_SANITIZER_F_SANCOV;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The sanitizer '%s' is not known", pszSan);
+ RTJsonValueRelease(hVal);
+ }
+ rc = RTJsonIteratorNext(hSanIt);
+ } while (RT_SUCCESS(rc));
+
+ if ( rc == VERR_JSON_IS_EMPTY
+ || rc == VERR_JSON_ITERATOR_END)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse sanitizers");
+
+ RTJsonIteratorFree(hSanIt);
+ }
+ else if (rc == VERR_JSON_IS_EMPTY)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Sanitizers\" is not an array");
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsSetTestBinarySanitizers(pFuzzRun->hFuzzObs, fSanitizers);
+ AssertRC(rc);
+ }
+
+ RTJsonValueRelease(hJsonValSan);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS; /* Just keep using the defaults. */
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Sanitizers\"");
+
+ 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 the given seed file 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 rtFuzzCmdMasterFuzzRunProcessSeedFile(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszFile, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Decompress if applicable. */
+ if (!RTStrICmp(pszCompression, "None"))
+ rc = RTFuzzCtxCorpusInputAddFromFile(hFuzzCtx, pszFile);
+ else
+ {
+ RTVFSIOSTREAM hVfsIosSeed;
+ rc = RTVfsIoStrmOpenNormal(pszFile, RTFILE_O_OPEN | RTFILE_O_READ, &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");
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes a signle input seed given as a file path 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 rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(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 hJsonValFile;
+ rc = RTJsonValueQueryByName(hJsonSeed, "File", &hJsonValFile);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszFile = RTJsonValueGetString(hJsonValFile);
+ if (RT_LIKELY(pszFile))
+ rc = rtFuzzCmdMasterFuzzRunProcessSeedFile(hFuzzCtx, pszCompression, pszFile, pErrInfo);
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"File\" value is not a string");
+
+ RTJsonValueRelease(hJsonValFile);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"File\" 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);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTJsonValueQueryByName(hJsonRoot, "InputSeedFiles", &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 = rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(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);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ }
+
+ 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);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t msTimeoutMax = 0;
+ rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&msTimeoutMax, "TimeoutMax", hJsonRoot, 1000, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzObsSetTestBinaryTimeout(pFuzzRun->hFuzzObs, msTimeoutMax);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes target recording 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 rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
+{
+ RTJSONVAL hJsonValTgt;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "TgtRec", &hJsonValTgt);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t fTgtRecFlags = 0;
+ RTJSONIT hTgtIt;
+ rc = RTJsonIteratorBeginArray(hJsonValTgt, &hTgtIt);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ RTJSONVAL hVal;
+ rc = RTJsonIteratorQueryValue(hTgtIt, &hVal, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszTgtRec = RTJsonValueGetString(hVal);
+ if (!RTStrICmp(pszTgtRec, "StdOut"))
+ fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDOUT;
+ else if (!RTStrICmp(pszTgtRec, "StdErr"))
+ fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDERR;
+ else if (!RTStrICmp(pszTgtRec, "ProcSts"))
+ fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_PROCSTATUS;
+ else if (!RTStrICmp(pszTgtRec, "SanCov"))
+ fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_SANCOV;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The recording flags '%s' is not known", pszTgtRec);
+ RTJsonValueRelease(hVal);
+ }
+ rc = RTJsonIteratorNext(hTgtIt);
+ } while (RT_SUCCESS(rc));
+
+ if ( rc == VERR_JSON_IS_EMPTY
+ || rc == VERR_JSON_ITERATOR_END)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse target recording flags");
+
+ RTJsonIteratorFree(hTgtIt);
+ }
+ else if (rc == VERR_JSON_IS_EMPTY)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"TgtRec\" is not an array");
+
+ pFuzzRun->fTgtRecFlags = fTgtRecFlags;
+
+ RTJsonValueRelease(hJsonValTgt);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ pFuzzRun->fTgtRecFlags = RTFUZZTGT_REC_STATE_F_PROCSTATUS;
+ rc = VINF_SUCCESS; /* Just keep using the defaults. */
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"TgtRec\"");
+
+ return rc;
+}
+
+
+/**
+ * Sets up the directories for the given fuzzing run.
+ *
+ * @returns IPRT status code.
+ * @param pThis The fuzzing master command state.
+ * @param pFuzzRun The fuzzing run to setup the directories for.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterFuzzRunSetupDirectories(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun, PRTERRINFO pErrInfo)
+{
+ /* Create temp directories. */
+ char szTmpDir[RTPATH_MAX];
+ int 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 (rc == VERR_ALREADY_EXISTS)
+ {
+ /* Clear the directory. */
+ rc = RTDirRemoveRecursive(szTmpDir, RTDIRRMREC_F_CONTENT_ONLY);
+ }
+
+ 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 == VERR_ALREADY_EXISTS)
+ {
+ rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
+ if (RT_FAILURE(rc))
+ 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);
+
+ 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 = rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs, RTFUZZCTXTYPE_BLOB, pFuzzRun->fTgtRecFlags);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunProcessEnvironment(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))
+ rc = rtFuzzCmdMasterFuzzRunProcessSanitizers(pFuzzRun, hJsonRoot, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterFuzzRunSetupDirectories(pThis, pFuzzRun, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Start fuzzing. */
+ RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
+ rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
+ if (RT_SUCCESS(rc))
+ {
+ RTTIMESPEC TimeSpec;
+ RTTimeNow(&TimeSpec);
+ RTTimeLocalExplode(&pFuzzRun->TimeCreated, &TimeSpec);
+ pFuzzRun->tsCreatedMs = RTTimeMilliTS();
+ pFuzzRun->fStarted = true;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
+ }
+
+ int rc2 = RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
+ AssertRC(rc2); RT_NOREF(rc2);
+ }
+ }
+
+ RTStrFree(pFuzzRun->pszId);
+ pFuzzRun->pszId = NULL;
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+
+ RTMemFree(pFuzzRun);
+ }
+ 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 = RTFuzzCtxStateExportToMem(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;
+}
+
+
+/**
+ * Queries the statistics for the given fuzzing run and adds the result to the response.
+ *
+ * @returns IPRT static code.
+ * @param pThis The fuzzing master command state.
+ * @param pFuzzRun The fuzzing run.
+ * @param pszIndent Indentation to use.
+ * @param fLast Flags whether this is the last element in the list.
+ * @param pErrInfo Where to store the error information on failure, optional.
+ */
+static int rtFuzzCmdMasterProcessQueryRunStats(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun,
+ const char *pszIndent, bool fLast, PRTERRINFO pErrInfo)
+{
+ RTFUZZOBSSTATS ObsStats;
+ RTFUZZCTXSTATS CtxStats;
+ RTFUZZCTX hFuzzCtx;
+ RT_ZERO(ObsStats); RT_ZERO(CtxStats);
+
+ int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFuzzCtxQueryStats(hFuzzCtx, &CtxStats);
+ RTFuzzCtxRelease(hFuzzCtx);
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &ObsStats);
+ if (RT_SUCCESS(rc))
+ {
+ const char s_szStatsFmt[] = "%s{ \n"
+ "%s \"Id\": \"%s\"\n"
+ "%s \"TimeCreated\": \"%s\"\n"
+ "%s \"UptimeSec\": %llu\n"
+ "%s \"FuzzedInputsPerSec\": %u\n"
+ "%s \"FuzzedInputs\": %u\n"
+ "%s \"FuzzedInputsHang\": %u\n"
+ "%s \"FuzzedInputsCrash\": %u\n"
+ "%s \"MemoryUsage\": %zu\n"
+ "%s \"CorpusSize\": %llu\n"
+ "%s}%s\n";
+ char aszTime[_1K]; RT_ZERO(aszTime);
+ char aszStats[_4K]; RT_ZERO(aszStats);
+
+ if (RTTimeToString(&pFuzzRun->TimeCreated, aszTime, sizeof(aszTime)))
+ {
+ ssize_t cchStats = RTStrPrintf2(&aszStats[0], sizeof(aszStats),
+ s_szStatsFmt, pszIndent,
+ pszIndent, pFuzzRun->pszId,
+ pszIndent, aszTime,
+ pszIndent, (RTTimeMilliTS() - pFuzzRun->tsCreatedMs) / RT_MS_1SEC_64,
+ pszIndent, ObsStats.cFuzzedInputsPerSec,
+ pszIndent, ObsStats.cFuzzedInputs,
+ pszIndent, ObsStats.cFuzzedInputsHang,
+ pszIndent, ObsStats.cFuzzedInputsCrash,
+ pszIndent, CtxStats.cbMemory,
+ pszIndent, CtxStats.cMutations,
+ pszIndent, fLast ? "" : ",");
+ if (RT_LIKELY(cchStats > 0))
+ {
+ rc = RTStrAAppend(&pThis->pszResponse, &aszStats[0]);
+ if (RT_FAILURE(rc))
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to build statistics response", rc);
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow");
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Buffer overflow conerting time to string");
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
+
+ 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)
+{
+ RTJSONVAL hJsonValId;
+ int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
+ if (RT_SUCCESS(rc))
+ {
+ RTJsonValueRelease(hJsonValId);
+ PRTFUZZRUN pFuzzRun;
+ rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+ if (RT_SUCCESS(rc))
+ rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pFuzzRun, " ",
+ true /*fLast*/, pErrInfo);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ /* Id is not there, so collect statistics of all running jobs. */
+ rc = RTStrAAppend(&pThis->pszResponse, " [\n");
+ if (RT_SUCCESS(rc))
+ {
+ PRTFUZZRUN pRun = NULL;
+ RTListForEach(&pThis->LstFuzzed, pRun, RTFUZZRUN, NdFuzzed)
+ {
+ bool fLast = RTListNodeIsLast(&pThis->LstFuzzed, &pRun->NdFuzzed);
+ rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pRun, " ", fLast, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ rc = RTStrAAppend(&pThis->pszResponse, " ]\n");
+ }
+ }
+ else
+ rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't get \"Id\" value");
+
+ 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.
+ *
+ * @param pThis The fuzzing master command state.
+ */
+static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/**
+ * Sends an ACK response to the client.
+ *
+ * @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.
+ *
+ * @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)
+ && cbThisRead)
+ {
+ 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;
+}
+