VirtualBox

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

Last change on this file since 106580 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.0 KB
Line 
1/* $Id: fuzzmastercmd.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Fuzzing framework API, master command.
4 */
5
6/*
7 * Copyright (C) 2018-2024 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 if (RT_FAILURE(rc))
477 break;
478 rc = RTJsonIteratorNext(hEnvIt);
479 } while (RT_SUCCESS(rc));
480
481 if ( rc == VERR_JSON_IS_EMPTY
482 || rc == VERR_JSON_ITERATOR_END)
483 rc = VINF_SUCCESS;
484 else
485 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse environment");
486
487 RTJsonIteratorFree(hEnvIt);
488 }
489 else if (rc == VERR_JSON_IS_EMPTY)
490 rc = VINF_SUCCESS;
491 else
492 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Environment\" is not an array");
493
494 if (RT_SUCCESS(rc))
495 {
496 rc = RTFuzzObsSetTestBinaryEnv(pFuzzRun->hFuzzObs, hEnv);
497 AssertRC(rc);
498 }
499 else if (hEnv)
500 RTEnvDestroy(hEnv);
501 }
502 else
503 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create environment block");
504 }
505 else
506 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"EnvReplace\"");
507
508 RTJsonValueRelease(hJsonValEnv);
509 }
510 else if (rc == VERR_NOT_FOUND)
511 rc = VINF_SUCCESS; /* Just keep using the default environment. */
512 else
513 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Environment\"");
514
515 return rc;
516}
517
518
519/**
520 * Processes process environment related configs for the given fuzzing run.
521 *
522 * @returns IPRT status code.
523 * @param pFuzzRun The fuzzing run.
524 * @param hJsonRoot The root node of the JSON request.
525 * @param pErrInfo Where to store the error information on failure, optional.
526 */
527static int rtFuzzCmdMasterFuzzRunProcessSanitizers(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
528{
529 RTJSONVAL hJsonValSan;
530 int rc = RTJsonValueQueryByName(hJsonRoot, "Sanitizers", &hJsonValSan);
531 if (RT_SUCCESS(rc))
532 {
533 uint32_t fSanitizers = 0;
534 RTJSONIT hSanIt;
535 rc = RTJsonIteratorBeginArray(hJsonValSan, &hSanIt);
536 if (RT_SUCCESS(rc))
537 {
538 do
539 {
540 RTJSONVAL hVal;
541 rc = RTJsonIteratorQueryValue(hSanIt, &hVal, NULL);
542 if (RT_SUCCESS(rc))
543 {
544 const char *pszSan = RTJsonValueGetString(hVal);
545 if (!RTStrICmp(pszSan, "Asan"))
546 fSanitizers |= RTFUZZOBS_SANITIZER_F_ASAN;
547 else if (!RTStrICmp(pszSan, "SanCov"))
548 fSanitizers |= RTFUZZOBS_SANITIZER_F_SANCOV;
549 else
550 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The sanitizer '%s' is not known", pszSan);
551 RTJsonValueRelease(hVal);
552 }
553 if (RT_FAILURE(rc))
554 break;
555
556 rc = RTJsonIteratorNext(hSanIt);
557 } while (RT_SUCCESS(rc));
558
559 if ( rc == VERR_JSON_IS_EMPTY
560 || rc == VERR_JSON_ITERATOR_END)
561 rc = VINF_SUCCESS;
562 else
563 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse sanitizers");
564
565 RTJsonIteratorFree(hSanIt);
566 }
567 else if (rc == VERR_JSON_IS_EMPTY)
568 rc = VINF_SUCCESS;
569 else
570 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Sanitizers\" is not an array");
571
572 if (RT_SUCCESS(rc))
573 {
574 rc = RTFuzzObsSetTestBinarySanitizers(pFuzzRun->hFuzzObs, fSanitizers);
575 AssertRC(rc);
576 }
577
578 RTJsonValueRelease(hJsonValSan);
579 }
580 else if (rc == VERR_NOT_FOUND)
581 rc = VINF_SUCCESS; /* Just keep using the defaults. */
582 else
583 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query the \"Sanitizers\"");
584
585 return rc;
586}
587
588
589/**
590 * Processes the given seed and adds it to the input corpus.
591 *
592 * @returns IPRT status code.
593 * @param hFuzzCtx The fuzzing context handle.
594 * @param pszCompression Compression used for the seed.
595 * @param pszSeed The seed as a base64 encoded string.
596 * @param pErrInfo Where to store the error information on failure, optional.
597 */
598static int rtFuzzCmdMasterFuzzRunProcessSeed(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszSeed, PRTERRINFO pErrInfo)
599{
600 int rc = VINF_SUCCESS;
601 ssize_t cbSeedDecoded = RTBase64DecodedSize(pszSeed, NULL);
602 if (cbSeedDecoded > 0)
603 {
604 uint8_t *pbSeedDecoded = (uint8_t *)RTMemAllocZ(cbSeedDecoded);
605 if (RT_LIKELY(pbSeedDecoded))
606 {
607 rc = RTBase64Decode(pszSeed, pbSeedDecoded, cbSeedDecoded, NULL, NULL);
608 if (RT_SUCCESS(rc))
609 {
610 /* Decompress if applicable. */
611 if (!RTStrICmp(pszCompression, "None"))
612 rc = RTFuzzCtxCorpusInputAdd(hFuzzCtx, pbSeedDecoded, cbSeedDecoded);
613 else
614 {
615 RTVFSIOSTREAM hVfsIosSeed;
616 rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbSeedDecoded, cbSeedDecoded, &hVfsIosSeed);
617 if (RT_SUCCESS(rc))
618 {
619 RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
620
621 if (!RTStrICmp(pszCompression, "Gzip"))
622 rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
623 else
624 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
625
626 if (RT_SUCCESS(rc))
627 {
628 RTVFSFILE hVfsFile;
629 rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
630 if (RT_SUCCESS(rc))
631 {
632 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
633 if (RT_SUCCESS(rc))
634 {
635 /* The VFS file contains the buffer for the seed now. */
636 rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
637 if (RT_FAILURE(rc))
638 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
639 RTVfsFileRelease(hVfsFile);
640 }
641 else
642 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
643 }
644 else
645 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
646
647 RTVfsIoStrmRelease(hVfsDecomp);
648 }
649
650 RTVfsIoStrmRelease(hVfsIosSeed);
651 }
652 else
653 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
654 }
655 }
656 else
657 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to decode the seed string");
658
659 RTMemFree(pbSeedDecoded);
660 }
661 else
662 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Failed to allocate %zd bytes of memory for the seed", cbSeedDecoded);
663 }
664 else
665 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: Couldn't find \"Seed\" doesn't contain a base64 encoded value");
666
667 return rc;
668}
669
670
671/**
672 * Processes a signle input seed for the given fuzzing run.
673 *
674 * @returns IPRT status code.
675 * @param pFuzzRun The fuzzing run.
676 * @param hJsonSeed The seed node of the JSON request.
677 * @param pErrInfo Where to store the error information on failure, optional.
678 */
679static int rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
680{
681 RTFUZZCTX hFuzzCtx;
682 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
683 if (RT_SUCCESS(rc))
684 {
685 RTJSONVAL hJsonValComp;
686 rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
687 if (RT_SUCCESS(rc))
688 {
689 const char *pszCompression = RTJsonValueGetString(hJsonValComp);
690 if (RT_LIKELY(pszCompression))
691 {
692 RTJSONVAL hJsonValSeed;
693 rc = RTJsonValueQueryByName(hJsonSeed, "Seed", &hJsonValSeed);
694 if (RT_SUCCESS(rc))
695 {
696 const char *pszSeed = RTJsonValueGetString(hJsonValSeed);
697 if (RT_LIKELY(pszSeed))
698 rc = rtFuzzCmdMasterFuzzRunProcessSeed(hFuzzCtx, pszCompression, pszSeed, pErrInfo);
699 else
700 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Seed\" value is not a string");
701
702 RTJsonValueRelease(hJsonValSeed);
703 }
704 else
705 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Seed\" value");
706 }
707 else
708 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
709
710 RTJsonValueRelease(hJsonValComp);
711 }
712 else
713 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
714
715 RTFuzzCtxRelease(hFuzzCtx);
716 }
717 else
718 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
719
720 return rc;
721}
722
723
724/**
725 * Processes the given seed file and adds it to the input corpus.
726 *
727 * @returns IPRT status code.
728 * @param hFuzzCtx The fuzzing context handle.
729 * @param pszCompression Compression used for the seed.
730 * @param pszSeed The seed as a base64 encoded string.
731 * @param pErrInfo Where to store the error information on failure, optional.
732 */
733static int rtFuzzCmdMasterFuzzRunProcessSeedFile(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszFile, PRTERRINFO pErrInfo)
734{
735 int rc = VINF_SUCCESS;
736
737 /* Decompress if applicable. */
738 if (!RTStrICmp(pszCompression, "None"))
739 rc = RTFuzzCtxCorpusInputAddFromFile(hFuzzCtx, pszFile);
740 else
741 {
742 RTVFSIOSTREAM hVfsIosSeed;
743 rc = RTVfsIoStrmOpenNormal(pszFile, RTFILE_O_OPEN | RTFILE_O_READ, &hVfsIosSeed);
744 if (RT_SUCCESS(rc))
745 {
746 RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
747
748 if (!RTStrICmp(pszCompression, "Gzip"))
749 rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
750 else
751 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
752
753 if (RT_SUCCESS(rc))
754 {
755 RTVFSFILE hVfsFile;
756 rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
757 if (RT_SUCCESS(rc))
758 {
759 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
760 if (RT_SUCCESS(rc))
761 {
762 /* The VFS file contains the buffer for the seed now. */
763 rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
764 if (RT_FAILURE(rc))
765 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
766 RTVfsFileRelease(hVfsFile);
767 }
768 else
769 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
770 }
771 else
772 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
773
774 RTVfsIoStrmRelease(hVfsDecomp);
775 }
776
777 RTVfsIoStrmRelease(hVfsIosSeed);
778 }
779 else
780 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
781 }
782
783 return rc;
784}
785
786
787/**
788 * Processes a signle input seed given as a file path for the given fuzzing run.
789 *
790 * @returns IPRT status code.
791 * @param pFuzzRun The fuzzing run.
792 * @param hJsonSeed The seed node of the JSON request.
793 * @param pErrInfo Where to store the error information on failure, optional.
794 */
795static int rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
796{
797 RTFUZZCTX hFuzzCtx;
798 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
799 if (RT_SUCCESS(rc))
800 {
801 RTJSONVAL hJsonValComp;
802 rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
803 if (RT_SUCCESS(rc))
804 {
805 const char *pszCompression = RTJsonValueGetString(hJsonValComp);
806 if (RT_LIKELY(pszCompression))
807 {
808 RTJSONVAL hJsonValFile;
809 rc = RTJsonValueQueryByName(hJsonSeed, "File", &hJsonValFile);
810 if (RT_SUCCESS(rc))
811 {
812 const char *pszFile = RTJsonValueGetString(hJsonValFile);
813 if (RT_LIKELY(pszFile))
814 rc = rtFuzzCmdMasterFuzzRunProcessSeedFile(hFuzzCtx, pszCompression, pszFile, pErrInfo);
815 else
816 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"File\" value is not a string");
817
818 RTJsonValueRelease(hJsonValFile);
819 }
820 else
821 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"File\" value");
822 }
823 else
824 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
825
826 RTJsonValueRelease(hJsonValComp);
827 }
828 else
829 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
830
831 RTFuzzCtxRelease(hFuzzCtx);
832 }
833 else
834 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
835
836 return rc;
837}
838
839
840/**
841 * Processes input seed related configs for the given fuzzing run.
842 *
843 * @returns IPRT status code.
844 * @param pFuzzRun The fuzzing run.
845 * @param hJsonRoot The root node of the JSON request.
846 * @param pErrInfo Where to store the error information on failure, optional.
847 */
848static int rtFuzzCmdMasterFuzzRunProcessInputSeeds(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
849{
850 RTJSONVAL hJsonValSeedArray;
851 int rc = RTJsonValueQueryByName(hJsonRoot, "InputSeeds", &hJsonValSeedArray);
852 if (RT_SUCCESS(rc))
853 {
854 RTJSONIT hIt;
855 rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
856 if (RT_SUCCESS(rc))
857 {
858 RTJSONVAL hJsonInpSeed;
859 while ( RT_SUCCESS(rc)
860 && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
861 {
862 rc = rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
863 RTJsonValueRelease(hJsonInpSeed);
864 if (RT_FAILURE(rc))
865 break;
866 rc = RTJsonIteratorNext(hIt);
867 }
868
869 if (rc == VERR_JSON_ITERATOR_END)
870 rc = VINF_SUCCESS;
871 }
872 else
873 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
874
875 RTJsonValueRelease(hJsonValSeedArray);
876 }
877 else if (rc == VERR_NOT_FOUND)
878 rc = VINF_SUCCESS;
879
880 if (RT_SUCCESS(rc))
881 {
882 rc = RTJsonValueQueryByName(hJsonRoot, "InputSeedFiles", &hJsonValSeedArray);
883 if (RT_SUCCESS(rc))
884 {
885 RTJSONIT hIt;
886 rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
887 if (RT_SUCCESS(rc))
888 {
889 RTJSONVAL hJsonInpSeed;
890 while ( RT_SUCCESS(rc)
891 && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
892 {
893 rc = rtFuzzCmdMasterFuzzRunProcessInputSeedFileSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
894 RTJsonValueRelease(hJsonInpSeed);
895 if (RT_FAILURE(rc))
896 break;
897 rc = RTJsonIteratorNext(hIt);
898 }
899
900 if (rc == VERR_JSON_ITERATOR_END)
901 rc = VINF_SUCCESS;
902 }
903 else
904 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
905
906 RTJsonValueRelease(hJsonValSeedArray);
907 }
908 else if (rc == VERR_NOT_FOUND)
909 rc = VINF_SUCCESS;
910 }
911
912 return rc;
913}
914
915
916/**
917 * Processes miscellaneous config items.
918 *
919 * @returns IPRT status code.
920 * @param pFuzzRun The fuzzing run.
921 * @param hJsonRoot The root node of the JSON request.
922 * @param pErrInfo Where to store the error information on failure, optional.
923 */
924static int rtFuzzCmdMasterFuzzRunProcessMiscCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
925{
926 size_t cbTmp;
927 int rc = rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(&cbTmp, "InputSeedMax", hJsonRoot, 0, pErrInfo);
928 if (RT_SUCCESS(rc))
929 {
930 RTFUZZCTX hFuzzCtx;
931 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
932 AssertRC(rc);
933
934 rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbTmp);
935 if (RT_FAILURE(rc))
936 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set maximum input seed size to %zu", cbTmp);
937 }
938
939 if (RT_SUCCESS(rc))
940 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, 0, pErrInfo);
941 if (RT_SUCCESS(rc))
942 {
943 uint32_t msTimeoutMax = 0;
944 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&msTimeoutMax, "TimeoutMax", hJsonRoot, 1000, pErrInfo);
945 if (RT_SUCCESS(rc))
946 rc = RTFuzzObsSetTestBinaryTimeout(pFuzzRun->hFuzzObs, msTimeoutMax);
947 }
948
949 return rc;
950}
951
952
953/**
954 * Processes target recording related configs for the given fuzzing run.
955 *
956 * @returns IPRT status code.
957 * @param pFuzzRun The fuzzing run.
958 * @param hJsonRoot The root node of the JSON request.
959 * @param pErrInfo Where to store the error information on failure, optional.
960 */
961static int rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
962{
963 RTJSONVAL hJsonValTgt;
964 int rc = RTJsonValueQueryByName(hJsonRoot, "TgtRec", &hJsonValTgt);
965 if (RT_SUCCESS(rc))
966 {
967 uint32_t fTgtRecFlags = 0;
968 RTJSONIT hTgtIt;
969 rc = RTJsonIteratorBeginArray(hJsonValTgt, &hTgtIt);
970 if (RT_SUCCESS(rc))
971 {
972 do
973 {
974 RTJSONVAL hVal;
975 rc = RTJsonIteratorQueryValue(hTgtIt, &hVal, NULL);
976 if (RT_SUCCESS(rc))
977 {
978 const char *pszTgtRec = RTJsonValueGetString(hVal);
979 if (!RTStrICmp(pszTgtRec, "StdOut"))
980 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDOUT;
981 else if (!RTStrICmp(pszTgtRec, "StdErr"))
982 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_STDERR;
983 else if (!RTStrICmp(pszTgtRec, "ProcSts"))
984 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_PROCSTATUS;
985 else if (!RTStrICmp(pszTgtRec, "SanCov"))
986 fTgtRecFlags |= RTFUZZTGT_REC_STATE_F_SANCOV;
987 else
988 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "JSON request malformed: The recording flags '%s' is not known", pszTgtRec);
989 RTJsonValueRelease(hVal);
990 }
991 if (RT_FAILURE(rc))
992 break;
993
994 rc = RTJsonIteratorNext(hTgtIt);
995 } while (RT_SUCCESS(rc));
996
997 if ( rc == VERR_JSON_IS_EMPTY
998 || rc == VERR_JSON_ITERATOR_END)
999 rc = VINF_SUCCESS;
1000 else
1001 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to parse target recording flags");
1002
1003 RTJsonIteratorFree(hTgtIt);
1004 }
1005 else if (rc == VERR_JSON_IS_EMPTY)
1006 rc = VINF_SUCCESS;
1007 else
1008 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"TgtRec\" is not an array");
1009
1010 pFuzzRun->fTgtRecFlags = fTgtRecFlags;
1011
1012 RTJsonValueRelease(hJsonValTgt);
1013 }
1014 else if (rc == VERR_NOT_FOUND)
1015 {
1016 pFuzzRun->fTgtRecFlags = RTFUZZTGT_REC_STATE_F_PROCSTATUS;
1017 rc = VINF_SUCCESS; /* Just keep using the defaults. */
1018 }
1019 else
1020 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"TgtRec\"");
1021
1022 return rc;
1023}
1024
1025
1026/**
1027 * Sets up the directories for the given fuzzing run.
1028 *
1029 * @returns IPRT status code.
1030 * @param pThis The fuzzing master command state.
1031 * @param pFuzzRun The fuzzing run to setup the directories for.
1032 * @param pErrInfo Where to store the error information on failure, optional.
1033 */
1034static int rtFuzzCmdMasterFuzzRunSetupDirectories(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun, PRTERRINFO pErrInfo)
1035{
1036 /* Create temp directories. */
1037 char szTmpDir[RTPATH_MAX];
1038 int rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszTmpDir, pFuzzRun->pszId);
1039 AssertRC(rc);
1040 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
1041 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1042 if (rc == VERR_ALREADY_EXISTS)
1043 {
1044 /* Clear the directory. */
1045 rc = RTDirRemoveRecursive(szTmpDir, RTDIRRMREC_F_CONTENT_ONLY);
1046 }
1047
1048 if (RT_SUCCESS(rc))
1049 {
1050 rc = RTFuzzObsSetTmpDirectory(pFuzzRun->hFuzzObs, szTmpDir);
1051 if (RT_SUCCESS(rc))
1052 {
1053 rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszResultsDir, pFuzzRun->pszId);
1054 AssertRC(rc);
1055 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
1056 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1057 if (RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS)
1058 {
1059 rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
1060 if (RT_FAILURE(rc))
1061 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set results directory to %s", szTmpDir);
1062 }
1063 else
1064 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create results directory %s", szTmpDir);
1065 }
1066 else
1067 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set temporary directory to %s", szTmpDir);
1068 }
1069 else
1070 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create temporary directory %s", szTmpDir);
1071
1072 return rc;
1073}
1074
1075
1076/**
1077 * Creates a new fuzzing run with the given ID.
1078 *
1079 * @returns IPRT status code.
1080 * @param pThis The fuzzing master command state.
1081 * @param pszId The ID to use.
1082 * @param hJsonRoot The root node of the JSON request.
1083 * @param pErrInfo Where to store the error information on failure, optional.
1084 */
1085static int rtFuzzCmdMasterCreateFuzzRunWithId(PRTFUZZCMDMASTER pThis, const char *pszId, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1086{
1087 int rc = VINF_SUCCESS;
1088 PRTFUZZRUN pFuzzRun = (PRTFUZZRUN)RTMemAllocZ(sizeof(*pFuzzRun));
1089 if (RT_LIKELY(pFuzzRun))
1090 {
1091 pFuzzRun->pszId = RTStrDup(pszId);
1092 if (RT_LIKELY(pFuzzRun->pszId))
1093 {
1094 rc = rtFuzzCmdMasterFuzzRunProcessTgtRecFlags(pFuzzRun, hJsonRoot, pErrInfo);
1095 if (RT_SUCCESS(rc))
1096 {
1097 rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs, RTFUZZCTXTYPE_BLOB, pFuzzRun->fTgtRecFlags);
1098 if (RT_SUCCESS(rc))
1099 {
1100 rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
1101 if (RT_SUCCESS(rc))
1102 rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
1103 if (RT_SUCCESS(rc))
1104 rc = rtFuzzCmdMasterFuzzRunProcessEnvironment(pFuzzRun, hJsonRoot, pErrInfo);
1105 if (RT_SUCCESS(rc))
1106 rc = rtFuzzCmdMasterFuzzRunProcessInputSeeds(pFuzzRun, hJsonRoot, pErrInfo);
1107 if (RT_SUCCESS(rc))
1108 rc = rtFuzzCmdMasterFuzzRunProcessMiscCfg(pFuzzRun, hJsonRoot, pErrInfo);
1109 if (RT_SUCCESS(rc))
1110 rc = rtFuzzCmdMasterFuzzRunProcessSanitizers(pFuzzRun, hJsonRoot, pErrInfo);
1111 if (RT_SUCCESS(rc))
1112 rc = rtFuzzCmdMasterFuzzRunSetupDirectories(pThis, pFuzzRun, pErrInfo);
1113
1114 if (RT_SUCCESS(rc))
1115 {
1116 /* Start fuzzing. */
1117 RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
1118 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1119 if (RT_SUCCESS(rc))
1120 {
1121 RTTIMESPEC TimeSpec;
1122 RTTimeNow(&TimeSpec);
1123 RTTimeLocalExplode(&pFuzzRun->TimeCreated, &TimeSpec);
1124 pFuzzRun->tsCreatedMs = RTTimeMilliTS();
1125 pFuzzRun->fStarted = true;
1126 return VINF_SUCCESS;
1127 }
1128 else
1129 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
1130 }
1131
1132 int rc2 = RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
1133 AssertRC(rc2); RT_NOREF(rc2);
1134 }
1135 }
1136
1137 RTStrFree(pFuzzRun->pszId);
1138 pFuzzRun->pszId = NULL;
1139 }
1140 else
1141 rc = VERR_NO_STR_MEMORY;
1142
1143 RTMemFree(pFuzzRun);
1144 }
1145 else
1146 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Out of memory allocating the fuzzer state");
1147
1148 return rc;
1149}
1150
1151
1152/**
1153 * Resolves the fuzzing run from the given ID config item and the given JSON request.
1154 *
1155 * @returns IPRT status code.
1156 * @param pThis The fuzzing master command state.
1157 * @param hJsonRoot The root node of the JSON request.
1158 * @param pszIdItem The JSON item which contains the ID of the fuzzing run.
1159 * @param ppFuzzRun Where to store the pointer to the fuzzing run on success.
1160 */
1161static int rtFuzzCmdMasterQueryFuzzRunFromJson(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, const char *pszIdItem, PRTERRINFO pErrInfo,
1162 PRTFUZZRUN *ppFuzzRun)
1163{
1164 RTJSONVAL hJsonValId;
1165 int rc = RTJsonValueQueryByName(hJsonRoot, pszIdItem, &hJsonValId);
1166 if (RT_SUCCESS(rc))
1167 {
1168 const char *pszId = RTJsonValueGetString(hJsonValId);
1169 if (pszId)
1170 {
1171 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
1172 if (pFuzzRun)
1173 *ppFuzzRun = pFuzzRun;
1174 else
1175 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "Request error: The ID \"%s\" wasn't found", pszId);
1176 }
1177 else
1178 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
1179
1180 RTJsonValueRelease(hJsonValId);
1181 }
1182 else
1183 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
1184 return rc;
1185}
1186
1187
1188/**
1189 * Processes the "StartFuzzing" request.
1190 *
1191 * @returns IPRT status code.
1192 * @param pThis The fuzzing master command state.
1193 * @param hJsonRoot The root node of the JSON request.
1194 * @param pErrInfo Where to store the error information on failure, optional.
1195 */
1196static int rtFuzzCmdMasterProcessJsonReqStart(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1197{
1198 RTJSONVAL hJsonValId;
1199 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
1200 if (RT_SUCCESS(rc))
1201 {
1202 const char *pszId = RTJsonValueGetString(hJsonValId);
1203 if (pszId)
1204 {
1205 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
1206 if (!pFuzzRun)
1207 rc = rtFuzzCmdMasterCreateFuzzRunWithId(pThis, pszId, hJsonRoot, pErrInfo);
1208 else
1209 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_ALREADY_EXISTS, "Request error: The ID \"%s\" is already registered", pszId);
1210 }
1211 else
1212 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
1213
1214 RTJsonValueRelease(hJsonValId);
1215 }
1216 else
1217 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
1218 return rc;
1219}
1220
1221
1222/**
1223 * Processes the "StopFuzzing" request.
1224 *
1225 * @returns IPRT status code.
1226 * @param pThis The fuzzing master command state.
1227 * @param hJsonValRoot The root node of the JSON request.
1228 * @param pErrInfo Where to store the error information on failure, optional.
1229 */
1230static int rtFuzzCmdMasterProcessJsonReqStop(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1231{
1232 PRTFUZZRUN pFuzzRun;
1233 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1234 if (RT_SUCCESS(rc))
1235 {
1236 RTListNodeRemove(&pFuzzRun->NdFuzzed);
1237 RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1238 RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
1239 RTStrFree(pFuzzRun->pszId);
1240 RTMemFree(pFuzzRun);
1241 }
1242
1243 return rc;
1244}
1245
1246
1247/**
1248 * Processes the "SuspendFuzzing" request.
1249 *
1250 * @returns IPRT status code.
1251 * @param pThis The fuzzing master command state.
1252 * @param hJsonValRoot The root node of the JSON request.
1253 * @param pErrInfo Where to store the error information on failure, optional.
1254 */
1255static int rtFuzzCmdMasterProcessJsonReqSuspend(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1256{
1257 PRTFUZZRUN pFuzzRun;
1258 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1259 if (RT_SUCCESS(rc))
1260 {
1261 if (pFuzzRun->fStarted)
1262 {
1263 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1264 if (RT_SUCCESS(rc))
1265 pFuzzRun->fStarted = false;
1266 else
1267 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
1268 }
1269 }
1270
1271 return rc;
1272}
1273
1274
1275/**
1276 * Processes the "ResumeFuzzing" request.
1277 *
1278 * @returns IPRT status code.
1279 * @param pThis The fuzzing master command state.
1280 * @param hJsonValRoot The root node of the JSON request.
1281 * @param pErrInfo Where to store the error information on failure, optional.
1282 */
1283static int rtFuzzCmdMasterProcessJsonReqResume(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1284{
1285 PRTFUZZRUN pFuzzRun;
1286 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1287 if (RT_SUCCESS(rc))
1288 {
1289 if (!pFuzzRun->fStarted)
1290 {
1291 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, pFuzzRun->cProcs, pErrInfo);
1292 if (RT_SUCCESS(rc))
1293 {
1294 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1295 if (RT_SUCCESS(rc))
1296 pFuzzRun->fStarted = true;
1297 else
1298 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Resuming the fuzzing process failed");
1299 }
1300 }
1301 }
1302
1303 return rc;
1304}
1305
1306
1307/**
1308 * Processes the "SaveFuzzingState" request.
1309 *
1310 * @returns IPRT status code.
1311 * @param pThis The fuzzing master command state.
1312 * @param hJsonValRoot The root node of the JSON request.
1313 * @param pErrInfo Where to store the error information on failure, optional.
1314 */
1315static int rtFuzzCmdMasterProcessJsonReqSaveState(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1316{
1317 PRTFUZZRUN pFuzzRun;
1318 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1319 if (RT_SUCCESS(rc))
1320 {
1321 /* Suspend fuzzing, save and resume if not stopped. */
1322 if (pFuzzRun->fStarted)
1323 {
1324 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
1325 if (RT_FAILURE(rc))
1326 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
1327 }
1328
1329 if (RT_SUCCESS(rc))
1330 {
1331 RTFUZZCTX hFuzzCtx;
1332 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
1333 AssertRC(rc);
1334
1335 void *pvState = NULL;
1336 size_t cbState = 0;
1337 rc = RTFuzzCtxStateExportToMem(hFuzzCtx, &pvState, &cbState);
1338 if (RT_SUCCESS(rc))
1339 {
1340 /* Encode to base64. */
1341 size_t cbStateStr = RTBase64EncodedLength(cbState) + 1;
1342 char *pszState = (char *)RTMemAllocZ(cbStateStr);
1343 if (pszState)
1344 {
1345 rc = RTBase64Encode(pvState, cbState, pszState, cbStateStr, &cbStateStr);
1346 if (RT_SUCCESS(rc))
1347 {
1348 /* Strip all new lines from the srting. */
1349 size_t offStr = 0;
1350 while (offStr < cbStateStr)
1351 {
1352#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1353 char *pszEol = strchr(&pszState[offStr], '\r');
1354#else
1355 char *pszEol = strchr(&pszState[offStr], '\n');
1356#endif
1357 if (pszEol)
1358 {
1359 offStr += pszEol - &pszState[offStr];
1360 memmove(pszEol, &pszEol[RTBASE64_EOL_SIZE], cbStateStr - offStr - RTBASE64_EOL_SIZE);
1361 cbStateStr -= RTBASE64_EOL_SIZE;
1362 }
1363 else
1364 break;
1365 }
1366
1367 const char s_szState[] = "{ \"State\": %s }";
1368 pThis->pszResponse = RTStrAPrintf2(s_szState, pszState);
1369 if (RT_UNLIKELY(!pThis->pszResponse))
1370 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
1371 }
1372 else
1373 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to encode the state as a base64 string");
1374 RTMemFree(pszState);
1375 }
1376 else
1377 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "Request error: Failed to allocate a state string for the response");
1378 RTMemFree(pvState);
1379 }
1380 else
1381 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Exporting the state failed");
1382 }
1383
1384 if (pFuzzRun->fStarted)
1385 {
1386 int rc2 = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
1387 if (RT_FAILURE(rc2))
1388 rtFuzzCmdMasterErrorRc(pErrInfo, rc2, "Request error: Resuming the fuzzing process failed");
1389 }
1390 }
1391
1392 return rc;
1393}
1394
1395
1396/**
1397 * Queries the statistics for the given fuzzing run and adds the result to the response.
1398 *
1399 * @returns IPRT static code.
1400 * @param pThis The fuzzing master command state.
1401 * @param pFuzzRun The fuzzing run.
1402 * @param pszIndent Indentation to use.
1403 * @param fLast Flags whether this is the last element in the list.
1404 * @param pErrInfo Where to store the error information on failure, optional.
1405 */
1406static int rtFuzzCmdMasterProcessQueryRunStats(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun,
1407 const char *pszIndent, bool fLast, PRTERRINFO pErrInfo)
1408{
1409 RTFUZZOBSSTATS ObsStats;
1410 RTFUZZCTXSTATS CtxStats;
1411 RTFUZZCTX hFuzzCtx;
1412 RT_ZERO(ObsStats); RT_ZERO(CtxStats);
1413
1414 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
1415 if (RT_SUCCESS(rc))
1416 {
1417 rc = RTFuzzCtxQueryStats(hFuzzCtx, &CtxStats);
1418 RTFuzzCtxRelease(hFuzzCtx);
1419 }
1420
1421 if (RT_SUCCESS(rc))
1422 rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &ObsStats);
1423 if (RT_SUCCESS(rc))
1424 {
1425 const char s_szStatsFmt[] = "%s{ \n"
1426 "%s \"Id\": \"%s\"\n"
1427 "%s \"TimeCreated\": \"%s\"\n"
1428 "%s \"UptimeSec\": %llu\n"
1429 "%s \"FuzzedInputsPerSec\": %u\n"
1430 "%s \"FuzzedInputs\": %u\n"
1431 "%s \"FuzzedInputsHang\": %u\n"
1432 "%s \"FuzzedInputsCrash\": %u\n"
1433 "%s \"MemoryUsage\": %zu\n"
1434 "%s \"CorpusSize\": %llu\n"
1435 "%s}%s\n";
1436 char aszTime[_1K]; RT_ZERO(aszTime);
1437 char aszStats[_4K]; RT_ZERO(aszStats);
1438
1439 if (RTTimeToString(&pFuzzRun->TimeCreated, aszTime, sizeof(aszTime)))
1440 {
1441 ssize_t cchStats = RTStrPrintf2(&aszStats[0], sizeof(aszStats),
1442 s_szStatsFmt, pszIndent,
1443 pszIndent, pFuzzRun->pszId,
1444 pszIndent, aszTime,
1445 pszIndent, (RTTimeMilliTS() - pFuzzRun->tsCreatedMs) / RT_MS_1SEC_64,
1446 pszIndent, ObsStats.cFuzzedInputsPerSec,
1447 pszIndent, ObsStats.cFuzzedInputs,
1448 pszIndent, ObsStats.cFuzzedInputsHang,
1449 pszIndent, ObsStats.cFuzzedInputsCrash,
1450 pszIndent, CtxStats.cbMemory,
1451 pszIndent, CtxStats.cMutations,
1452 pszIndent, fLast ? "" : ",");
1453 if (RT_LIKELY(cchStats > 0))
1454 {
1455 rc = RTStrAAppend(&pThis->pszResponse, &aszStats[0]);
1456 if (RT_FAILURE(rc))
1457 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to build statistics response", rc);
1458 }
1459 else
1460 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow");
1461 }
1462 else
1463 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Buffer overflow conerting time to string");
1464 }
1465 else
1466 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
1467
1468 return rc;
1469}
1470
1471
1472/**
1473 * Processes the "QueryStats" request.
1474 *
1475 * @returns IPRT status code.
1476 * @param pThis The fuzzing master command state.
1477 * @param hJsonValRoot The root node of the JSON request.
1478 * @param pErrInfo Where to store the error information on failure, optional.
1479 */
1480static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1481{
1482 RTJSONVAL hJsonValId;
1483 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
1484 if (RT_SUCCESS(rc))
1485 {
1486 RTJsonValueRelease(hJsonValId);
1487 PRTFUZZRUN pFuzzRun;
1488 rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1489 if (RT_SUCCESS(rc))
1490 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pFuzzRun, " ",
1491 true /*fLast*/, pErrInfo);
1492 }
1493 else if (rc == VERR_NOT_FOUND)
1494 {
1495 /* Id is not there, so collect statistics of all running jobs. */
1496 rc = RTStrAAppend(&pThis->pszResponse, " [\n");
1497 if (RT_SUCCESS(rc))
1498 {
1499 PRTFUZZRUN pRun = NULL;
1500 RTListForEach(&pThis->LstFuzzed, pRun, RTFUZZRUN, NdFuzzed)
1501 {
1502 bool fLast = RTListNodeIsLast(&pThis->LstFuzzed, &pRun->NdFuzzed);
1503 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pRun, " ", fLast, pErrInfo);
1504 if (RT_FAILURE(rc))
1505 break;
1506 }
1507 if (RT_SUCCESS(rc))
1508 rc = RTStrAAppend(&pThis->pszResponse, " ]\n");
1509 }
1510 }
1511 else
1512 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't get \"Id\" value");
1513
1514 return rc;
1515}
1516
1517
1518/**
1519 * Processes a JSON request.
1520 *
1521 * @returns IPRT status code.
1522 * @param pThis The fuzzing master command state.
1523 * @param hJsonValRoot The root node of the JSON request.
1524 * @param pErrInfo Where to store the error information on failure, optional.
1525 */
1526static int rtFuzzCmdMasterProcessJsonReq(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1527{
1528 RTJSONVAL hJsonValReq;
1529 int rc = RTJsonValueQueryByName(hJsonRoot, "Request", &hJsonValReq);
1530 if (RT_SUCCESS(rc))
1531 {
1532 const char *pszReq = RTJsonValueGetString(hJsonValReq);
1533 if (pszReq)
1534 {
1535 if (!RTStrCmp(pszReq, "StartFuzzing"))
1536 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, pErrInfo);
1537 else if (!RTStrCmp(pszReq, "StopFuzzing"))
1538 rc = rtFuzzCmdMasterProcessJsonReqStop(pThis, hJsonRoot, pErrInfo);
1539 else if (!RTStrCmp(pszReq, "SuspendFuzzing"))
1540 rc = rtFuzzCmdMasterProcessJsonReqSuspend(pThis, hJsonRoot, pErrInfo);
1541 else if (!RTStrCmp(pszReq, "ResumeFuzzing"))
1542 rc = rtFuzzCmdMasterProcessJsonReqResume(pThis, hJsonRoot, pErrInfo);
1543 else if (!RTStrCmp(pszReq, "SaveFuzzingState"))
1544 rc = rtFuzzCmdMasterProcessJsonReqSaveState(pThis, hJsonRoot, pErrInfo);
1545 else if (!RTStrCmp(pszReq, "QueryStats"))
1546 rc = rtFuzzCmdMasterProcessJsonReqQueryStats(pThis, hJsonRoot, pErrInfo);
1547 else if (!RTStrCmp(pszReq, "Shutdown"))
1548 pThis->fShutdown = true;
1549 else
1550 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" contains unknown value \"%s\"", pszReq);
1551 }
1552 else
1553 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" is not a string value");
1554
1555 RTJsonValueRelease(hJsonValReq);
1556 }
1557 else
1558 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Request\" value");
1559
1560 return rc;
1561}
1562
1563
1564/**
1565 * Loads a fuzzing configuration for immediate startup from the given file.
1566 *
1567 * @returns IPRT status code.
1568 * @param pThis The fuzzing master command state.
1569 * @param pszFuzzCfg The fuzzing config to load.
1570 */
1571static int rtFuzzCmdMasterFuzzCfgLoadFromFile(PRTFUZZCMDMASTER pThis, const char *pszFuzzCfg)
1572{
1573 RTJSONVAL hJsonRoot;
1574 int rc = RTJsonParseFromFile(&hJsonRoot, pszFuzzCfg, NULL);
1575 if (RT_SUCCESS(rc))
1576 {
1577 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, NULL);
1578 RTJsonValueRelease(hJsonRoot);
1579 }
1580 else
1581 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "JSON request malformed: Couldn't load file \"%s\"", pszFuzzCfg);
1582
1583 return rc;
1584}
1585
1586
1587/**
1588 * Destroys all running fuzzers for the given master state.
1589 *
1590 * @param pThis The fuzzing master command state.
1591 */
1592static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
1593{
1594 RT_NOREF(pThis);
1595}
1596
1597
1598/**
1599 * Sends an ACK response to the client.
1600 *
1601 * @param hSocket The socket handle to send the ACK to.
1602 * @param pszResponse Additional response data.
1603 */
1604static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
1605{
1606 const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
1607 const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n \"Response\":\n";
1608 const char s_szSuccRespClose[] = "\n }\n";
1609 if (pszResponse)
1610 {
1611 RTSGSEG aSegs[3];
1612 RTSGBUF SgBuf;
1613 aSegs[0].pvSeg = (void *)s_szSuccResp;
1614 aSegs[0].cbSeg = sizeof(s_szSuccResp) - 1;
1615 aSegs[1].pvSeg = (void *)pszResponse;
1616 aSegs[1].cbSeg = strlen(pszResponse);
1617 aSegs[2].pvSeg = (void *)s_szSuccRespClose;
1618 aSegs[2].cbSeg = sizeof(s_szSuccRespClose) - 1;
1619
1620 RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
1621 RTTcpSgWrite(hSocket, &SgBuf);
1622 }
1623 else
1624 RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
1625}
1626
1627
1628/**
1629 * Sends an NACK response to the client.
1630 *
1631 * @param hSocket The socket handle to send the ACK to.
1632 * @param pErrInfo Optional error information to send along.
1633 */
1634static void rtFuzzCmdMasterTcpSendNAck(RTSOCKET hSocket, PRTERRINFO pErrInfo)
1635{
1636 const char s_szFail[] = "{ \"Status\": \"NACK\" }\n";
1637 const char s_szFailInfo[] = "{ \"Status\": \"NACK\"\n \"Information\": \"%s\" }\n";
1638
1639 if (pErrInfo)
1640 {
1641 char szTmp[_1K];
1642 ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
1643 if (cchResp > 0)
1644 RTTcpWrite(hSocket, szTmp, cchResp);
1645 else
1646 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1647 }
1648 else
1649 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1650}
1651
1652
1653/**
1654 * TCP server serving callback for a single connection.
1655 *
1656 * @returns IPRT status code.
1657 * @param hSocket The socket handle of the connection.
1658 * @param pvUser Opaque user data.
1659 */
1660static DECLCALLBACK(int) rtFuzzCmdMasterTcpServe(RTSOCKET hSocket, void *pvUser)
1661{
1662 PRTFUZZCMDMASTER pThis = (PRTFUZZCMDMASTER)pvUser;
1663 size_t cbReqMax = _32K;
1664 size_t cbReq = 0;
1665 uint8_t *pbReq = (uint8_t *)RTMemAllocZ(cbReqMax);
1666
1667 if (RT_LIKELY(pbReq))
1668 {
1669 uint8_t *pbCur = pbReq;
1670
1671 for (;;)
1672 {
1673 size_t cbThisRead = cbReqMax - cbReq;
1674 int rc = RTTcpRead(hSocket, pbCur, cbThisRead, &cbThisRead);
1675 if ( RT_SUCCESS(rc)
1676 && cbThisRead)
1677 {
1678 cbReq += cbThisRead;
1679
1680 /* Check for a zero terminator marking the end of the request. */
1681 uint8_t *pbEnd = (uint8_t *)memchr(pbCur, 0, cbThisRead);
1682 if (pbEnd)
1683 {
1684 /* Adjust request size, data coming after the zero terminiator is ignored right now. */
1685 cbReq -= cbThisRead - (pbEnd - pbCur) + 1;
1686
1687 RTJSONVAL hJsonReq;
1688 RTERRINFOSTATIC ErrInfo;
1689 RTErrInfoInitStatic(&ErrInfo);
1690
1691 rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
1692 if (RT_SUCCESS(rc))
1693 {
1694 rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
1695 if (RT_SUCCESS(rc))
1696 rtFuzzCmdMasterTcpSendAck(hSocket, pThis->pszResponse);
1697 else
1698 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1699 RTJsonValueRelease(hJsonReq);
1700 }
1701 else
1702 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1703
1704 if (pThis->pszResponse)
1705 {
1706 RTStrFree(pThis->pszResponse);
1707 pThis->pszResponse = NULL;
1708 }
1709 break;
1710 }
1711 else if (cbReq == cbReqMax)
1712 {
1713 /* Try to increase the buffer. */
1714 uint8_t *pbReqNew = (uint8_t *)RTMemRealloc(pbReq, cbReqMax + _32K);
1715 if (RT_LIKELY(pbReqNew))
1716 {
1717 cbReqMax += _32K;
1718 pbReq = pbReqNew;
1719 pbCur = pbReq + cbReq;
1720 }
1721 else
1722 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1723 }
1724 else
1725 pbCur += cbThisRead;
1726 }
1727 else
1728 break;
1729 }
1730 }
1731 else
1732 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1733
1734 if (pbReq)
1735 RTMemFree(pbReq);
1736
1737 return pThis->fShutdown ? VERR_TCP_SERVER_STOP : VINF_SUCCESS;
1738}
1739
1740
1741/**
1742 * Mainloop for the fuzzing master.
1743 *
1744 * @returns Process exit code.
1745 * @param pThis The fuzzing master command state.
1746 * @param pszLoadCfg Initial config to load.
1747 */
1748static RTEXITCODE rtFuzzCmdMasterRun(PRTFUZZCMDMASTER pThis, const char *pszLoadCfg)
1749{
1750 if (pszLoadCfg)
1751 {
1752 int rc = rtFuzzCmdMasterFuzzCfgLoadFromFile(pThis, pszLoadCfg);
1753 if (RT_FAILURE(rc))
1754 return RTEXITCODE_FAILURE;
1755 }
1756
1757 /* Start up the control server. */
1758 int rc = RTTcpServerCreateEx(NULL, pThis->uPort, &pThis->hTcpSrv);
1759 if (RT_SUCCESS(rc))
1760 {
1761 do
1762 {
1763 rc = RTTcpServerListen(pThis->hTcpSrv, rtFuzzCmdMasterTcpServe, pThis);
1764 } while (rc != VERR_TCP_SERVER_STOP);
1765 }
1766
1767 RTTcpServerDestroy(pThis->hTcpSrv);
1768 rtFuzzCmdMasterDestroy(pThis);
1769 return RTEXITCODE_SUCCESS;
1770}
1771
1772
1773RTR3DECL(RTEXITCODE) RTFuzzCmdMaster(unsigned cArgs, char **papszArgs)
1774{
1775 /*
1776 * Parse the command line.
1777 */
1778 static const RTGETOPTDEF s_aOptions[] =
1779 {
1780 { "--fuzz-config", 'c', RTGETOPT_REQ_STRING },
1781 { "--temp-dir", 't', RTGETOPT_REQ_STRING },
1782 { "--results-dir", 'r', RTGETOPT_REQ_STRING },
1783 { "--listen-port", 'p', RTGETOPT_REQ_UINT16 },
1784 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
1785 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
1786 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1787 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1788 };
1789
1790 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1791 RTGETOPTSTATE GetState;
1792 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1793 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1794 if (RT_SUCCESS(rc))
1795 {
1796 /* Option variables: */
1797 bool fDaemonize = false;
1798 const char *pszLoadCfg = NULL;
1799 RTFUZZCMDMASTER This;
1800
1801 RTListInit(&This.LstFuzzed);
1802 This.hTcpSrv = NIL_RTTCPSERVER;
1803 This.uPort = 4242;
1804 This.pszTmpDir = NULL;
1805 This.pszResultsDir = NULL;
1806 This.fShutdown = false;
1807 This.pszResponse = NULL;
1808
1809 /* Argument parsing loop. */
1810 bool fContinue = true;
1811 do
1812 {
1813 RTGETOPTUNION ValueUnion;
1814 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1815 switch (chOpt)
1816 {
1817 case 0:
1818 fContinue = false;
1819 break;
1820
1821 case 'c':
1822 pszLoadCfg = ValueUnion.psz;
1823 break;
1824
1825 case 'p':
1826 This.uPort = ValueUnion.u16;
1827 break;
1828
1829 case 't':
1830 This.pszTmpDir = ValueUnion.psz;
1831 break;
1832
1833 case 'r':
1834 This.pszResultsDir = ValueUnion.psz;
1835 break;
1836
1837 case 'd':
1838 fDaemonize = true;
1839 break;
1840
1841 case 'Z':
1842 fDaemonize = false;
1843 break;
1844
1845 case 'h':
1846 RTPrintf("Usage: to be written\nOption dump:\n");
1847 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1848 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1849 fContinue = false;
1850 break;
1851
1852 case 'V':
1853 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1854 fContinue = false;
1855 break;
1856
1857 default:
1858 rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
1859 fContinue = false;
1860 break;
1861 }
1862 } while (fContinue);
1863
1864 if (rcExit == RTEXITCODE_SUCCESS)
1865 {
1866 /*
1867 * Daemonize ourselves if asked to.
1868 */
1869 if (fDaemonize)
1870 {
1871 rc = RTProcDaemonize(papszArgs, "--daemonized");
1872 if (RT_FAILURE(rc))
1873 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1874 }
1875 else
1876 rcExit = rtFuzzCmdMasterRun(&This, pszLoadCfg);
1877 }
1878 }
1879 else
1880 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1881 return rcExit;
1882}
1883
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette