VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp

Last change on this file was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.8 KB
Line 
1/* $Id: fuzzmastercmd.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * IPRT - Fuzzing framework API, master command.
4 */
5
6/*
7 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/fuzz.h>
42#include "internal/iprt.h"
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/base64.h>
47#include <iprt/buildconfig.h>
48#include <iprt/ctype.h>
49#include <iprt/env.h>
50#include <iprt/err.h>
51#include <iprt/file.h>
52#include <iprt/getopt.h>
53#include <iprt/json.h>
54#include <iprt/list.h>
55#include <iprt/mem.h>
56#include <iprt/message.h>
57#include <iprt/path.h>
58#include <iprt/process.h>
59#include <iprt/stream.h>
60#include <iprt/string.h>
61#include <iprt/tcp.h>
62#include <iprt/thread.h>
63#include <iprt/time.h>
64#include <iprt/vfs.h>
65#include <iprt/zip.h>
66
67
68/**
69 * A running fuzzer state.
70 */
71typedef struct RTFUZZRUN
72{
73 /** List node. */
74 RTLISTNODE NdFuzzed;
75 /** Identifier. */
76 char *pszId;
77 /** Number of processes. */
78 uint32_t cProcs;
79 /** Target recorder flags. */
80 uint32_t fTgtRecFlags;
81 /** The fuzzing observer state handle. */
82 RTFUZZOBS hFuzzObs;
83 /** Flag whether fuzzing was started. */
84 bool fStarted;
85 /** Time when this run was created. */
86 RTTIME TimeCreated;
87 /** Millisecond timestamp when the run was created. */
88 uint64_t tsCreatedMs;
89} RTFUZZRUN;
90/** Pointer to a running fuzzer state. */
91typedef RTFUZZRUN *PRTFUZZRUN;
92
93
94/**
95 * Fuzzing master command state.
96 */
97typedef struct RTFUZZCMDMASTER
98{
99 /** List of running fuzzers. */
100 RTLISTANCHOR LstFuzzed;
101 /** The port to listen on. */
102 uint16_t uPort;
103 /** The TCP server for requests. */
104 PRTTCPSERVER hTcpSrv;
105 /** The root temp directory. */
106 const char *pszTmpDir;
107 /** The root results directory. */
108 const char *pszResultsDir;
109 /** Flag whether to shutdown. */
110 bool fShutdown;
111 /** The response message. */
112 char *pszResponse;
113} RTFUZZCMDMASTER;
114/** Pointer to a fuzzing master command state. */
115typedef RTFUZZCMDMASTER *PRTFUZZCMDMASTER;
116
117
118/**
119 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
120 *
121 * @returns @a rc
122 * @param pErrInfo Extended error info.
123 * @param rc The return code.
124 * @param pszFormat The message format.
125 * @param ... The message format arguments.
126 */
127static int rtFuzzCmdMasterErrorRc(PRTERRINFO pErrInfo, int rc, const char *pszFormat, ...)
128{
129 va_list va;
130 va_start(va, pszFormat);
131 if (pErrInfo)
132 RTErrInfoSetV(pErrInfo, rc, pszFormat, va);
133 else
134 RTMsgErrorV(pszFormat, va);
135 va_end(va);
136 return rc;
137}
138
139
140/**
141 * Returns a running fuzzer state by the given ID.
142 *
143 * @returns Pointer to the running fuzzer state or NULL if not found.
144 * @param pThis The fuzzing master command state.
145 * @param pszId The ID to look for.
146 */
147static PRTFUZZRUN rtFuzzCmdMasterGetFuzzerById(PRTFUZZCMDMASTER pThis, const char *pszId)
148{
149 PRTFUZZRUN pIt = NULL;
150 RTListForEach(&pThis->LstFuzzed, pIt, RTFUZZRUN, NdFuzzed)
151 {
152 if (!RTStrCmp(pIt->pszId, pszId))
153 return pIt;
154 }
155
156 return NULL;
157}
158
159
160#if 0 /* unused */
161/**
162 * Processes and returns the value of the given config item in the JSON request.
163 *
164 * @returns IPRT status code.
165 * @param ppszStr Where to store the pointer to the string on success.
166 * @param pszCfgItem The config item to resolve.
167 * @param hJsonCfg The JSON object containing the item.
168 * @param pErrInfo Where to store the error information on failure, optional.
169 */
170static int rtFuzzCmdMasterFuzzRunProcessCfgString(char **ppszStr, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
171{
172 int rc = RTJsonValueQueryStringByName(hJsonCfg, pszCfgItem, ppszStr);
173 if (RT_FAILURE(rc))
174 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query string value of \"%s\"", pszCfgItem);
175
176 return rc;
177}
178
179
180/**
181 * Processes and returns the value of the given config item in the JSON request.
182 *
183 * @returns IPRT status code.
184 * @param pfVal Where to store the config value on success.
185 * @param pszCfgItem The config item to resolve.
186 * @param hJsonCfg The JSON object containing the item.
187 * @param pErrInfo Where to store the error information on failure, optional.
188 */
189static int rtFuzzCmdMasterFuzzRunProcessCfgBool(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
190{
191 int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
192 if (RT_FAILURE(rc))
193 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
194
195 return rc;
196}
197
198
199/**
200 * Processes and returns the value of the given config item in the JSON request.
201 *
202 * @returns IPRT status code.
203 * @param pfVal Where to store the config value on success.
204 * @param pszCfgItem The config item to resolve.
205 * @param hJsonCfg The JSON object containing the item.
206 * @param fDef Default value if the item wasn't found.
207 * @param pErrInfo Where to store the error information on failure, optional.
208 */
209static int rtFuzzCmdMasterFuzzRunProcessCfgBoolDef(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, bool fDef, PRTERRINFO pErrInfo)
210{
211 int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
212 if (rc == VERR_NOT_FOUND)
213 {
214 *pfVal = fDef;
215 rc = VINF_SUCCESS;
216 }
217 else if (RT_FAILURE(rc))
218 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
219
220 return rc;
221}
222#endif
223
224
225/**
226 * Processes and returns the value of the given config item in the JSON request.
227 *
228 * @returns IPRT status code.
229 * @param pcbVal Where to store the config value on success.
230 * @param pszCfgItem The config item to resolve.
231 * @param hJsonCfg The JSON object containing the item.
232 * @param cbDef Default value if the item wasn't found.
233 * @param pErrInfo Where to store the error information on failure, optional.
234 */
235static int rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(size_t *pcbVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, size_t cbDef, PRTERRINFO pErrInfo)
236{
237 *pcbVal = cbDef; /* Make GCC 6.3.0 happy. */
238
239 int64_t i64Val = 0;
240 int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
241 if (rc == VERR_NOT_FOUND)
242 rc = VINF_SUCCESS;
243 else if (RT_FAILURE(rc))
244 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query size_t value of \"%s\"", pszCfgItem);
245 else if (i64Val < 0 || (size_t)i64Val != (uint64_t)i64Val)
246 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
247 else
248 *pcbVal = (size_t)i64Val;
249
250 return rc;
251}
252
253
254/**
255 * Processes and returns the value of the given config item in the JSON request.
256 *
257 * @returns IPRT status code.
258 * @param pcbVal Where to store the config value on success.
259 * @param pszCfgItem The config item to resolve.
260 * @param hJsonCfg The JSON object containing the item.
261 * @param cbDef Default value if the item wasn't found.
262 * @param pErrInfo Where to store the error information on failure, optional.
263 */
264static int rtFuzzCmdMasterFuzzRunProcessCfgU32Def(uint32_t *pu32Val, const char *pszCfgItem, RTJSONVAL hJsonCfg, uint32_t u32Def, PRTERRINFO pErrInfo)
265{
266 int64_t i64Val = 0;
267 int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
268 if (rc == VERR_NOT_FOUND)
269 {
270 *pu32Val = u32Def;
271 rc = VINF_SUCCESS;
272 }
273 else if (RT_FAILURE(rc))
274 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query uint32_t value of \"%s\"", pszCfgItem);
275 else if (i64Val < 0 || (uint32_t)i64Val != (uint64_t)i64Val)
276 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
277 else
278 *pu32Val = (uint32_t)i64Val;
279
280 return rc;
281}
282
283
284/**
285 * Returns the configured input channel for the binary under test.
286 *
287 * @returns Selected input channel or RTFUZZOBSINPUTCHAN_INVALID if an error occurred.
288 * @param pszCfgItem The config item to resolve.
289 * @param hJsonCfg The JSON object containing the item.
290 * @param enmChanDef Default value if the item wasn't found.
291 * @param pErrInfo Where to store the error information on failure, optional.
292 */
293static RTFUZZOBSINPUTCHAN rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan(const char *pszCfgItem, RTJSONVAL hJsonCfg, RTFUZZOBSINPUTCHAN enmChanDef, PRTERRINFO pErrInfo)
294{
295 RTFUZZOBSINPUTCHAN enmInputChan = RTFUZZOBSINPUTCHAN_INVALID;
296
297 RTJSONVAL hJsonVal;
298 int rc = RTJsonValueQueryByName(hJsonCfg, pszCfgItem, &hJsonVal);
299 if (rc == VERR_NOT_FOUND)
300 enmInputChan = enmChanDef;
301 else if (RT_SUCCESS(rc))
302 {
303 const char *pszBinary = RTJsonValueGetString(hJsonVal);
304 if (pszBinary)
305 {
306 if (!RTStrCmp(pszBinary, "File"))
307 enmInputChan = RTFUZZOBSINPUTCHAN_FILE;
308 else if (!RTStrCmp(pszBinary, "Stdin"))
309 enmInputChan = RTFUZZOBSINPUTCHAN_STDIN;
310 else if (!RTStrCmp(pszBinary, "FuzzingAware"))
311 enmInputChan = RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT;
312 else
313 rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "JSON request malformed: \"%s\" for \"%s\" is not known", pszCfgItem, pszBinary);
314 }
315 else
316 rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"%s\" is not a string", pszCfgItem);
317
318 RTJsonValueRelease(hJsonVal);
319 }
320 else
321 rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"%s\"", pszCfgItem);
322
323 return enmInputChan;
324}
325
326
327/**
328 * Processes binary related configs for the given fuzzing run.
329 *
330 * @returns IPRT status code.
331 * @param pFuzzRun The fuzzing run.
332 * @param hJsonRoot The root node of the JSON request.
333 * @param pErrInfo Where to store the error information on failure, optional.
334 */
335static int rtFuzzCmdMasterFuzzRunProcessBinaryCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
336{
337 RTJSONVAL hJsonVal;
338 int rc = RTJsonValueQueryByName(hJsonRoot, "BinaryPath", &hJsonVal);
339 if (RT_SUCCESS(rc))
340 {
341 const char *pszBinary = RTJsonValueGetString(hJsonVal);
342 if (RT_LIKELY(pszBinary))
343 {
344 RTFUZZOBSINPUTCHAN enmInputChan = rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan("InputChannel", hJsonRoot, RTFUZZOBSINPUTCHAN_STDIN, pErrInfo);
345 if (enmInputChan != RTFUZZOBSINPUTCHAN_INVALID)
346 {
347 rc = RTFuzzObsSetTestBinary(pFuzzRun->hFuzzObs, pszBinary, enmInputChan);
348 if (RT_FAILURE(rc))
349 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to add the binary path for the fuzzing run");
350 }
351 }
352 else
353 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"BinaryPath\" is not a string");
354 RTJsonValueRelease(hJsonVal);
355 }
356 else
357 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query value of \"BinaryPath\"");
358
359 return rc;
360}
361
362
363/**
364 * Processes argument related configs for the given fuzzing run.
365 *
366 * @returns IPRT status code.
367 * @param pFuzzRun The fuzzing run.
368 * @param hJsonRoot The root node of the JSON request.
369 * @param pErrInfo Where to store the error information on failure, optional.
370 */
371static int rtFuzzCmdMasterFuzzRunProcessArgCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
372{
373 RTJSONVAL hJsonValArgArray;
374 int rc = RTJsonValueQueryByName(hJsonRoot, "Arguments", &hJsonValArgArray);
375 if (RT_SUCCESS(rc))
376 {
377 unsigned cArgs = 0;
378 rc = RTJsonValueQueryArraySize(hJsonValArgArray, &cArgs);
379 if (RT_SUCCESS(rc))
380 {
381 if (cArgs > 0)
382 {
383 const char **papszArgs = (const char **)RTMemAllocZ(cArgs * sizeof(const char *));
384 RTJSONVAL *pahJsonVal = (RTJSONVAL *)RTMemAllocZ(cArgs * sizeof(RTJSONVAL));
385 if (RT_LIKELY(papszArgs && pahJsonVal))
386 {
387 unsigned idx = 0;
388
389 for (idx = 0; idx < cArgs && RT_SUCCESS(rc); idx++)
390 {
391 rc = RTJsonValueQueryByIndex(hJsonValArgArray, idx, &pahJsonVal[idx]);
392 if (RT_SUCCESS(rc))
393 {
394 papszArgs[idx] = RTJsonValueGetString(pahJsonVal[idx]);
395 if (RT_UNLIKELY(!papszArgs[idx]))
396 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Argument %u is not a string", idx);
397 }
398 }
399
400 if (RT_SUCCESS(rc))
401 {
402 rc = RTFuzzObsSetTestBinaryArgs(pFuzzRun->hFuzzObs, papszArgs, cArgs);
403 if (RT_FAILURE(rc))
404 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to set arguments for the fuzzing run");
405 }
406
407 /* Release queried values. */
408 while (idx > 0)
409 {
410 RTJsonValueRelease(pahJsonVal[idx - 1]);
411 idx--;
412 }
413 }
414 else
415 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Out of memory allocating memory for the argument vector");
416
417 if (papszArgs)
418 RTMemFree(papszArgs);
419 if (pahJsonVal)
420 RTMemFree(pahJsonVal);
421 }
422 }
423 else
424 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Arguments\" is not an array");
425 RTJsonValueRelease(hJsonValArgArray);
426 }
427
428 return rc;
429}
430
431
432/**
433 * Processes process environment related configs for the given fuzzing run.
434 *
435 * @returns IPRT status code.
436 * @param pFuzzRun The fuzzing run.
437 * @param hJsonRoot The root node of the JSON request.
438 * @param pErrInfo Where to store the error information on failure, optional.
439 */
440static int rtFuzzCmdMasterFuzzRunProcessEnvironment(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
441{
442 RTJSONVAL hJsonValEnv;
443 int rc = RTJsonValueQueryByName(hJsonRoot, "Env", &hJsonValEnv);
444 if (RT_SUCCESS(rc))
445 {
446 bool fReplaceEnv = false; /* false means to append everything to the default block. */
447
448 rc = RTJsonValueQueryBooleanByName(hJsonRoot, "EnvReplace", &fReplaceEnv);
449 if ( RT_SUCCESS(rc)
450 || rc == VERR_NOT_FOUND)
451 {
452 RTJSONIT hEnvIt;
453 RTENV hEnv = NULL;
454
455 if (fReplaceEnv)
456 rc = RTEnvCreate(&hEnv);
457 else
458 rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
459
460 if (RT_SUCCESS(rc))
461 {
462 rc = RTJsonIteratorBeginArray(hJsonValEnv, &hEnvIt);
463 if (RT_SUCCESS(rc))
464 {
465 do
466 {
467 RTJSONVAL hVal;
468 rc = RTJsonIteratorQueryValue(hEnvIt, &hVal, NULL);
469 if (RT_SUCCESS(rc))
470 {
471 const char *pszVar = RTJsonValueGetString(hVal);
472 if (RT_LIKELY(pszVar))
473 rc = RTEnvPutEx(hEnv, pszVar);
474 RTJsonValueRelease(hVal);
475 }
476 rc = RTJsonIteratorNext(hEnvIt);
477 } while (RT_SUCCESS(rc));
478
479 if ( rc == VERR_JSON_IS_EMPTY
480 || rc == VERR_JSON_ITERATOR_END)
481 rc = VINF_SUCCESS;
482 else
483 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse environment");
484
485 RTJsonIteratorFree(hEnvIt);
486 }
487 else if (rc == VERR_JSON_IS_EMPTY)
488 rc = VINF_SUCCESS;
489 else
490 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Environment\" is not an array");
491
492 if (RT_SUCCESS(rc))
493 {
494 rc = RTFuzzObsSetTestBinaryEnv(pFuzzRun->hFuzzObs, hEnv);
495 AssertRC(rc);
496 }
497 else if (hEnv)
498 RTEnvDestroy(hEnv);
499 }
500 else
501 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create environment block");
502 }
503 else
504 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"EnvReplace\"");
505
506 RTJsonValueRelease(hJsonValEnv);
507 }
508 else if (rc == VERR_NOT_FOUND)
509 rc = VINF_SUCCESS; /* Just keep using the default environment. */
510 else
511 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Environment\"");
512
513 return rc;
514}
515
516
517/**
518 * Processes process environment related configs for the given fuzzing run.
519 *
520 * @returns IPRT status code.
521 * @param pFuzzRun The fuzzing run.
522 * @param hJsonRoot The root node of the JSON request.
523 * @param pErrInfo Where to store the error information on failure, optional.
524 */
525static int rtFuzzCmdMasterFuzzRunProcessSanitizers(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
526{
527 RTJSONVAL hJsonValSan;
528 int rc = RTJsonValueQueryByName(hJsonRoot, "Sanitizers", &hJsonValSan);
529 if (RT_SUCCESS(rc))
530 {
531 uint32_t fSanitizers = 0;
532 RTJSONIT hSanIt;
533 rc = RTJsonIteratorBeginArray(hJsonValSan, &hSanIt);
534 if (RT_SUCCESS(rc))
535 {
536 do
537 {
538 RTJSONVAL hVal;
539 rc = RTJsonIteratorQueryValue(hSanIt, &hVal, NULL);
540 if (RT_SUCCESS(rc))
541 {
542 const char *pszSan = RTJsonValueGetString(hVal);
543 if (!RTStrICmp(pszSan, "Asan"))
544 fSanitizers |= RTFUZZOBS_SANITIZER_F_ASAN;
545 else if (!RTStrICmp(pszSan, "SanCov"))
546 fSanitizers |= RTFUZZOBS_SANITIZER_F_SANCOV;
547 else
548 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The sanitizer '%s' is not known", pszSan);
549 RTJsonValueRelease(hVal);
550 }
551 rc = RTJsonIteratorNext(hSanIt);
552 } while (RT_SUCCESS(rc));
553
554 if ( rc == VERR_JSON_IS_EMPTY
555 || rc == VERR_JSON_ITERATOR_END)
556 rc = VINF_SUCCESS;
557 else
558 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse sanitizers");
559
560 RTJsonIteratorFree(hSanIt);
561 }
562 else if (rc == VERR_JSON_IS_EMPTY)
563 rc = VINF_SUCCESS;
564 else
565 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Sanitizers\" is not an array");
566
567 if (RT_SUCCESS(rc))
568 {
569 rc = RTFuzzObsSetTestBinarySanitizers(pFuzzRun->hFuzzObs, fSanitizers);
570 AssertRC(rc);
571 }
572
573 RTJsonValueRelease(hJsonValSan);
574 }
575 else if (rc == VERR_NOT_FOUND)
576 rc = VINF_SUCCESS; /* Just keep using the defaults. */
577 else
578 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Sanitizers\"");
579
580 return rc;
581}
582
583
584/**
585 * Processes the given seed and adds it to the input corpus.
586 *
587 * @returns IPRT status code.
588 * @param hFuzzCtx The fuzzing context handle.
589 * @param pszCompression Compression used for the seed.
590 * @param pszSeed The seed as a base64 encoded string.
591 * @param pErrInfo Where to store the error information on failure, optional.
592 */
593static int rtFuzzCmdMasterFuzzRunProcessSeed(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszSeed, PRTERRINFO pErrInfo)
594{
595 int rc = VINF_SUCCESS;
596 ssize_t cbSeedDecoded = RTBase64DecodedSize(pszSeed, NULL);
597 if (cbSeedDecoded > 0)
598 {
599 uint8_t *pbSeedDecoded = (uint8_t *)RTMemAllocZ(cbSeedDecoded);
600 if (RT_LIKELY(pbSeedDecoded))
601 {
602 rc = RTBase64Decode(pszSeed, pbSeedDecoded, cbSeedDecoded, NULL, NULL);
603 if (RT_SUCCESS(rc))
604 {
605 /* Decompress if applicable. */
606 if (!RTStrICmp(pszCompression, "None"))
607 rc = RTFuzzCtxCorpusInputAdd(hFuzzCtx, pbSeedDecoded, cbSeedDecoded);
608 else
609 {
610 RTVFSIOSTREAM hVfsIosSeed;
611 rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbSeedDecoded, cbSeedDecoded, &hVfsIosSeed);
612 if (RT_SUCCESS(rc))
613 {
614 RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
615
616 if (!RTStrICmp(pszCompression, "Gzip"))
617 rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
618 else
619 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
620
621 if (RT_SUCCESS(rc))
622 {
623 RTVFSFILE hVfsFile;
624 rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
625 if (RT_SUCCESS(rc))
626 {
627 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
628 if (RT_SUCCESS(rc))
629 {
630 /* The VFS file contains the buffer for the seed now. */
631 rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
632 if (RT_FAILURE(rc))
633 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
634 RTVfsFileRelease(hVfsFile);
635 }
636 else
637 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
638 }
639 else
640 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
641
642 RTVfsIoStrmRelease(hVfsDecomp);
643 }
644
645 RTVfsIoStrmRelease(hVfsIosSeed);
646 }
647 else
648 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
649 }
650 }
651 else
652 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to decode the seed string");
653
654 RTMemFree(pbSeedDecoded);
655 }
656 else
657 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Failed to allocate %zd bytes of memory for the seed", cbSeedDecoded);
658 }
659 else
660 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: Couldn't find \"Seed\" doesn't contain a base64 encoded value");
661
662 return rc;
663}
664
665
666/**
667 * Processes a signle input seed for the given fuzzing run.
668 *
669 * @returns IPRT status code.
670 * @param pFuzzRun The fuzzing run.
671 * @param hJsonSeed The seed node of the JSON request.
672 * @param pErrInfo Where to store the error information on failure, optional.
673 */
674static int rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
675{
676 RTFUZZCTX hFuzzCtx;
677 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
678 if (RT_SUCCESS(rc))
679 {
680 RTJSONVAL hJsonValComp;
681 rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
682 if (RT_SUCCESS(rc))
683 {
684 const char *pszCompression = RTJsonValueGetString(hJsonValComp);
685 if (RT_LIKELY(pszCompression))
686 {
687 RTJSONVAL hJsonValSeed;
688 rc = RTJsonValueQueryByName(hJsonSeed, "Seed", &hJsonValSeed);
689 if (RT_SUCCESS(rc))
690 {
691 const char *pszSeed = RTJsonValueGetString(hJsonValSeed);
692 if (RT_LIKELY(pszSeed))
693 rc = rtFuzzCmdMasterFuzzRunProcessSeed(hFuzzCtx, pszCompression, pszSeed, pErrInfo);
694 else
695 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Seed\" value is not a string");
696
697 RTJsonValueRelease(hJsonValSeed);
698 }
699 else
700 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Seed\" value");
701 }
702 else
703 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
704
705 RTJsonValueRelease(hJsonValComp);
706 }
707 else
708 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
709
710 RTFuzzCtxRelease(hFuzzCtx);
711 }
712 else
713 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
714
715 return rc;
716}
717
718
719/**
720 * Processes the given seed file and adds it to the input corpus.
721 *
722 * @returns IPRT status code.
723 * @param hFuzzCtx The fuzzing context handle.
724 * @param pszCompression Compression used for the seed.
725 * @param pszSeed The seed as a base64 encoded string.
726 * @param pErrInfo Where to store the error information on failure, optional.
727 */
728static int rtFuzzCmdMasterFuzzRunProcessSeedFile(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszFile, PRTERRINFO pErrInfo)
729{
730 int rc = VINF_SUCCESS;
731
732 /* Decompress if applicable. */
733 if (!RTStrICmp(pszCompression, "None"))
734 rc = RTFuzzCtxCorpusInputAddFromFile(hFuzzCtx, pszFile);
735 else
736 {
737 RTVFSIOSTREAM hVfsIosSeed;
738 rc = RTVfsIoStrmOpenNormal(pszFile, RTFILE_O_OPEN | RTFILE_O_READ, &hVfsIosSeed);
739 if (RT_SUCCESS(rc))
740 {
741 RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
742
743 if (!RTStrICmp(pszCompression, "Gzip"))
744 rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
745 else
746 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
747
748 if (RT_SUCCESS(rc))
749 {
750 RTVFSFILE hVfsFile;
751 rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
752 if (RT_SUCCESS(rc))
753 {
754 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
755 if (RT_SUCCESS(rc))
756 {
757 /* The VFS file contains the buffer for the seed now. */
758 rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
759 if (RT_FAILURE(rc))
760 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
761 RTVfsFileRelease(hVfsFile);
762 }
763 else
764 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
765 }
766 else
767 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
768
769 RTVfsIoStrmRelease(hVfsDecomp);
770 }
771
772 RTVfsIoStrmRelease(hVfsIosSeed);
773 }
774 else
775 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
776 }
777
778 return rc;
779}
780
781
782/**
783 * Processes a signle input seed given as a file path for the given fuzzing run.
784 *
785 * @returns IPRT status code.
786 * @param pFuzzRun The fuzzing run.
787 * @param hJsonSeed The seed node of the JSON request.
788 * @param pErrInfo Where to store the error information on failure, optional.
789 */
790static int rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
791{
792 RTFUZZCTX hFuzzCtx;
793 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
794 if (RT_SUCCESS(rc))
795 {
796 RTJSONVAL hJsonValComp;
797 rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
798 if (RT_SUCCESS(rc))
799 {
800 const char *pszCompression = RTJsonValueGetString(hJsonValComp);
801 if (RT_LIKELY(pszCompression))
802 {
803 RTJSONVAL hJsonValFile;
804 rc = RTJsonValueQueryByName(hJsonSeed, "File", &hJsonValFile);
805 if (RT_SUCCESS(rc))
806 {
807 const char *pszFile = RTJsonValueGetString(hJsonValFile);
808 if (RT_LIKELY(pszFile))
809 rc = rtFuzzCmdMasterFuzzRunProcessSeedFile(hFuzzCtx, pszCompression, pszFile, pErrInfo);
810 else
811 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"File\" value is not a string");
812
813 RTJsonValueRelease(hJsonValFile);
814 }
815 else
816 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"File\" value");
817 }
818 else
819 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
820
821 RTJsonValueRelease(hJsonValComp);
822 }
823 else
824 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
825
826 RTFuzzCtxRelease(hFuzzCtx);
827 }
828 else
829 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
830
831 return rc;
832}
833
834
835/**
836 * Processes input seed related configs for the given fuzzing run.
837 *
838 * @returns IPRT status code.
839 * @param pFuzzRun The fuzzing run.
840 * @param hJsonRoot The root node of the JSON request.
841 * @param pErrInfo Where to store the error information on failure, optional.
842 */
843static int rtFuzzCmdMasterFuzzRunProcessInputSeeds(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
844{
845 RTJSONVAL hJsonValSeedArray;
846 int rc = RTJsonValueQueryByName(hJsonRoot, "InputSeeds", &hJsonValSeedArray);
847 if (RT_SUCCESS(rc))
848 {
849 RTJSONIT hIt;
850 rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
851 if (RT_SUCCESS(rc))
852 {
853 RTJSONVAL hJsonInpSeed;
854 while ( RT_SUCCESS(rc)
855 && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
856 {
857 rc = rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
858 RTJsonValueRelease(hJsonInpSeed);
859 if (RT_FAILURE(rc))
860 break;
861 rc = RTJsonIteratorNext(hIt);
862 }
863
864 if (rc == VERR_JSON_ITERATOR_END)
865 rc = VINF_SUCCESS;
866 }
867 else
868 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
869
870 RTJsonValueRelease(hJsonValSeedArray);
871 }
872 else if (rc == VERR_NOT_FOUND)
873 rc = VINF_SUCCESS;
874
875 if (RT_SUCCESS(rc))
876 {
877 rc = RTJsonValueQueryByName(hJsonRoot, "InputSeedFiles", &hJsonValSeedArray);
878 if (RT_SUCCESS(rc))
879 {
880 RTJSONIT hIt;
881 rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
882 if (RT_SUCCESS(rc))
883 {
884 RTJSONVAL hJsonInpSeed;
885 while ( RT_SUCCESS(rc)
886 && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
887 {
888 rc = rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
889 RTJsonValueRelease(hJsonInpSeed);
890 if (RT_FAILURE(rc))
891 break;
892 rc = RTJsonIteratorNext(hIt);
893 }
894
895 if (rc == VERR_JSON_ITERATOR_END)
896 rc = VINF_SUCCESS;
897 }
898 else
899 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
900
901 RTJsonValueRelease(hJsonValSeedArray);
902 }
903 else if (rc == VERR_NOT_FOUND)
904 rc = VINF_SUCCESS;
905 }
906
907 return rc;
908}
909
910
911/**
912 * Processes miscellaneous config items.
913 *
914 * @returns IPRT status code.
915 * @param pFuzzRun The fuzzing run.
916 * @param hJsonRoot The root node of the JSON request.
917 * @param pErrInfo Where to store the error information on failure, optional.
918 */
919static int rtFuzzCmdMasterFuzzRunProcessMiscCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
920{
921 size_t cbTmp;
922 int rc = rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(&cbTmp, "InputSeedMax", hJsonRoot, 0, pErrInfo);
923 if (RT_SUCCESS(rc))
924 {
925 RTFUZZCTX hFuzzCtx;
926 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
927 AssertRC(rc);
928
929 rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbTmp);
930 if (RT_FAILURE(rc))
931 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set maximum input seed size to %zu", cbTmp);
932 }
933
934 if (RT_SUCCESS(rc))
935 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, 0, pErrInfo);
936 if (RT_SUCCESS(rc))
937 {
938 uint32_t msTimeoutMax = 0;
939 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&msTimeoutMax, "TimeoutMax", hJsonRoot, 1000, pErrInfo);
940 if (RT_SUCCESS(rc))
941 rc = RTFuzzObsSetTestBinaryTimeout(pFuzzRun->hFuzzObs, msTimeoutMax);
942 }
943
944 return rc;
945}
946
947
948/**
949 * Processes target recording related configs for the given fuzzing run.
950 *
951 * @returns IPRT status code.
952 * @param pFuzzRun The fuzzing run.
953 * @param hJsonRoot The root node of the JSON request.
954 * @param pErrInfo Where to store the error information on failure, optional.
955 */
956static int rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
957{
958 RTJSONVAL hJsonValTgt;
959 int rc = RTJsonValueQueryByName(hJsonRoot, "TgtRec", &hJsonValTgt);
960 if (RT_SUCCESS(rc))
961 {
962 uint32_t fTgtRecFlags = 0;
963 RTJSONIT hTgtIt;
964 rc = RTJsonIteratorBeginArray(hJsonValTgt, &hTgtIt);
965 if (RT_SUCCESS(rc))
966 {
967 do
968 {
969 RTJSONVAL hVal;
970 rc = RTJsonIteratorQueryValue(hTgtIt, &hVal, NULL);
971 if (RT_SUCCESS(rc))
972 {
973 const char *pszTgtRec = RTJsonValueGetString(hVal);
974 if (!RTStrICmp(pszTgtRec, "StdOut"))
975 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDOUT;
976 else if (!RTStrICmp(pszTgtRec, "StdErr"))
977 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDERR;
978 else if (!RTStrICmp(pszTgtRec, "ProcSts"))
979 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_PROCSTATUS;
980 else if (!RTStrICmp(pszTgtRec, "SanCov"))
981 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_SANCOV;
982 else
983 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The recording flags '%s' is not known", pszTgtRec);
984 RTJsonValueRelease(hVal);
985 }
986 rc = RTJsonIteratorNext(hTgtIt);
987 } while (RT_SUCCESS(rc));
988
989 if ( rc == VERR_JSON_IS_EMPTY
990 || rc == VERR_JSON_ITERATOR_END)
991 rc = VINF_SUCCESS;
992 else
993 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse target recording flags");
994
995 RTJsonIteratorFree(hTgtIt);
996 }
997 else if (rc == VERR_JSON_IS_EMPTY)
998 rc = VINF_SUCCESS;
999 else
1000 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"TgtRec\" is not an array");
1001
1002 pFuzzRun->fTgtRecFlags = fTgtRecFlags;
1003
1004 RTJsonValueRelease(hJsonValTgt);
1005 }
1006 else if (rc == VERR_NOT_FOUND)
1007 {
1008 pFuzzRun->fTgtRecFlags = RTFUZZTGT_REC_STATE_F_PROCSTATUS;
1009 rc = VINF_SUCCESS; /* Just keep using the defaults. */
1010 }
1011 else
1012 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"TgtRec\"");
1013
1014 return rc;
1015}
1016
1017
1018/**
1019 * Sets up the directories for the given fuzzing run.
1020 *
1021 * @returns IPRT status code.
1022 * @param pThis The fuzzing master command state.
1023 * @param pFuzzRun The fuzzing run to setup the directories for.
1024 * @param pErrInfo Where to store the error information on failure, optional.
1025 */
1026static int rtFuzzCmdMasterFuzzRunSetupDirectories(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun, PRTERRINFO pErrInfo)
1027{
1028 /* Create temp directories. */
1029 char szTmpDir[RTPATH_MAX];
1030 int rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszTmpDir, pFuzzRun->pszId);
1031 AssertRC(rc);
1032 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
1033 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1034 if (rc == VERR_ALREADY_EXISTS)
1035 {
1036 /* Clear the directory. */
1037 rc = RTDirRemoveRecursive(szTmpDir, RTDIRRMREC_F_CONTENT_ONLY);
1038 }
1039
1040 if (RT_SUCCESS(rc))
1041 {
1042 rc = RTFuzzObsSetTmpDirectory(pFuzzRun->hFuzzObs, szTmpDir);
1043 if (RT_SUCCESS(rc))
1044 {
1045 rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszResultsDir, pFuzzRun->pszId);
1046 AssertRC(rc);
1047 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
1048 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1049 if (RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS)
1050 {
1051 rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
1052 if (RT_FAILURE(rc))
1053 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set results directory to %s", szTmpDir);
1054 }
1055 else
1056 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create results directory %s", szTmpDir);
1057 }
1058 else
1059 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set temporary directory to %s", szTmpDir);
1060 }
1061 else
1062 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create temporary directory %s", szTmpDir);
1063
1064 return rc;
1065}
1066
1067
1068/**
1069 * Creates a new fuzzing run with the given ID.
1070 *
1071 * @returns IPRT status code.
1072 * @param pThis The fuzzing master command state.
1073 * @param pszId The ID to use.
1074 * @param hJsonRoot The root node of the JSON request.
1075 * @param pErrInfo Where to store the error information on failure, optional.
1076 */
1077static int rtFuzzCmdMasterCreateFuzzRunWithId(PRTFUZZCMDMASTER pThis, const char *pszId, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1078{
1079 int rc = VINF_SUCCESS;
1080 PRTFUZZRUN pFuzzRun = (PRTFUZZRUN)RTMemAllocZ(sizeof(*pFuzzRun));
1081 if (RT_LIKELY(pFuzzRun))
1082 {
1083 pFuzzRun->pszId = RTStrDup(pszId);
1084 if (RT_LIKELY(pFuzzRun->pszId))
1085 {
1086 rc = rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(pFuzzRun, hJsonRoot, pErrInfo);
1087 if (RT_SUCCESS(rc))
1088 {
1089 rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs, RTFUZZCTXTYPE_BLOB, pFuzzRun->fTgtRecFlags);
1090 if (RT_SUCCESS(rc))
1091 {
1092 rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
1093 if (RT_SUCCESS(rc))
1094 rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
1095 if (RT_SUCCESS(rc))
1096 rc = rtFuzzCmdMasterFuzzRunProcessEnvironment(pFuzzRun, hJsonRoot, pErrInfo);
1097 if (RT_SUCCESS(rc))
1098 rc = rtFuzzCmdMasterFuzzRunProcessInputSeeds(pFuzzRun, hJsonRoot, pErrInfo);
1099 if (RT_SUCCESS(rc))
1100 rc = rtFuzzCmdMasterFuzzRunProcessMiscCfg(pFuzzRun, hJsonRoot, pErrInfo);
1101 if (RT_SUCCESS(rc))
1102 rc = rtFuzzCmdMasterFuzzRunProcessSanitizers(pFuzzRun, hJsonRoot, pErrInfo);
1103 if (RT_SUCCESS(rc))
1104 rc = rtFuzzCmdMasterFuzzRunSetupDirectories(pThis, pFuzzRun, pErrInfo);
1105
1106 if (RT_SUCCESS(rc))
1107 {
1108 /* Start fuzzing. */
1109 RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
1110 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1111 if (RT_SUCCESS(rc))
1112 {
1113 RTTIMESPEC TimeSpec;
1114 RTTimeNow(&TimeSpec);
1115 RTTimeLocalExplode(&pFuzzRun->TimeCreated, &TimeSpec);
1116 pFuzzRun->tsCreatedMs = RTTimeMilliTS();
1117 pFuzzRun->fStarted = true;
1118 return VINF_SUCCESS;
1119 }
1120 else
1121 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
1122 }
1123
1124 int rc2 = RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
1125 AssertRC(rc2); RT_NOREF(rc2);
1126 }
1127 }
1128
1129 RTStrFree(pFuzzRun->pszId);
1130 pFuzzRun->pszId = NULL;
1131 }
1132 else
1133 rc = VERR_NO_STR_MEMORY;
1134
1135 RTMemFree(pFuzzRun);
1136 }
1137 else
1138 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Out of memory allocating the fuzzer state");
1139
1140 return rc;
1141}
1142
1143
1144/**
1145 * Resolves the fuzzing run from the given ID config item and the given JSON request.
1146 *
1147 * @returns IPRT status code.
1148 * @param pThis The fuzzing master command state.
1149 * @param hJsonRoot The root node of the JSON request.
1150 * @param pszIdItem The JSON item which contains the ID of the fuzzing run.
1151 * @param ppFuzzRun Where to store the pointer to the fuzzing run on success.
1152 */
1153static int rtFuzzCmdMasterQueryFuzzRunFromJson(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, const char *pszIdItem, PRTERRINFO pErrInfo,
1154 PRTFUZZRUN *ppFuzzRun)
1155{
1156 RTJSONVAL hJsonValId;
1157 int rc = RTJsonValueQueryByName(hJsonRoot, pszIdItem, &hJsonValId);
1158 if (RT_SUCCESS(rc))
1159 {
1160 const char *pszId = RTJsonValueGetString(hJsonValId);
1161 if (pszId)
1162 {
1163 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
1164 if (pFuzzRun)
1165 *ppFuzzRun = pFuzzRun;
1166 else
1167 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "Request error: The ID \"%s\" wasn't found", pszId);
1168 }
1169 else
1170 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
1171
1172 RTJsonValueRelease(hJsonValId);
1173 }
1174 else
1175 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
1176 return rc;
1177}
1178
1179
1180/**
1181 * Processes the "StartFuzzing" request.
1182 *
1183 * @returns IPRT status code.
1184 * @param pThis The fuzzing master command state.
1185 * @param hJsonRoot The root node of the JSON request.
1186 * @param pErrInfo Where to store the error information on failure, optional.
1187 */
1188static int rtFuzzCmdMasterProcessJsonReqStart(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1189{
1190 RTJSONVAL hJsonValId;
1191 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
1192 if (RT_SUCCESS(rc))
1193 {
1194 const char *pszId = RTJsonValueGetString(hJsonValId);
1195 if (pszId)
1196 {
1197 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
1198 if (!pFuzzRun)
1199 rc = rtFuzzCmdMasterCreateFuzzRunWithId(pThis, pszId, hJsonRoot, pErrInfo);
1200 else
1201 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_ALREADY_EXISTS, "Request error: The ID \"%s\" is already registered", pszId);
1202 }
1203 else
1204 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
1205
1206 RTJsonValueRelease(hJsonValId);
1207 }
1208 else
1209 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
1210 return rc;
1211}
1212
1213
1214/**
1215 * Processes the "StopFuzzing" request.
1216 *
1217 * @returns IPRT status code.
1218 * @param pThis The fuzzing master command state.
1219 * @param hJsonValRoot The root node of the JSON request.
1220 * @param pErrInfo Where to store the error information on failure, optional.
1221 */
1222static int rtFuzzCmdMasterProcessJsonReqStop(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1223{
1224 PRTFUZZRUN pFuzzRun;
1225 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1226 if (RT_SUCCESS(rc))
1227 {
1228 RTListNodeRemove(&pFuzzRun->NdFuzzed);
1229 RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1230 RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
1231 RTStrFree(pFuzzRun->pszId);
1232 RTMemFree(pFuzzRun);
1233 }
1234
1235 return rc;
1236}
1237
1238
1239/**
1240 * Processes the "SuspendFuzzing" request.
1241 *
1242 * @returns IPRT status code.
1243 * @param pThis The fuzzing master command state.
1244 * @param hJsonValRoot The root node of the JSON request.
1245 * @param pErrInfo Where to store the error information on failure, optional.
1246 */
1247static int rtFuzzCmdMasterProcessJsonReqSuspend(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1248{
1249 PRTFUZZRUN pFuzzRun;
1250 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1251 if (RT_SUCCESS(rc))
1252 {
1253 if (pFuzzRun->fStarted)
1254 {
1255 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1256 if (RT_SUCCESS(rc))
1257 pFuzzRun->fStarted = false;
1258 else
1259 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
1260 }
1261 }
1262
1263 return rc;
1264}
1265
1266
1267/**
1268 * Processes the "ResumeFuzzing" request.
1269 *
1270 * @returns IPRT status code.
1271 * @param pThis The fuzzing master command state.
1272 * @param hJsonValRoot The root node of the JSON request.
1273 * @param pErrInfo Where to store the error information on failure, optional.
1274 */
1275static int rtFuzzCmdMasterProcessJsonReqResume(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1276{
1277 PRTFUZZRUN pFuzzRun;
1278 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1279 if (RT_SUCCESS(rc))
1280 {
1281 if (!pFuzzRun->fStarted)
1282 {
1283 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, pFuzzRun->cProcs, pErrInfo);
1284 if (RT_SUCCESS(rc))
1285 {
1286 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1287 if (RT_SUCCESS(rc))
1288 pFuzzRun->fStarted = true;
1289 else
1290 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Resuming the fuzzing process failed");
1291 }
1292 }
1293 }
1294
1295 return rc;
1296}
1297
1298
1299/**
1300 * Processes the "SaveFuzzingState" request.
1301 *
1302 * @returns IPRT status code.
1303 * @param pThis The fuzzing master command state.
1304 * @param hJsonValRoot The root node of the JSON request.
1305 * @param pErrInfo Where to store the error information on failure, optional.
1306 */
1307static int rtFuzzCmdMasterProcessJsonReqSaveState(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1308{
1309 PRTFUZZRUN pFuzzRun;
1310 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1311 if (RT_SUCCESS(rc))
1312 {
1313 /* Suspend fuzzing, save and resume if not stopped. */
1314 if (pFuzzRun->fStarted)
1315 {
1316 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1317 if (RT_FAILURE(rc))
1318 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
1319 }
1320
1321 if (RT_SUCCESS(rc))
1322 {
1323 RTFUZZCTX hFuzzCtx;
1324 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
1325 AssertRC(rc);
1326
1327 void *pvState = NULL;
1328 size_t cbState = 0;
1329 rc = RTFuzzCtxStateExportToMem(hFuzzCtx, &pvState, &cbState);
1330 if (RT_SUCCESS(rc))
1331 {
1332 /* Encode to base64. */
1333 size_t cbStateStr = RTBase64EncodedLength(cbState) + 1;
1334 char *pszState = (char *)RTMemAllocZ(cbStateStr);
1335 if (pszState)
1336 {
1337 rc = RTBase64Encode(pvState, cbState, pszState, cbStateStr, &cbStateStr);
1338 if (RT_SUCCESS(rc))
1339 {
1340 /* Strip all new lines from the srting. */
1341 size_t offStr = 0;
1342 while (offStr < cbStateStr)
1343 {
1344#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1345 char *pszEol = strchr(&pszState[offStr], '\r');
1346#else
1347 char *pszEol = strchr(&pszState[offStr], '\n');
1348#endif
1349 if (pszEol)
1350 {
1351 offStr += pszEol - &pszState[offStr];
1352 memmove(pszEol, &pszEol[RTBASE64_EOL_SIZE], cbStateStr - offStr - RTBASE64_EOL_SIZE);
1353 cbStateStr -= RTBASE64_EOL_SIZE;
1354 }
1355 else
1356 break;
1357 }
1358
1359 const char s_szState[] = "{ \"State\": %s }";
1360 pThis->pszResponse = RTStrAPrintf2(s_szState, pszState);
1361 if (RT_UNLIKELY(!pThis->pszResponse))
1362 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
1363 }
1364 else
1365 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to encode the state as a base64 string");
1366 RTMemFree(pszState);
1367 }
1368 else
1369 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "Request error: Failed to allocate a state string for the response");
1370 RTMemFree(pvState);
1371 }
1372 else
1373 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Exporting the state failed");
1374 }
1375
1376 if (pFuzzRun->fStarted)
1377 {
1378 int rc2 = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1379 if (RT_FAILURE(rc2))
1380 rtFuzzCmdMasterErrorRc(pErrInfo, rc2, "Request error: Resuming the fuzzing process failed");
1381 }
1382 }
1383
1384 return rc;
1385}
1386
1387
1388/**
1389 * Queries the statistics for the given fuzzing run and adds the result to the response.
1390 *
1391 * @returns IPRT static code.
1392 * @param pThis The fuzzing master command state.
1393 * @param pFuzzRun The fuzzing run.
1394 * @param pszIndent Indentation to use.
1395 * @param fLast Flags whether this is the last element in the list.
1396 * @param pErrInfo Where to store the error information on failure, optional.
1397 */
1398static int rtFuzzCmdMasterProcessQueryRunStats(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun,
1399 const char *pszIndent, bool fLast, PRTERRINFO pErrInfo)
1400{
1401 RTFUZZOBSSTATS ObsStats;
1402 RTFUZZCTXSTATS CtxStats;
1403 RTFUZZCTX hFuzzCtx;
1404 RT_ZERO(ObsStats); RT_ZERO(CtxStats);
1405
1406 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
1407 if (RT_SUCCESS(rc))
1408 {
1409 rc = RTFuzzCtxQueryStats(hFuzzCtx, &CtxStats);
1410 RTFuzzCtxRelease(hFuzzCtx);
1411 }
1412
1413 if (RT_SUCCESS(rc))
1414 rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &ObsStats);
1415 if (RT_SUCCESS(rc))
1416 {
1417 const char s_szStatsFmt[] = "%s{ \n"
1418 "%s \"Id\": \"%s\"\n"
1419 "%s \"TimeCreated\": \"%s\"\n"
1420 "%s \"UptimeSec\": %llu\n"
1421 "%s \"FuzzedInputsPerSec\": %u\n"
1422 "%s \"FuzzedInputs\": %u\n"
1423 "%s \"FuzzedInputsHang\": %u\n"
1424 "%s \"FuzzedInputsCrash\": %u\n"
1425 "%s \"MemoryUsage\": %zu\n"
1426 "%s \"CorpusSize\": %llu\n"
1427 "%s}%s\n";
1428 char aszTime[_1K]; RT_ZERO(aszTime);
1429 char aszStats[_4K]; RT_ZERO(aszStats);
1430
1431 if (RTTimeToString(&pFuzzRun->TimeCreated, aszTime, sizeof(aszTime)))
1432 {
1433 ssize_t cchStats = RTStrPrintf2(&aszStats[0], sizeof(aszStats),
1434 s_szStatsFmt, pszIndent,
1435 pszIndent, pFuzzRun->pszId,
1436 pszIndent, aszTime,
1437 pszIndent, (RTTimeMilliTS() - pFuzzRun->tsCreatedMs) / RT_MS_1SEC_64,
1438 pszIndent, ObsStats.cFuzzedInputsPerSec,
1439 pszIndent, ObsStats.cFuzzedInputs,
1440 pszIndent, ObsStats.cFuzzedInputsHang,
1441 pszIndent, ObsStats.cFuzzedInputsCrash,
1442 pszIndent, CtxStats.cbMemory,
1443 pszIndent, CtxStats.cMutations,
1444 pszIndent, fLast ? "" : ",");
1445 if (RT_LIKELY(cchStats > 0))
1446 {
1447 rc = RTStrAAppend(&pThis->pszResponse, &aszStats[0]);
1448 if (RT_FAILURE(rc))
1449 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to build statistics response", rc);
1450 }
1451 else
1452 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow");
1453 }
1454 else
1455 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Buffer overflow conerting time to string");
1456 }
1457 else
1458 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
1459
1460 return rc;
1461}
1462
1463
1464/**
1465 * Processes the "QueryStats" request.
1466 *
1467 * @returns IPRT status code.
1468 * @param pThis The fuzzing master command state.
1469 * @param hJsonValRoot The root node of the JSON request.
1470 * @param pErrInfo Where to store the error information on failure, optional.
1471 */
1472static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1473{
1474 RTJSONVAL hJsonValId;
1475 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
1476 if (RT_SUCCESS(rc))
1477 {
1478 RTJsonValueRelease(hJsonValId);
1479 PRTFUZZRUN pFuzzRun;
1480 rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1481 if (RT_SUCCESS(rc))
1482 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pFuzzRun, " ",
1483 true /*fLast*/, pErrInfo);
1484 }
1485 else if (rc == VERR_NOT_FOUND)
1486 {
1487 /* Id is not there, so collect statistics of all running jobs. */
1488 rc = RTStrAAppend(&pThis->pszResponse, " [\n");
1489 if (RT_SUCCESS(rc))
1490 {
1491 PRTFUZZRUN pRun = NULL;
1492 RTListForEach(&pThis->LstFuzzed, pRun, RTFUZZRUN, NdFuzzed)
1493 {
1494 bool fLast = RTListNodeIsLast(&pThis->LstFuzzed, &pRun->NdFuzzed);
1495 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pRun, " ", fLast, pErrInfo);
1496 if (RT_FAILURE(rc))
1497 break;
1498 }
1499 if (RT_SUCCESS(rc))
1500 rc = RTStrAAppend(&pThis->pszResponse, " ]\n");
1501 }
1502 }
1503 else
1504 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't get \"Id\" value");
1505
1506 return rc;
1507}
1508
1509
1510/**
1511 * Processes a JSON request.
1512 *
1513 * @returns IPRT status code.
1514 * @param pThis The fuzzing master command state.
1515 * @param hJsonValRoot The root node of the JSON request.
1516 * @param pErrInfo Where to store the error information on failure, optional.
1517 */
1518static int rtFuzzCmdMasterProcessJsonReq(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1519{
1520 RTJSONVAL hJsonValReq;
1521 int rc = RTJsonValueQueryByName(hJsonRoot, "Request", &hJsonValReq);
1522 if (RT_SUCCESS(rc))
1523 {
1524 const char *pszReq = RTJsonValueGetString(hJsonValReq);
1525 if (pszReq)
1526 {
1527 if (!RTStrCmp(pszReq, "StartFuzzing"))
1528 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, pErrInfo);
1529 else if (!RTStrCmp(pszReq, "StopFuzzing"))
1530 rc = rtFuzzCmdMasterProcessJsonReqStop(pThis, hJsonRoot, pErrInfo);
1531 else if (!RTStrCmp(pszReq, "SuspendFuzzing"))
1532 rc = rtFuzzCmdMasterProcessJsonReqSuspend(pThis, hJsonRoot, pErrInfo);
1533 else if (!RTStrCmp(pszReq, "ResumeFuzzing"))
1534 rc = rtFuzzCmdMasterProcessJsonReqResume(pThis, hJsonRoot, pErrInfo);
1535 else if (!RTStrCmp(pszReq, "SaveFuzzingState"))
1536 rc = rtFuzzCmdMasterProcessJsonReqSaveState(pThis, hJsonRoot, pErrInfo);
1537 else if (!RTStrCmp(pszReq, "QueryStats"))
1538 rc = rtFuzzCmdMasterProcessJsonReqQueryStats(pThis, hJsonRoot, pErrInfo);
1539 else if (!RTStrCmp(pszReq, "Shutdown"))
1540 pThis->fShutdown = true;
1541 else
1542 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" contains unknown value \"%s\"", pszReq);
1543 }
1544 else
1545 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" is not a string value");
1546
1547 RTJsonValueRelease(hJsonValReq);
1548 }
1549 else
1550 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Request\" value");
1551
1552 return rc;
1553}
1554
1555
1556/**
1557 * Loads a fuzzing configuration for immediate startup from the given file.
1558 *
1559 * @returns IPRT status code.
1560 * @param pThis The fuzzing master command state.
1561 * @param pszFuzzCfg The fuzzing config to load.
1562 */
1563static int rtFuzzCmdMasterFuzzCfgLoadFromFile(PRTFUZZCMDMASTER pThis, const char *pszFuzzCfg)
1564{
1565 RTJSONVAL hJsonRoot;
1566 int rc = RTJsonParseFromFile(&hJsonRoot, pszFuzzCfg, NULL);
1567 if (RT_SUCCESS(rc))
1568 {
1569 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, NULL);
1570 RTJsonValueRelease(hJsonRoot);
1571 }
1572 else
1573 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "JSON request malformed: Couldn't load file \"%s\"", pszFuzzCfg);
1574
1575 return rc;
1576}
1577
1578
1579/**
1580 * Destroys all running fuzzers for the given master state.
1581 *
1582 * @param pThis The fuzzing master command state.
1583 */
1584static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
1585{
1586 RT_NOREF(pThis);
1587}
1588
1589
1590/**
1591 * Sends an ACK response to the client.
1592 *
1593 * @param hSocket The socket handle to send the ACK to.
1594 * @param pszResponse Additional response data.
1595 */
1596static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
1597{
1598 const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
1599 const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n \"Response\":\n";
1600 const char s_szSuccRespClose[] = "\n }\n";
1601 if (pszResponse)
1602 {
1603 RTSGSEG aSegs[3];
1604 RTSGBUF SgBuf;
1605 aSegs[0].pvSeg = (void *)s_szSuccResp;
1606 aSegs[0].cbSeg = sizeof(s_szSuccResp) - 1;
1607 aSegs[1].pvSeg = (void *)pszResponse;
1608 aSegs[1].cbSeg = strlen(pszResponse);
1609 aSegs[2].pvSeg = (void *)s_szSuccRespClose;
1610 aSegs[2].cbSeg = sizeof(s_szSuccRespClose) - 1;
1611
1612 RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
1613 RTTcpSgWrite(hSocket, &SgBuf);
1614 }
1615 else
1616 RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
1617}
1618
1619
1620/**
1621 * Sends an NACK response to the client.
1622 *
1623 * @param hSocket The socket handle to send the ACK to.
1624 * @param pErrInfo Optional error information to send along.
1625 */
1626static void rtFuzzCmdMasterTcpSendNAck(RTSOCKET hSocket, PRTERRINFO pErrInfo)
1627{
1628 const char s_szFail[] = "{ \"Status\": \"NACK\" }\n";
1629 const char s_szFailInfo[] = "{ \"Status\": \"NACK\"\n \"Information\": \"%s\" }\n";
1630
1631 if (pErrInfo)
1632 {
1633 char szTmp[_1K];
1634 ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
1635 if (cchResp > 0)
1636 RTTcpWrite(hSocket, szTmp, cchResp);
1637 else
1638 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1639 }
1640 else
1641 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1642}
1643
1644
1645/**
1646 * TCP server serving callback for a single connection.
1647 *
1648 * @returns IPRT status code.
1649 * @param hSocket The socket handle of the connection.
1650 * @param pvUser Opaque user data.
1651 */
1652static DECLCALLBACK(int) rtFuzzCmdMasterTcpServe(RTSOCKET hSocket, void *pvUser)
1653{
1654 PRTFUZZCMDMASTER pThis = (PRTFUZZCMDMASTER)pvUser;
1655 size_t cbReqMax = _32K;
1656 size_t cbReq = 0;
1657 uint8_t *pbReq = (uint8_t *)RTMemAllocZ(cbReqMax);
1658
1659 if (RT_LIKELY(pbReq))
1660 {
1661 uint8_t *pbCur = pbReq;
1662
1663 for (;;)
1664 {
1665 size_t cbThisRead = cbReqMax - cbReq;
1666 int rc = RTTcpRead(hSocket, pbCur, cbThisRead, &cbThisRead);
1667 if ( RT_SUCCESS(rc)
1668 && cbThisRead)
1669 {
1670 cbReq += cbThisRead;
1671
1672 /* Check for a zero terminator marking the end of the request. */
1673 uint8_t *pbEnd = (uint8_t *)memchr(pbCur, 0, cbThisRead);
1674 if (pbEnd)
1675 {
1676 /* Adjust request size, data coming after the zero terminiator is ignored right now. */
1677 cbReq -= cbThisRead - (pbEnd - pbCur) + 1;
1678
1679 RTJSONVAL hJsonReq;
1680 RTERRINFOSTATIC ErrInfo;
1681 RTErrInfoInitStatic(&ErrInfo);
1682
1683 rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
1684 if (RT_SUCCESS(rc))
1685 {
1686 rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
1687 if (RT_SUCCESS(rc))
1688 rtFuzzCmdMasterTcpSendAck(hSocket, pThis->pszResponse);
1689 else
1690 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1691 RTJsonValueRelease(hJsonReq);
1692 }
1693 else
1694 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1695
1696 if (pThis->pszResponse)
1697 {
1698 RTStrFree(pThis->pszResponse);
1699 pThis->pszResponse = NULL;
1700 }
1701 break;
1702 }
1703 else if (cbReq == cbReqMax)
1704 {
1705 /* Try to increase the buffer. */
1706 uint8_t *pbReqNew = (uint8_t *)RTMemRealloc(pbReq, cbReqMax + _32K);
1707 if (RT_LIKELY(pbReqNew))
1708 {
1709 cbReqMax += _32K;
1710 pbReq = pbReqNew;
1711 pbCur = pbReq + cbReq;
1712 }
1713 else
1714 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1715 }
1716 else
1717 pbCur += cbThisRead;
1718 }
1719 else
1720 break;
1721 }
1722 }
1723 else
1724 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1725
1726 if (pbReq)
1727 RTMemFree(pbReq);
1728
1729 return pThis->fShutdown ? VERR_TCP_SERVER_STOP : VINF_SUCCESS;
1730}
1731
1732
1733/**
1734 * Mainloop for the fuzzing master.
1735 *
1736 * @returns Process exit code.
1737 * @param pThis The fuzzing master command state.
1738 * @param pszLoadCfg Initial config to load.
1739 */
1740static RTEXITCODE rtFuzzCmdMasterRun(PRTFUZZCMDMASTER pThis, const char *pszLoadCfg)
1741{
1742 if (pszLoadCfg)
1743 {
1744 int rc = rtFuzzCmdMasterFuzzCfgLoadFromFile(pThis, pszLoadCfg);
1745 if (RT_FAILURE(rc))
1746 return RTEXITCODE_FAILURE;
1747 }
1748
1749 /* Start up the control server. */
1750 int rc = RTTcpServerCreateEx(NULL, pThis->uPort, &pThis->hTcpSrv);
1751 if (RT_SUCCESS(rc))
1752 {
1753 do
1754 {
1755 rc = RTTcpServerListen(pThis->hTcpSrv, rtFuzzCmdMasterTcpServe, pThis);
1756 } while (rc != VERR_TCP_SERVER_STOP);
1757 }
1758
1759 RTTcpServerDestroy(pThis->hTcpSrv);
1760 rtFuzzCmdMasterDestroy(pThis);
1761 return RTEXITCODE_SUCCESS;
1762}
1763
1764
1765RTR3DECL(RTEXITCODE) RTFuzzCmdMaster(unsigned cArgs, char **papszArgs)
1766{
1767 /*
1768 * Parse the command line.
1769 */
1770 static const RTGETOPTDEF s_aOptions[] =
1771 {
1772 { "--fuzz-config", 'c', RTGETOPT_REQ_STRING },
1773 { "--temp-dir", 't', RTGETOPT_REQ_STRING },
1774 { "--results-dir", 'r', RTGETOPT_REQ_STRING },
1775 { "--listen-port", 'p', RTGETOPT_REQ_UINT16 },
1776 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
1777 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
1778 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1779 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1780 };
1781
1782 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1783 RTGETOPTSTATE GetState;
1784 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1785 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1786 if (RT_SUCCESS(rc))
1787 {
1788 /* Option variables: */
1789 bool fDaemonize = false;
1790 bool fDaemonized = false;
1791 const char *pszLoadCfg = NULL;
1792 RTFUZZCMDMASTER This;
1793
1794 RTListInit(&This.LstFuzzed);
1795 This.hTcpSrv = NIL_RTTCPSERVER;
1796 This.uPort = 4242;
1797 This.pszTmpDir = NULL;
1798 This.pszResultsDir = NULL;
1799 This.fShutdown = false;
1800 This.pszResponse = NULL;
1801
1802 /* Argument parsing loop. */
1803 bool fContinue = true;
1804 do
1805 {
1806 RTGETOPTUNION ValueUnion;
1807 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1808 switch (chOpt)
1809 {
1810 case 0:
1811 fContinue = false;
1812 break;
1813
1814 case 'c':
1815 pszLoadCfg = ValueUnion.psz;
1816 break;
1817
1818 case 'p':
1819 This.uPort = ValueUnion.u16;
1820 break;
1821
1822 case 't':
1823 This.pszTmpDir = ValueUnion.psz;
1824 break;
1825
1826 case 'r':
1827 This.pszResultsDir = ValueUnion.psz;
1828 break;
1829
1830 case 'd':
1831 fDaemonize = true;
1832 break;
1833
1834 case 'Z':
1835 fDaemonized = true;
1836 fDaemonize = false;
1837 break;
1838
1839 case 'h':
1840 RTPrintf("Usage: to be written\nOption dump:\n");
1841 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1842 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1843 fContinue = false;
1844 break;
1845
1846 case 'V':
1847 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1848 fContinue = false;
1849 break;
1850
1851 default:
1852 rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
1853 fContinue = false;
1854 break;
1855 }
1856 } while (fContinue);
1857
1858 if (rcExit == RTEXITCODE_SUCCESS)
1859 {
1860 /*
1861 * Daemonize ourselves if asked to.
1862 */
1863 if (fDaemonize)
1864 {
1865 rc = RTProcDaemonize(papszArgs, "--daemonized");
1866 if (RT_FAILURE(rc))
1867 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1868 }
1869 else
1870 rcExit = rtFuzzCmdMasterRun(&This, pszLoadCfg);
1871 }
1872 }
1873 else
1874 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1875 return rcExit;
1876}
1877
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use