VirtualBox

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

Last change on this file since 77658 was 77658, checked in by vboxsync, 6 years ago

Runtime/fuzz: Statistics updates, don't fail if a working directory already exists

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.0 KB
Line 
1/* $Id: fuzzmastercmd.cpp 77658 2019-03-11 20:15:55Z vboxsync $ */
2/** @file
3 * IPRT - Fuzzing framework API, master command.
4 */
5
6/*
7 * Copyright (C) 2018-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/fuzz.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/base64.h>
37#include <iprt/buildconfig.h>
38#include <iprt/ctype.h>
39#include <iprt/err.h>
40#include <iprt/file.h>
41#include <iprt/getopt.h>
42#include <iprt/json.h>
43#include <iprt/list.h>
44#include <iprt/mem.h>
45#include <iprt/message.h>
46#include <iprt/path.h>
47#include <iprt/process.h>
48#include <iprt/stream.h>
49#include <iprt/string.h>
50#include <iprt/tcp.h>
51#include <iprt/thread.h>
52#include <iprt/vfs.h>
53#include <iprt/zip.h>
54
55
56/**
57 * A running fuzzer state.
58 */
59typedef struct RTFUZZRUN
60{
61 /** List node. */
62 RTLISTNODE NdFuzzed;
63 /** Identifier. */
64 char *pszId;
65 /** Number of processes. */
66 uint32_t cProcs;
67 /** The fuzzing observer state handle. */
68 RTFUZZOBS hFuzzObs;
69 /** Flag whether fuzzing was started. */
70 bool fStarted;
71} RTFUZZRUN;
72/** Pointer to a running fuzzer state. */
73typedef RTFUZZRUN *PRTFUZZRUN;
74
75
76/**
77 * Fuzzing master command state.
78 */
79typedef struct RTFUZZCMDMASTER
80{
81 /** List of running fuzzers. */
82 RTLISTANCHOR LstFuzzed;
83 /** The port to listen on. */
84 uint16_t uPort;
85 /** The TCP server for requests. */
86 PRTTCPSERVER hTcpSrv;
87 /** The root temp directory. */
88 const char *pszTmpDir;
89 /** The root results directory. */
90 const char *pszResultsDir;
91 /** Flag whether to shutdown. */
92 bool fShutdown;
93 /** The response message. */
94 char *pszResponse;
95} RTFUZZCMDMASTER;
96/** Pointer to a fuzzing master command state. */
97typedef RTFUZZCMDMASTER *PRTFUZZCMDMASTER;
98
99
100/**
101 * Wrapper around RTErrInfoSetV / RTMsgErrorV.
102 *
103 * @returns @a rc
104 * @param pErrInfo Extended error info.
105 * @param rc The return code.
106 * @param pszFormat The message format.
107 * @param ... The message format arguments.
108 */
109static int rtFuzzCmdMasterErrorRc(PRTERRINFO pErrInfo, int rc, const char *pszFormat, ...)
110{
111 va_list va;
112 va_start(va, pszFormat);
113 if (pErrInfo)
114 RTErrInfoSetV(pErrInfo, rc, pszFormat, va);
115 else
116 RTMsgErrorV(pszFormat, va);
117 va_end(va);
118 return rc;
119}
120
121
122/**
123 * Returns a running fuzzer state by the given ID.
124 *
125 * @returns Pointer to the running fuzzer state or NULL if not found.
126 * @param pThis The fuzzing master command state.
127 * @param pszId The ID to look for.
128 */
129static PRTFUZZRUN rtFuzzCmdMasterGetFuzzerById(PRTFUZZCMDMASTER pThis, const char *pszId)
130{
131 PRTFUZZRUN pIt = NULL;
132 RTListForEach(&pThis->LstFuzzed, pIt, RTFUZZRUN, NdFuzzed)
133 {
134 if (!RTStrCmp(pIt->pszId, pszId))
135 return pIt;
136 }
137
138 return NULL;
139}
140
141
142#if 0 /* unused */
143/**
144 * Processes and returns the value of the given config item in the JSON request.
145 *
146 * @returns IPRT status code.
147 * @param ppszStr Where to store the pointer to the string on success.
148 * @param pszCfgItem The config item to resolve.
149 * @param hJsonCfg The JSON object containing the item.
150 * @param pErrInfo Where to store the error information on failure, optional.
151 */
152static int rtFuzzCmdMasterFuzzRunProcessCfgString(char **ppszStr, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
153{
154 int rc = RTJsonValueQueryStringByName(hJsonCfg, pszCfgItem, ppszStr);
155 if (RT_FAILURE(rc))
156 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query string value of \"%s\"", pszCfgItem);
157
158 return rc;
159}
160
161
162/**
163 * Processes and returns the value of the given config item in the JSON request.
164 *
165 * @returns IPRT status code.
166 * @param pfVal Where to store the config value on success.
167 * @param pszCfgItem The config item to resolve.
168 * @param hJsonCfg The JSON object containing the item.
169 * @param pErrInfo Where to store the error information on failure, optional.
170 */
171static int rtFuzzCmdMasterFuzzRunProcessCfgBool(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, PRTERRINFO pErrInfo)
172{
173 int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
174 if (RT_FAILURE(rc))
175 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
176
177 return rc;
178}
179
180
181/**
182 * Processes and returns the value of the given config item in the JSON request.
183 *
184 * @returns IPRT status code.
185 * @param pfVal Where to store the config value on success.
186 * @param pszCfgItem The config item to resolve.
187 * @param hJsonCfg The JSON object containing the item.
188 * @param fDef Default value if the item wasn't found.
189 * @param pErrInfo Where to store the error information on failure, optional.
190 */
191static int rtFuzzCmdMasterFuzzRunProcessCfgBoolDef(bool *pfVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, bool fDef, PRTERRINFO pErrInfo)
192{
193 int rc = RTJsonValueQueryBooleanByName(hJsonCfg, pszCfgItem, pfVal);
194 if (rc == VERR_NOT_FOUND)
195 {
196 *pfVal = fDef;
197 rc = VINF_SUCCESS;
198 }
199 else if (RT_FAILURE(rc))
200 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query boolean value of \"%s\"", pszCfgItem);
201
202 return rc;
203}
204#endif
205
206
207/**
208 * Processes and returns the value of the given config item in the JSON request.
209 *
210 * @returns IPRT status code.
211 * @param pcbVal Where to store the config value on success.
212 * @param pszCfgItem The config item to resolve.
213 * @param hJsonCfg The JSON object containing the item.
214 * @param cbDef Default value if the item wasn't found.
215 * @param pErrInfo Where to store the error information on failure, optional.
216 */
217static int rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(size_t *pcbVal, const char *pszCfgItem, RTJSONVAL hJsonCfg, size_t cbDef, PRTERRINFO pErrInfo)
218{
219 *pcbVal = cbDef; /* Make GCC 6.3.0 happy. */
220
221 int64_t i64Val = 0;
222 int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
223 if (rc == VERR_NOT_FOUND)
224 rc = VINF_SUCCESS;
225 else if (RT_FAILURE(rc))
226 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query size_t value of \"%s\"", pszCfgItem);
227 else if (i64Val < 0 || (size_t)i64Val != (uint64_t)i64Val)
228 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
229 else
230 *pcbVal = (size_t)i64Val;
231
232 return rc;
233}
234
235
236/**
237 * Processes and returns the value of the given config item in the JSON request.
238 *
239 * @returns IPRT status code.
240 * @param pcbVal Where to store the config value on success.
241 * @param pszCfgItem The config item to resolve.
242 * @param hJsonCfg The JSON object containing the item.
243 * @param cbDef Default value if the item wasn't found.
244 * @param pErrInfo Where to store the error information on failure, optional.
245 */
246static int rtFuzzCmdMasterFuzzRunProcessCfgU32Def(uint32_t *pu32Val, const char *pszCfgItem, RTJSONVAL hJsonCfg, uint32_t u32Def, PRTERRINFO pErrInfo)
247{
248 int64_t i64Val = 0;
249 int rc = RTJsonValueQueryIntegerByName(hJsonCfg, pszCfgItem, &i64Val);
250 if (rc == VERR_NOT_FOUND)
251 {
252 *pu32Val = u32Def;
253 rc = VINF_SUCCESS;
254 }
255 else if (RT_FAILURE(rc))
256 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query uint32_t value of \"%s\"", pszCfgItem);
257 else if (i64Val < 0 || (uint32_t)i64Val != (uint64_t)i64Val)
258 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_OUT_OF_RANGE, "JSON request malformed: Integer \"%s\" is out of range", pszCfgItem);
259 else
260 *pu32Val = (uint32_t)i64Val;
261
262 return rc;
263}
264
265
266/**
267 * Returns the configured input channel for the binary under test.
268 *
269 * @returns Selected input channel or RTFUZZOBSINPUTCHAN_INVALID if an error occurred.
270 * @param pszCfgItem The config item to resolve.
271 * @param hJsonCfg The JSON object containing the item.
272 * @param enmChanDef Default value if the item wasn't found.
273 * @param pErrInfo Where to store the error information on failure, optional.
274 */
275static RTFUZZOBSINPUTCHAN rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan(const char *pszCfgItem, RTJSONVAL hJsonCfg, RTFUZZOBSINPUTCHAN enmChanDef, PRTERRINFO pErrInfo)
276{
277 RTFUZZOBSINPUTCHAN enmInputChan = RTFUZZOBSINPUTCHAN_INVALID;
278
279 RTJSONVAL hJsonVal;
280 int rc = RTJsonValueQueryByName(hJsonCfg, pszCfgItem, &hJsonVal);
281 if (rc == VERR_NOT_FOUND)
282 enmInputChan = enmChanDef;
283 else if (RT_SUCCESS(rc))
284 {
285 const char *pszBinary = RTJsonValueGetString(hJsonVal);
286 if (pszBinary)
287 {
288 if (!RTStrCmp(pszBinary, "File"))
289 enmInputChan = RTFUZZOBSINPUTCHAN_FILE;
290 else if (!RTStrCmp(pszBinary, "Stdin"))
291 enmInputChan = RTFUZZOBSINPUTCHAN_STDIN;
292 else if (!RTStrCmp(pszBinary, "FuzzingAware"))
293 enmInputChan = RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT;
294 else
295 rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_PARAMETER, "JSON request malformed: \"%s\" for \"%s\" is not known", pszCfgItem, pszBinary);
296 }
297 else
298 rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"%s\" is not a string", pszCfgItem);
299
300 RTJsonValueRelease(hJsonVal);
301 }
302 else
303 rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query \"%s\"", pszCfgItem);
304
305 return enmInputChan;
306}
307
308
309/**
310 * Processes binary related configs for the given fuzzing run.
311 *
312 * @returns IPRT status code.
313 * @param pFuzzRun The fuzzing run.
314 * @param hJsonRoot The root node of the JSON request.
315 * @param pErrInfo Where to store the error information on failure, optional.
316 */
317static int rtFuzzCmdMasterFuzzRunProcessBinaryCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
318{
319 RTJSONVAL hJsonVal;
320 int rc = RTJsonValueQueryByName(hJsonRoot, "BinaryPath", &hJsonVal);
321 if (RT_SUCCESS(rc))
322 {
323 const char *pszBinary = RTJsonValueGetString(hJsonVal);
324 if (RT_LIKELY(pszBinary))
325 {
326 RTFUZZOBSINPUTCHAN enmInputChan = rtFuzzCmdMasterFuzzRunProcessCfgGetInputChan("InputChannel", hJsonRoot, RTFUZZOBSINPUTCHAN_STDIN, pErrInfo);
327 if (enmInputChan != RTFUZZOBSINPUTCHAN_INVALID)
328 {
329 rc = RTFuzzObsSetTestBinary(pFuzzRun->hFuzzObs, pszBinary, enmInputChan);
330 if (RT_FAILURE(rc))
331 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to add the binary path for the fuzzing run");
332 }
333 }
334 else
335 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"BinaryPath\" is not a string");
336 RTJsonValueRelease(hJsonVal);
337 }
338 else
339 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to query value of \"BinaryPath\"");
340
341 return rc;
342}
343
344
345/**
346 * Processes argument related configs for the given fuzzing run.
347 *
348 * @returns IPRT status code.
349 * @param pFuzzRun The fuzzing run.
350 * @param hJsonRoot The root node of the JSON request.
351 * @param pErrInfo Where to store the error information on failure, optional.
352 */
353static int rtFuzzCmdMasterFuzzRunProcessArgCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
354{
355 RTJSONVAL hJsonValArgArray;
356 int rc = RTJsonValueQueryByName(hJsonRoot, "Arguments", &hJsonValArgArray);
357 if (RT_SUCCESS(rc))
358 {
359 unsigned cArgs = 0;
360 rc = RTJsonValueQueryArraySize(hJsonValArgArray, &cArgs);
361 if (RT_SUCCESS(rc))
362 {
363 if (cArgs > 0)
364 {
365 const char **papszArgs = (const char **)RTMemAllocZ(cArgs * sizeof(const char *));
366 RTJSONVAL *pahJsonVal = (RTJSONVAL *)RTMemAllocZ(cArgs * sizeof(RTJSONVAL));
367 if (RT_LIKELY(papszArgs && pahJsonVal))
368 {
369 unsigned idx = 0;
370
371 for (idx = 0; idx < cArgs && RT_SUCCESS(rc); idx++)
372 {
373 rc = RTJsonValueQueryByIndex(hJsonValArgArray, idx, &pahJsonVal[idx]);
374 if (RT_SUCCESS(rc))
375 {
376 papszArgs[idx] = RTJsonValueGetString(pahJsonVal[idx]);
377 if (RT_UNLIKELY(!papszArgs[idx]))
378 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Argument %u is not a string", idx);
379 }
380 }
381
382 if (RT_SUCCESS(rc))
383 {
384 rc = RTFuzzObsSetTestBinaryArgs(pFuzzRun->hFuzzObs, papszArgs, cArgs);
385 if (RT_FAILURE(rc))
386 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to set arguments for the fuzzing run");
387 }
388
389 /* Release queried values. */
390 while (idx > 0)
391 {
392 RTJsonValueRelease(pahJsonVal[idx - 1]);
393 idx--;
394 }
395 }
396 else
397 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Out of memory allocating memory for the argument vector");
398
399 if (papszArgs)
400 RTMemFree(papszArgs);
401 if (pahJsonVal)
402 RTMemFree(pahJsonVal);
403 }
404 }
405 else
406 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: \"Arguments\" is not an array");
407 RTJsonValueRelease(hJsonValArgArray);
408 }
409
410 return rc;
411}
412
413
414/**
415 * Processes the given seed and adds it to the input corpus.
416 *
417 * @returns IPRT status code.
418 * @param hFuzzCtx The fuzzing context handle.
419 * @param pszCompression Compression used for the seed.
420 * @param pszSeed The seed as a base64 encoded string.
421 * @param pErrInfo Where to store the error information on failure, optional.
422 */
423static int rtFuzzCmdMasterFuzzRunProcessSeed(RTFUZZCTX hFuzzCtx, const char *pszCompression, const char *pszSeed, PRTERRINFO pErrInfo)
424{
425 int rc = VINF_SUCCESS;
426 ssize_t cbSeedDecoded = RTBase64DecodedSize(pszSeed, NULL);
427 if (cbSeedDecoded > 0)
428 {
429 uint8_t *pbSeedDecoded = (uint8_t *)RTMemAllocZ(cbSeedDecoded);
430 if (RT_LIKELY(pbSeedDecoded))
431 {
432 rc = RTBase64Decode(pszSeed, pbSeedDecoded, cbSeedDecoded, NULL, NULL);
433 if (RT_SUCCESS(rc))
434 {
435 /* Decompress if applicable. */
436 if (!RTStrICmp(pszCompression, "None"))
437 rc = RTFuzzCtxCorpusInputAdd(hFuzzCtx, pbSeedDecoded, cbSeedDecoded);
438 else
439 {
440 RTVFSIOSTREAM hVfsIosSeed;
441 rc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pbSeedDecoded, cbSeedDecoded, &hVfsIosSeed);
442 if (RT_SUCCESS(rc))
443 {
444 RTVFSIOSTREAM hVfsDecomp = NIL_RTVFSIOSTREAM;
445
446 if (!RTStrICmp(pszCompression, "Gzip"))
447 rc = RTZipGzipDecompressIoStream(hVfsIosSeed, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR, &hVfsDecomp);
448 else
449 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Compression \"%s\" is not known", pszCompression);
450
451 if (RT_SUCCESS(rc))
452 {
453 RTVFSFILE hVfsFile;
454 rc = RTVfsMemFileCreate(hVfsDecomp, 2 * _1M, &hVfsFile);
455 if (RT_SUCCESS(rc))
456 {
457 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
458 if (RT_SUCCESS(rc))
459 {
460 /* The VFS file contains the buffer for the seed now. */
461 rc = RTFuzzCtxCorpusInputAddFromVfsFile(hFuzzCtx, hVfsFile);
462 if (RT_FAILURE(rc))
463 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to add input seed");
464 RTVfsFileRelease(hVfsFile);
465 }
466 else
467 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to seek to the beginning of the seed");
468 }
469 else
470 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "Request error: Failed to decompress input seed");
471
472 RTVfsIoStrmRelease(hVfsDecomp);
473 }
474
475 RTVfsIoStrmRelease(hVfsIosSeed);
476 }
477 else
478 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create I/O stream from seed buffer");
479 }
480 }
481 else
482 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to decode the seed string");
483
484 RTMemFree(pbSeedDecoded);
485 }
486 else
487 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Failed to allocate %zd bytes of memory for the seed", cbSeedDecoded);
488 }
489 else
490 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: Couldn't find \"Seed\" doesn't contain a base64 encoded value");
491
492 return rc;
493}
494
495
496/**
497 * Processes a signle input seed for the given fuzzing run.
498 *
499 * @returns IPRT status code.
500 * @param pFuzzRun The fuzzing run.
501 * @param hJsonSeed The seed node of the JSON request.
502 * @param pErrInfo Where to store the error information on failure, optional.
503 */
504static int rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonSeed, PRTERRINFO pErrInfo)
505{
506 RTFUZZCTX hFuzzCtx;
507 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
508 if (RT_SUCCESS(rc))
509 {
510 RTJSONVAL hJsonValComp;
511 rc = RTJsonValueQueryByName(hJsonSeed, "Compression", &hJsonValComp);
512 if (RT_SUCCESS(rc))
513 {
514 const char *pszCompression = RTJsonValueGetString(hJsonValComp);
515 if (RT_LIKELY(pszCompression))
516 {
517 RTJSONVAL hJsonValSeed;
518 rc = RTJsonValueQueryByName(hJsonSeed, "Seed", &hJsonValSeed);
519 if (RT_SUCCESS(rc))
520 {
521 const char *pszSeed = RTJsonValueGetString(hJsonValSeed);
522 if (RT_LIKELY(pszSeed))
523 rc = rtFuzzCmdMasterFuzzRunProcessSeed(hFuzzCtx, pszCompression, pszSeed, pErrInfo);
524 else
525 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Seed\" value is not a string");
526
527 RTJsonValueRelease(hJsonValSeed);
528 }
529 else
530 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Seed\" value");
531 }
532 else
533 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_INVALID_STATE, "JSON request malformed: \"Compression\" value is not a string");
534
535 RTJsonValueRelease(hJsonValComp);
536 }
537 else
538 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Compression\" value");
539
540 RTFuzzCtxRelease(hFuzzCtx);
541 }
542 else
543 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Failed to query fuzzing context from observer");
544
545 return rc;
546}
547
548
549/**
550 * Processes input seed related configs for the given fuzzing run.
551 *
552 * @returns IPRT status code.
553 * @param pFuzzRun The fuzzing run.
554 * @param hJsonRoot The root node of the JSON request.
555 * @param pErrInfo Where to store the error information on failure, optional.
556 */
557static int rtFuzzCmdMasterFuzzRunProcessInputSeeds(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
558{
559 RTJSONVAL hJsonValSeedArray;
560 int rc = RTJsonValueQueryByName(hJsonRoot, "InputSeeds", &hJsonValSeedArray);
561 if (RT_SUCCESS(rc))
562 {
563 RTJSONIT hIt;
564 rc = RTJsonIteratorBegin(hJsonValSeedArray, &hIt);
565 if (RT_SUCCESS(rc))
566 {
567 RTJSONVAL hJsonInpSeed;
568 while ( RT_SUCCESS(rc)
569 && RTJsonIteratorQueryValue(hIt, &hJsonInpSeed, NULL) != VERR_JSON_ITERATOR_END)
570 {
571 rc = rtFuzzCmdMasterFuzzRunProcessInputSeedSingle(pFuzzRun, hJsonInpSeed, pErrInfo);
572 RTJsonValueRelease(hJsonInpSeed);
573 if (RT_FAILURE(rc))
574 break;
575 rc = RTJsonIteratorNext(hIt);
576 }
577
578 if (rc == VERR_JSON_ITERATOR_END)
579 rc = VINF_SUCCESS;
580 }
581 else
582 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Failed to create array iterator");
583
584 RTJsonValueRelease(hJsonValSeedArray);
585 }
586
587 return rc;
588}
589
590
591/**
592 * Processes miscellaneous config items.
593 *
594 * @returns IPRT status code.
595 * @param pFuzzRun The fuzzing run.
596 * @param hJsonRoot The root node of the JSON request.
597 * @param pErrInfo Where to store the error information on failure, optional.
598 */
599static int rtFuzzCmdMasterFuzzRunProcessMiscCfg(PRTFUZZRUN pFuzzRun, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
600{
601 size_t cbTmp;
602 int rc = rtFuzzCmdMasterFuzzRunProcessCfgSizeDef(&cbTmp, "InputSeedMax", hJsonRoot, 0, pErrInfo);
603 if (RT_SUCCESS(rc))
604 {
605 RTFUZZCTX hFuzzCtx;
606 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
607 AssertRC(rc);
608
609 rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbTmp);
610 if (RT_FAILURE(rc))
611 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set maximum input seed size to %zu", cbTmp);
612 }
613
614 if (RT_SUCCESS(rc))
615 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, 0, pErrInfo);
616
617 return rc;
618}
619
620
621/**
622 * Sets up the directories for the given fuzzing run.
623 *
624 * @returns IPRT status code.
625 * @param pThis The fuzzing master command state.
626 * @param pFuzzRun The fuzzing run to setup the directories for.
627 * @param pErrInfo Where to store the error information on failure, optional.
628 */
629static int rtFuzzCmdMasterFuzzRunSetupDirectories(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun, PRTERRINFO pErrInfo)
630{
631 /* Create temp directories. */
632 char szTmpDir[RTPATH_MAX];
633 int rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszTmpDir, pFuzzRun->pszId);
634 AssertRC(rc);
635 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
636 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
637 if (rc == VERR_ALREADY_EXISTS)
638 {
639 /* Clear the directory. */
640 rc = RTDirRemoveRecursive(szTmpDir, RTDIRRMREC_F_CONTENT_ONLY);
641 }
642
643 if (RT_SUCCESS(rc))
644 {
645 rc = RTFuzzObsSetTmpDirectory(pFuzzRun->hFuzzObs, szTmpDir);
646 if (RT_SUCCESS(rc))
647 {
648 rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszResultsDir, pFuzzRun->pszId);
649 AssertRC(rc);
650 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
651 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
652 if (RT_SUCCESS(rc) || rc == VERR_ALREADY_EXISTS)
653 {
654 rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
655 if (RT_FAILURE(rc))
656 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set results directory to %s", szTmpDir);
657 }
658 else
659 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create results directory %s", szTmpDir);
660 }
661 else
662 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set temporary directory to %s", szTmpDir);
663 }
664 else
665 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create temporary directory %s", szTmpDir);
666
667 return rc;
668}
669
670
671/**
672 * Creates a new fuzzing run with the given ID.
673 *
674 * @returns IPRT status code.
675 * @param pThis The fuzzing master command state.
676 * @param pszId The ID to use.
677 * @param hJsonRoot The root node of the JSON request.
678 * @param pErrInfo Where to store the error information on failure, optional.
679 */
680static int rtFuzzCmdMasterCreateFuzzRunWithId(PRTFUZZCMDMASTER pThis, const char *pszId, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
681{
682 int rc = VINF_SUCCESS;
683 PRTFUZZRUN pFuzzRun = (PRTFUZZRUN)RTMemAllocZ(sizeof(*pFuzzRun));
684 if (RT_LIKELY(pFuzzRun))
685 {
686 pFuzzRun->pszId = RTStrDup(pszId);
687 if (RT_LIKELY(pFuzzRun->pszId))
688 {
689 rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs, RTFUZZCTXTYPE_BLOB);
690 if (RT_SUCCESS(rc))
691 {
692 rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
693 if (RT_SUCCESS(rc))
694 rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
695 if (RT_SUCCESS(rc))
696 rc = rtFuzzCmdMasterFuzzRunProcessInputSeeds(pFuzzRun, hJsonRoot, pErrInfo);
697 if (RT_SUCCESS(rc))
698 rc = rtFuzzCmdMasterFuzzRunProcessMiscCfg(pFuzzRun, hJsonRoot, pErrInfo);
699 if (RT_SUCCESS(rc))
700 rc = rtFuzzCmdMasterFuzzRunSetupDirectories(pThis, pFuzzRun, pErrInfo);
701
702 if (RT_SUCCESS(rc))
703 {
704 /* Start fuzzing. */
705 RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
706 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
707 if (RT_SUCCESS(rc))
708 pFuzzRun->fStarted = true;
709 else
710 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
711 }
712 }
713 }
714 else
715 rc = VERR_NO_STR_MEMORY;
716 }
717 else
718 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Out of memory allocating the fuzzer state");
719
720 return rc;
721}
722
723
724/**
725 * Resolves the fuzzing run from the given ID config item and the given JSON request.
726 *
727 * @returns IPRT status code.
728 * @param pThis The fuzzing master command state.
729 * @param hJsonRoot The root node of the JSON request.
730 * @param pszIdItem The JSON item which contains the ID of the fuzzing run.
731 * @param ppFuzzRun Where to store the pointer to the fuzzing run on success.
732 */
733static int rtFuzzCmdMasterQueryFuzzRunFromJson(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, const char *pszIdItem, PRTERRINFO pErrInfo,
734 PRTFUZZRUN *ppFuzzRun)
735{
736 RTJSONVAL hJsonValId;
737 int rc = RTJsonValueQueryByName(hJsonRoot, pszIdItem, &hJsonValId);
738 if (RT_SUCCESS(rc))
739 {
740 const char *pszId = RTJsonValueGetString(hJsonValId);
741 if (pszId)
742 {
743 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
744 if (pFuzzRun)
745 *ppFuzzRun = pFuzzRun;
746 else
747 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "Request error: The ID \"%s\" wasn't found", pszId);
748 }
749 else
750 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
751
752 RTJsonValueRelease(hJsonValId);
753 }
754 else
755 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
756 return rc;
757}
758
759
760/**
761 * Processes the "StartFuzzing" request.
762 *
763 * @returns IPRT status code.
764 * @param pThis The fuzzing master command state.
765 * @param hJsonRoot The root node of the JSON request.
766 * @param pErrInfo Where to store the error information on failure, optional.
767 */
768static int rtFuzzCmdMasterProcessJsonReqStart(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
769{
770 RTJSONVAL hJsonValId;
771 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
772 if (RT_SUCCESS(rc))
773 {
774 const char *pszId = RTJsonValueGetString(hJsonValId);
775 if (pszId)
776 {
777 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
778 if (!pFuzzRun)
779 rc = rtFuzzCmdMasterCreateFuzzRunWithId(pThis, pszId, hJsonRoot, pErrInfo);
780 else
781 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_ALREADY_EXISTS, "Request error: The ID \"%s\" is already registered", pszId);
782 }
783 else
784 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
785
786 RTJsonValueRelease(hJsonValId);
787 }
788 else
789 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
790 return rc;
791}
792
793
794/**
795 * Processes the "StopFuzzing" request.
796 *
797 * @returns IPRT status code.
798 * @param pThis The fuzzing master command state.
799 * @param hJsonValRoot The root node of the JSON request.
800 * @param pErrInfo Where to store the error information on failure, optional.
801 */
802static int rtFuzzCmdMasterProcessJsonReqStop(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
803{
804 PRTFUZZRUN pFuzzRun;
805 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
806 if (RT_SUCCESS(rc))
807 {
808 RTListNodeRemove(&pFuzzRun->NdFuzzed);
809 RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
810 RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
811 RTStrFree(pFuzzRun->pszId);
812 RTMemFree(pFuzzRun);
813 }
814
815 return rc;
816}
817
818
819/**
820 * Processes the "SuspendFuzzing" request.
821 *
822 * @returns IPRT status code.
823 * @param pThis The fuzzing master command state.
824 * @param hJsonValRoot The root node of the JSON request.
825 * @param pErrInfo Where to store the error information on failure, optional.
826 */
827static int rtFuzzCmdMasterProcessJsonReqSuspend(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
828{
829 PRTFUZZRUN pFuzzRun;
830 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
831 if (RT_SUCCESS(rc))
832 {
833 if (pFuzzRun->fStarted)
834 {
835 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
836 if (RT_SUCCESS(rc))
837 pFuzzRun->fStarted = false;
838 else
839 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
840 }
841 }
842
843 return rc;
844}
845
846
847/**
848 * Processes the "ResumeFuzzing" request.
849 *
850 * @returns IPRT status code.
851 * @param pThis The fuzzing master command state.
852 * @param hJsonValRoot The root node of the JSON request.
853 * @param pErrInfo Where to store the error information on failure, optional.
854 */
855static int rtFuzzCmdMasterProcessJsonReqResume(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
856{
857 PRTFUZZRUN pFuzzRun;
858 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
859 if (RT_SUCCESS(rc))
860 {
861 if (!pFuzzRun->fStarted)
862 {
863 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, pFuzzRun->cProcs, pErrInfo);
864 if (RT_SUCCESS(rc))
865 {
866 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
867 if (RT_SUCCESS(rc))
868 pFuzzRun->fStarted = true;
869 else
870 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Resuming the fuzzing process failed");
871 }
872 }
873 }
874
875 return rc;
876}
877
878
879/**
880 * Processes the "SaveFuzzingState" request.
881 *
882 * @returns IPRT status code.
883 * @param pThis The fuzzing master command state.
884 * @param hJsonValRoot The root node of the JSON request.
885 * @param pErrInfo Where to store the error information on failure, optional.
886 */
887static int rtFuzzCmdMasterProcessJsonReqSaveState(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
888{
889 PRTFUZZRUN pFuzzRun;
890 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
891 if (RT_SUCCESS(rc))
892 {
893 /* Suspend fuzzing, save and resume if not stopped. */
894 if (pFuzzRun->fStarted)
895 {
896 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
897 if (RT_FAILURE(rc))
898 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
899 }
900
901 if (RT_SUCCESS(rc))
902 {
903 RTFUZZCTX hFuzzCtx;
904 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
905 AssertRC(rc);
906
907 void *pvState = NULL;
908 size_t cbState = 0;
909 rc = RTFuzzCtxStateExportToMem(hFuzzCtx, &pvState, &cbState);
910 if (RT_SUCCESS(rc))
911 {
912 /* Encode to base64. */
913 size_t cbStateStr = RTBase64EncodedLength(cbState) + 1;
914 char *pszState = (char *)RTMemAllocZ(cbStateStr);
915 if (pszState)
916 {
917 rc = RTBase64Encode(pvState, cbState, pszState, cbStateStr, &cbStateStr);
918 if (RT_SUCCESS(rc))
919 {
920 /* Strip all new lines from the srting. */
921 size_t offStr = 0;
922 while (offStr < cbStateStr)
923 {
924#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
925 char *pszEol = strchr(&pszState[offStr], '\r');
926#else
927 char *pszEol = strchr(&pszState[offStr], '\n');
928#endif
929 if (pszEol)
930 {
931 offStr += pszEol - &pszState[offStr];
932 memmove(pszEol, &pszEol[RTBASE64_EOL_SIZE], cbStateStr - offStr - RTBASE64_EOL_SIZE);
933 cbStateStr -= RTBASE64_EOL_SIZE;
934 }
935 else
936 break;
937 }
938
939 const char s_szState[] = "{ \"State\": %s }";
940 pThis->pszResponse = RTStrAPrintf2(s_szState, pszState);
941 if (RT_UNLIKELY(!pThis->pszResponse))
942 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
943 }
944 else
945 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to encode the state as a base64 string");
946 RTMemFree(pszState);
947 }
948 else
949 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "Request error: Failed to allocate a state string for the response");
950 RTMemFree(pvState);
951 }
952 else
953 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Exporting the state failed");
954 }
955
956 if (pFuzzRun->fStarted)
957 {
958 int rc2 = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
959 if (RT_FAILURE(rc2))
960 rtFuzzCmdMasterErrorRc(pErrInfo, rc2, "Request error: Resuming the fuzzing process failed");
961 }
962 }
963
964 return rc;
965}
966
967
968/**
969 * Queries the statistics for the given fuzzing run and adds the result to the response.
970 *
971 * @returns IPRT static code.
972 * @param pThis The fuzzing master command state.
973 * @param pFuzzRun The fuzzing run.
974 * @param pszIndent Indentation to use.
975 * @param fLast Flags whether this is the last element in the list.
976 * @param pErrInfo Where to store the error information on failure, optional.
977 */
978static int rtFuzzCmdMasterProcessQueryRunStats(PRTFUZZCMDMASTER pThis, PRTFUZZRUN pFuzzRun,
979 const char *pszIndent, bool fLast, PRTERRINFO pErrInfo)
980{
981 RTFUZZOBSSTATS ObsStats;
982 RTFUZZCTXSTATS CtxStats;
983 RTFUZZCTX hFuzzCtx;
984
985 int rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
986 if (RT_SUCCESS(rc))
987 {
988 rc = RTFuzzCtxQueryStats(hFuzzCtx, &CtxStats);
989 RTFuzzCtxRelease(hFuzzCtx);
990 }
991
992 if (RT_SUCCESS(rc))
993 rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &ObsStats);
994 if (RT_SUCCESS(rc))
995 {
996 const char s_szStatsFmt[] = "%s{ \n"
997 "%s \"Id\": %s\n"
998 "%s \"FuzzedInputsPerSec\": %u\n"
999 "%s \"FuzzedInputs\": %u\n"
1000 "%s \"FuzzedInputsHang\": %u\n"
1001 "%s \"FuzzedInputsCrash\": %u\n"
1002 "%s \"MemoryUsage\": %zu\n"
1003 "%s \"CorpusSize\": %llu\n"
1004 "%s}%s\n";
1005 char achStats[_4K]; RT_ZERO(achStats);
1006 ssize_t cchStats = RTStrPrintf2(&achStats[0], sizeof(achStats),
1007 s_szStatsFmt, pszIndent,
1008 pszIndent, pFuzzRun->pszId,
1009 pszIndent, ObsStats.cFuzzedInputsPerSec,
1010 pszIndent, ObsStats.cFuzzedInputs,
1011 pszIndent, ObsStats.cFuzzedInputsHang,
1012 pszIndent, ObsStats.cFuzzedInputsCrash,
1013 pszIndent, CtxStats.cbMemory,
1014 pszIndent, CtxStats.cMutations,
1015 pszIndent, fLast ? "" : ",");
1016 if (RT_LIKELY(cchStats > 0))
1017 {
1018 rc = RTStrAAppend(&pThis->pszResponse, &achStats[0]);
1019 if (RT_FAILURE(rc))
1020 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to build statistics response", rc);
1021 }
1022 else
1023 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
1024 }
1025 else
1026 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
1027
1028 return rc;
1029}
1030
1031
1032/**
1033 * Processes the "QueryStats" request.
1034 *
1035 * @returns IPRT status code.
1036 * @param pThis The fuzzing master command state.
1037 * @param hJsonValRoot The root node of the JSON request.
1038 * @param pErrInfo Where to store the error information on failure, optional.
1039 */
1040static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1041{
1042 RTJSONVAL hJsonValId;
1043 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
1044 if (RT_SUCCESS(rc))
1045 {
1046 RTJsonValueRelease(hJsonValId);
1047 PRTFUZZRUN pFuzzRun;
1048 rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
1049 if (RT_SUCCESS(rc))
1050 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pFuzzRun, " ",
1051 true /*fLast*/, pErrInfo);
1052 }
1053 else if (rc == VERR_NOT_FOUND)
1054 {
1055 /* Id is not there, so collect statistics of all running jobs. */
1056 rc = RTStrAAppend(&pThis->pszResponse, " [\n");
1057 if (RT_SUCCESS(rc))
1058 {
1059 PRTFUZZRUN pRun = NULL;
1060 RTListForEach(&pThis->LstFuzzed, pRun, RTFUZZRUN, NdFuzzed)
1061 {
1062 bool fLast = RTListNodeIsLast(&pThis->LstFuzzed, &pRun->NdFuzzed);
1063 rc = rtFuzzCmdMasterProcessQueryRunStats(pThis, pRun, " ", fLast, pErrInfo);
1064 if (RT_FAILURE(rc))
1065 break;
1066 }
1067 if (RT_SUCCESS(rc))
1068 rc = RTStrAAppend(&pThis->pszResponse, " ]\n");
1069 }
1070 }
1071 else
1072 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't get \"Id\" value");
1073
1074 return rc;
1075}
1076
1077
1078/**
1079 * Processes a JSON request.
1080 *
1081 * @returns IPRT status code.
1082 * @param pThis The fuzzing master command state.
1083 * @param hJsonValRoot The root node of the JSON request.
1084 * @param pErrInfo Where to store the error information on failure, optional.
1085 */
1086static int rtFuzzCmdMasterProcessJsonReq(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
1087{
1088 RTJSONVAL hJsonValReq;
1089 int rc = RTJsonValueQueryByName(hJsonRoot, "Request", &hJsonValReq);
1090 if (RT_SUCCESS(rc))
1091 {
1092 const char *pszReq = RTJsonValueGetString(hJsonValReq);
1093 if (pszReq)
1094 {
1095 if (!RTStrCmp(pszReq, "StartFuzzing"))
1096 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, pErrInfo);
1097 else if (!RTStrCmp(pszReq, "StopFuzzing"))
1098 rc = rtFuzzCmdMasterProcessJsonReqStop(pThis, hJsonRoot, pErrInfo);
1099 else if (!RTStrCmp(pszReq, "SuspendFuzzing"))
1100 rc = rtFuzzCmdMasterProcessJsonReqSuspend(pThis, hJsonRoot, pErrInfo);
1101 else if (!RTStrCmp(pszReq, "ResumeFuzzing"))
1102 rc = rtFuzzCmdMasterProcessJsonReqResume(pThis, hJsonRoot, pErrInfo);
1103 else if (!RTStrCmp(pszReq, "SaveFuzzingState"))
1104 rc = rtFuzzCmdMasterProcessJsonReqSaveState(pThis, hJsonRoot, pErrInfo);
1105 else if (!RTStrCmp(pszReq, "QueryStats"))
1106 rc = rtFuzzCmdMasterProcessJsonReqQueryStats(pThis, hJsonRoot, pErrInfo);
1107 else if (!RTStrCmp(pszReq, "Shutdown"))
1108 pThis->fShutdown = true;
1109 else
1110 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" contains unknown value \"%s\"", pszReq);
1111 }
1112 else
1113 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" is not a string value");
1114
1115 RTJsonValueRelease(hJsonValReq);
1116 }
1117 else
1118 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Request\" value");
1119
1120 return rc;
1121}
1122
1123
1124/**
1125 * Loads a fuzzing configuration for immediate startup from the given file.
1126 *
1127 * @returns IPRT status code.
1128 * @param pThis The fuzzing master command state.
1129 * @param pszFuzzCfg The fuzzing config to load.
1130 */
1131static int rtFuzzCmdMasterFuzzCfgLoadFromFile(PRTFUZZCMDMASTER pThis, const char *pszFuzzCfg)
1132{
1133 RTJSONVAL hJsonRoot;
1134 int rc = RTJsonParseFromFile(&hJsonRoot, pszFuzzCfg, NULL);
1135 if (RT_SUCCESS(rc))
1136 {
1137 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, NULL);
1138 RTJsonValueRelease(hJsonRoot);
1139 }
1140 else
1141 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "JSON request malformed: Couldn't load file \"%s\"", pszFuzzCfg);
1142
1143 return rc;
1144}
1145
1146
1147/**
1148 * Destroys all running fuzzers for the given master state.
1149 *
1150 * @returns nothing.
1151 * @param pThis The fuzzing master command state.
1152 */
1153static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
1154{
1155 RT_NOREF(pThis);
1156}
1157
1158
1159/**
1160 * Sends an ACK response to the client.
1161 *
1162 * @returns nothing.
1163 * @param hSocket The socket handle to send the ACK to.
1164 * @param pszResponse Additional response data.
1165 */
1166static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
1167{
1168 const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
1169 const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n \"Response\":\n";
1170 const char s_szSuccRespClose[] = "\n }\n";
1171 if (pszResponse)
1172 {
1173 RTSGSEG aSegs[3];
1174 RTSGBUF SgBuf;
1175 aSegs[0].pvSeg = (void *)s_szSuccResp;
1176 aSegs[0].cbSeg = sizeof(s_szSuccResp) - 1;
1177 aSegs[1].pvSeg = (void *)pszResponse;
1178 aSegs[1].cbSeg = strlen(pszResponse);
1179 aSegs[2].pvSeg = (void *)s_szSuccRespClose;
1180 aSegs[2].cbSeg = sizeof(s_szSuccRespClose) - 1;
1181
1182 RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
1183 RTTcpSgWrite(hSocket, &SgBuf);
1184 }
1185 else
1186 RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
1187}
1188
1189
1190/**
1191 * Sends an NACK response to the client.
1192 *
1193 * @returns nothing.
1194 * @param hSocket The socket handle to send the ACK to.
1195 * @param pErrInfo Optional error information to send along.
1196 */
1197static void rtFuzzCmdMasterTcpSendNAck(RTSOCKET hSocket, PRTERRINFO pErrInfo)
1198{
1199 const char s_szFail[] = "{ \"Status\": \"NACK\" }\n";
1200 const char s_szFailInfo[] = "{ \"Status\": \"NACK\"\n \"Information\": \"%s\" }\n";
1201
1202 if (pErrInfo)
1203 {
1204 char szTmp[_1K];
1205 ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
1206 if (cchResp > 0)
1207 RTTcpWrite(hSocket, szTmp, cchResp);
1208 else
1209 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1210 }
1211 else
1212 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1213}
1214
1215
1216/**
1217 * TCP server serving callback for a single connection.
1218 *
1219 * @returns IPRT status code.
1220 * @param hSocket The socket handle of the connection.
1221 * @param pvUser Opaque user data.
1222 */
1223static DECLCALLBACK(int) rtFuzzCmdMasterTcpServe(RTSOCKET hSocket, void *pvUser)
1224{
1225 PRTFUZZCMDMASTER pThis = (PRTFUZZCMDMASTER)pvUser;
1226 size_t cbReqMax = _32K;
1227 size_t cbReq = 0;
1228 uint8_t *pbReq = (uint8_t *)RTMemAllocZ(cbReqMax);
1229
1230 if (RT_LIKELY(pbReq))
1231 {
1232 uint8_t *pbCur = pbReq;
1233
1234 for (;;)
1235 {
1236 size_t cbThisRead = cbReqMax - cbReq;
1237 int rc = RTTcpRead(hSocket, pbCur, cbThisRead, &cbThisRead);
1238 if ( RT_SUCCESS(rc)
1239 && cbThisRead)
1240 {
1241 cbReq += cbThisRead;
1242
1243 /* Check for a zero terminator marking the end of the request. */
1244 uint8_t *pbEnd = (uint8_t *)memchr(pbCur, 0, cbThisRead);
1245 if (pbEnd)
1246 {
1247 /* Adjust request size, data coming after the zero terminiator is ignored right now. */
1248 cbReq -= cbThisRead - (pbEnd - pbCur) + 1;
1249
1250 RTJSONVAL hJsonReq;
1251 RTERRINFOSTATIC ErrInfo;
1252 RTErrInfoInitStatic(&ErrInfo);
1253
1254 rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
1255 if (RT_SUCCESS(rc))
1256 {
1257 rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
1258 if (RT_SUCCESS(rc))
1259 rtFuzzCmdMasterTcpSendAck(hSocket, pThis->pszResponse);
1260 else
1261 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1262 RTJsonValueRelease(hJsonReq);
1263 }
1264 else
1265 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1266
1267 if (pThis->pszResponse)
1268 {
1269 RTStrFree(pThis->pszResponse);
1270 pThis->pszResponse = NULL;
1271 }
1272 break;
1273 }
1274 else if (cbReq == cbReqMax)
1275 {
1276 /* Try to increase the buffer. */
1277 uint8_t *pbReqNew = (uint8_t *)RTMemRealloc(pbReq, cbReqMax + _32K);
1278 if (RT_LIKELY(pbReqNew))
1279 {
1280 cbReqMax += _32K;
1281 pbReq = pbReqNew;
1282 pbCur = pbReq + cbReq;
1283 }
1284 else
1285 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1286 }
1287 else
1288 pbCur += cbThisRead;
1289 }
1290 else
1291 break;
1292 }
1293 }
1294 else
1295 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1296
1297 if (pbReq)
1298 RTMemFree(pbReq);
1299
1300 return pThis->fShutdown ? VERR_TCP_SERVER_STOP : VINF_SUCCESS;
1301}
1302
1303
1304/**
1305 * Mainloop for the fuzzing master.
1306 *
1307 * @returns Process exit code.
1308 * @param pThis The fuzzing master command state.
1309 * @param pszLoadCfg Initial config to load.
1310 */
1311static RTEXITCODE rtFuzzCmdMasterRun(PRTFUZZCMDMASTER pThis, const char *pszLoadCfg)
1312{
1313 if (pszLoadCfg)
1314 {
1315 int rc = rtFuzzCmdMasterFuzzCfgLoadFromFile(pThis, pszLoadCfg);
1316 if (RT_FAILURE(rc))
1317 return RTEXITCODE_FAILURE;
1318 }
1319
1320 /* Start up the control server. */
1321 int rc = RTTcpServerCreateEx(NULL, pThis->uPort, &pThis->hTcpSrv);
1322 if (RT_SUCCESS(rc))
1323 {
1324 do
1325 {
1326 rc = RTTcpServerListen(pThis->hTcpSrv, rtFuzzCmdMasterTcpServe, pThis);
1327 } while (rc != VERR_TCP_SERVER_STOP);
1328 }
1329
1330 RTTcpServerDestroy(pThis->hTcpSrv);
1331 rtFuzzCmdMasterDestroy(pThis);
1332 return RTEXITCODE_SUCCESS;
1333}
1334
1335
1336RTR3DECL(RTEXITCODE) RTFuzzCmdMaster(unsigned cArgs, char **papszArgs)
1337{
1338 /*
1339 * Parse the command line.
1340 */
1341 static const RTGETOPTDEF s_aOptions[] =
1342 {
1343 { "--fuzz-config", 'c', RTGETOPT_REQ_STRING },
1344 { "--temp-dir", 't', RTGETOPT_REQ_STRING },
1345 { "--results-dir", 'r', RTGETOPT_REQ_STRING },
1346 { "--listen-port", 'p', RTGETOPT_REQ_UINT16 },
1347 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
1348 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
1349 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1350 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1351 };
1352
1353 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1354 RTGETOPTSTATE GetState;
1355 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1356 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1357 if (RT_SUCCESS(rc))
1358 {
1359 /* Option variables: */
1360 bool fDaemonize = false;
1361 bool fDaemonized = false;
1362 const char *pszLoadCfg = NULL;
1363 RTFUZZCMDMASTER This;
1364
1365 RTListInit(&This.LstFuzzed);
1366 This.hTcpSrv = NIL_RTTCPSERVER;
1367 This.uPort = 4242;
1368 This.pszTmpDir = NULL;
1369 This.pszResultsDir = NULL;
1370 This.fShutdown = false;
1371 This.pszResponse = NULL;
1372
1373 /* Argument parsing loop. */
1374 bool fContinue = true;
1375 do
1376 {
1377 RTGETOPTUNION ValueUnion;
1378 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1379 switch (chOpt)
1380 {
1381 case 0:
1382 fContinue = false;
1383 break;
1384
1385 case 'c':
1386 pszLoadCfg = ValueUnion.psz;
1387 break;
1388
1389 case 'p':
1390 This.uPort = ValueUnion.u16;
1391 break;
1392
1393 case 't':
1394 This.pszTmpDir = ValueUnion.psz;
1395 break;
1396
1397 case 'r':
1398 This.pszResultsDir = ValueUnion.psz;
1399 break;
1400
1401 case 'd':
1402 fDaemonize = true;
1403 break;
1404
1405 case 'Z':
1406 fDaemonized = true;
1407 fDaemonize = false;
1408 break;
1409
1410 case 'h':
1411 RTPrintf("Usage: to be written\nOption dump:\n");
1412 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1413 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1414 fContinue = false;
1415 break;
1416
1417 case 'V':
1418 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1419 fContinue = false;
1420 break;
1421
1422 default:
1423 rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
1424 fContinue = false;
1425 break;
1426 }
1427 } while (fContinue);
1428
1429 if (rcExit == RTEXITCODE_SUCCESS)
1430 {
1431 /*
1432 * Daemonize ourselves if asked to.
1433 */
1434 if (fDaemonize)
1435 {
1436 rc = RTProcDaemonize(papszArgs, "--daemonized");
1437 if (RT_FAILURE(rc))
1438 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1439 }
1440 else
1441 rcExit = rtFuzzCmdMasterRun(&This, pszLoadCfg);
1442 }
1443 }
1444 else
1445 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1446 return rcExit;
1447}
1448
Note: See TracBrowser for help on using the repository browser.

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