VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.7 KB
Line 
1/* $Id: fuzzmastercmd.cpp 76553 2019-01-01 01:45:53Z 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.virtualbox.org. 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 * Creates a new fuzzing run with the given ID.
623 *
624 * @returns IPRT status code.
625 * @param pThis The fuzzing master command state.
626 * @param pszId The ID to use.
627 * @param hJsonRoot The root node of the JSON request.
628 * @param pErrInfo Where to store the error information on failure, optional.
629 */
630static int rtFuzzCmdMasterCreateFuzzRunWithId(PRTFUZZCMDMASTER pThis, const char *pszId, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
631{
632 int rc = VINF_SUCCESS;
633 PRTFUZZRUN pFuzzRun = (PRTFUZZRUN)RTMemAllocZ(sizeof(*pFuzzRun));
634 if (RT_LIKELY(pFuzzRun))
635 {
636 pFuzzRun->pszId = RTStrDup(pszId);
637 if (RT_LIKELY(pFuzzRun->pszId))
638 {
639 rc = RTFuzzObsCreate(&pFuzzRun->hFuzzObs);
640 if (RT_SUCCESS(rc))
641 {
642 rc = rtFuzzCmdMasterFuzzRunProcessBinaryCfg(pFuzzRun, hJsonRoot, pErrInfo);
643 if (RT_SUCCESS(rc))
644 rc = rtFuzzCmdMasterFuzzRunProcessArgCfg(pFuzzRun, hJsonRoot, pErrInfo);
645 if (RT_SUCCESS(rc))
646 rc = rtFuzzCmdMasterFuzzRunProcessInputSeeds(pFuzzRun, hJsonRoot, pErrInfo);
647 if (RT_SUCCESS(rc))
648 rc = rtFuzzCmdMasterFuzzRunProcessMiscCfg(pFuzzRun, hJsonRoot, pErrInfo);
649 if (RT_SUCCESS(rc))
650 {
651 /* Create temp directories. */
652 char szTmpDir[RTPATH_MAX];
653 rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszTmpDir, pFuzzRun->pszId);
654 AssertRC(rc);
655 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
656 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
657 if (RT_SUCCESS(rc))
658 {
659 rc = RTFuzzObsSetTmpDirectory(pFuzzRun->hFuzzObs, szTmpDir);
660 if (RT_SUCCESS(rc))
661 {
662 rc = RTPathJoin(&szTmpDir[0], sizeof(szTmpDir), pThis->pszResultsDir, pFuzzRun->pszId);
663 AssertRC(rc);
664 rc = RTDirCreate(szTmpDir, 0700, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET
665 | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
666 if (RT_SUCCESS(rc))
667 {
668 rc = RTFuzzObsSetResultDirectory(pFuzzRun->hFuzzObs, szTmpDir);
669 if (RT_SUCCESS(rc))
670 {
671 /* Start fuzzing. */
672 RTListAppend(&pThis->LstFuzzed, &pFuzzRun->NdFuzzed);
673 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
674 if (RT_SUCCESS(rc))
675 pFuzzRun->fStarted = true;
676 else
677 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to start fuzzing with %Rrc", rc);
678 }
679 else
680 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set results directory to %s", szTmpDir);
681 }
682 else
683 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create results directory %s", szTmpDir);
684 }
685 else
686 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to set temporary directory to %s", szTmpDir);
687 }
688 else
689 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to create temporary directory %s", szTmpDir);
690 }
691 }
692 }
693 else
694 rc = VERR_NO_STR_MEMORY;
695 }
696 else
697 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_MEMORY, "Request error: Out of memory allocating the fuzzer state");
698
699 return rc;
700}
701
702
703/**
704 * Resolves the fuzzing run from the given ID config item and the given JSON request.
705 *
706 * @returns IPRT status code.
707 * @param pThis The fuzzing master command state.
708 * @param hJsonRoot The root node of the JSON request.
709 * @param pszIdItem The JSON item which contains the ID of the fuzzing run.
710 * @param ppFuzzRun Where to store the pointer to the fuzzing run on success.
711 */
712static int rtFuzzCmdMasterQueryFuzzRunFromJson(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, const char *pszIdItem, PRTERRINFO pErrInfo,
713 PRTFUZZRUN *ppFuzzRun)
714{
715 RTJSONVAL hJsonValId;
716 int rc = RTJsonValueQueryByName(hJsonRoot, pszIdItem, &hJsonValId);
717 if (RT_SUCCESS(rc))
718 {
719 const char *pszId = RTJsonValueGetString(hJsonValId);
720 if (pszId)
721 {
722 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
723 if (pFuzzRun)
724 *ppFuzzRun = pFuzzRun;
725 else
726 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NOT_FOUND, "Request error: The ID \"%s\" wasn't found", pszId);
727 }
728 else
729 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
730
731 RTJsonValueRelease(hJsonValId);
732 }
733 else
734 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
735 return rc;
736}
737
738
739/**
740 * Processes the "StartFuzzing" request.
741 *
742 * @returns IPRT status code.
743 * @param pThis The fuzzing master command state.
744 * @param hJsonRoot The root node of the JSON request.
745 * @param pErrInfo Where to store the error information on failure, optional.
746 */
747static int rtFuzzCmdMasterProcessJsonReqStart(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
748{
749 RTJSONVAL hJsonValId;
750 int rc = RTJsonValueQueryByName(hJsonRoot, "Id", &hJsonValId);
751 if (RT_SUCCESS(rc))
752 {
753 const char *pszId = RTJsonValueGetString(hJsonValId);
754 if (pszId)
755 {
756 PRTFUZZRUN pFuzzRun = rtFuzzCmdMasterGetFuzzerById(pThis, pszId);
757 if (!pFuzzRun)
758 rc = rtFuzzCmdMasterCreateFuzzRunWithId(pThis, pszId, hJsonRoot, pErrInfo);
759 else
760 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_ALREADY_EXISTS, "Request error: The ID \"%s\" is already registered", pszId);
761 }
762 else
763 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Id\" is not a string value");
764
765 RTJsonValueRelease(hJsonValId);
766 }
767 else
768 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Id\" value");
769 return rc;
770}
771
772
773/**
774 * Processes the "StopFuzzing" request.
775 *
776 * @returns IPRT status code.
777 * @param pThis The fuzzing master command state.
778 * @param hJsonValRoot The root node of the JSON request.
779 * @param pErrInfo Where to store the error information on failure, optional.
780 */
781static int rtFuzzCmdMasterProcessJsonReqStop(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
782{
783 PRTFUZZRUN pFuzzRun;
784 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
785 if (RT_SUCCESS(rc))
786 {
787 RTListNodeRemove(&pFuzzRun->NdFuzzed);
788 RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
789 RTFuzzObsDestroy(pFuzzRun->hFuzzObs);
790 RTStrFree(pFuzzRun->pszId);
791 RTMemFree(pFuzzRun);
792 }
793
794 return rc;
795}
796
797
798/**
799 * Processes the "SuspendFuzzing" request.
800 *
801 * @returns IPRT status code.
802 * @param pThis The fuzzing master command state.
803 * @param hJsonValRoot The root node of the JSON request.
804 * @param pErrInfo Where to store the error information on failure, optional.
805 */
806static int rtFuzzCmdMasterProcessJsonReqSuspend(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
807{
808 PRTFUZZRUN pFuzzRun;
809 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
810 if (RT_SUCCESS(rc))
811 {
812 if (pFuzzRun->fStarted)
813 {
814 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
815 if (RT_SUCCESS(rc))
816 pFuzzRun->fStarted = false;
817 else
818 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
819 }
820 }
821
822 return rc;
823}
824
825
826/**
827 * Processes the "ResumeFuzzing" request.
828 *
829 * @returns IPRT status code.
830 * @param pThis The fuzzing master command state.
831 * @param hJsonValRoot The root node of the JSON request.
832 * @param pErrInfo Where to store the error information on failure, optional.
833 */
834static int rtFuzzCmdMasterProcessJsonReqResume(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
835{
836 PRTFUZZRUN pFuzzRun;
837 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
838 if (RT_SUCCESS(rc))
839 {
840 if (!pFuzzRun->fStarted)
841 {
842 rc = rtFuzzCmdMasterFuzzRunProcessCfgU32Def(&pFuzzRun->cProcs, "FuzzingProcs", hJsonRoot, pFuzzRun->cProcs, pErrInfo);
843 if (RT_SUCCESS(rc))
844 {
845 rc = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
846 if (RT_SUCCESS(rc))
847 pFuzzRun->fStarted = true;
848 else
849 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Resuming the fuzzing process failed");
850 }
851 }
852 }
853
854 return rc;
855}
856
857
858/**
859 * Processes the "SaveFuzzingState" request.
860 *
861 * @returns IPRT status code.
862 * @param pThis The fuzzing master command state.
863 * @param hJsonValRoot The root node of the JSON request.
864 * @param pErrInfo Where to store the error information on failure, optional.
865 */
866static int rtFuzzCmdMasterProcessJsonReqSaveState(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
867{
868 PRTFUZZRUN pFuzzRun;
869 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
870 if (RT_SUCCESS(rc))
871 {
872 /* Suspend fuzzing, save and resume if not stopped. */
873 if (pFuzzRun->fStarted)
874 {
875 rc = RTFuzzObsExecStop(pFuzzRun->hFuzzObs);
876 if (RT_FAILURE(rc))
877 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Suspending the fuzzing process failed");
878 }
879
880 if (RT_SUCCESS(rc))
881 {
882 RTFUZZCTX hFuzzCtx;
883 rc = RTFuzzObsQueryCtx(pFuzzRun->hFuzzObs, &hFuzzCtx);
884 AssertRC(rc);
885
886 void *pvState = NULL;
887 size_t cbState = 0;
888 rc = RTFuzzCtxStateExport(hFuzzCtx, &pvState, &cbState);
889 if (RT_SUCCESS(rc))
890 {
891 /* Encode to base64. */
892 size_t cbStateStr = RTBase64EncodedLength(cbState) + 1;
893 char *pszState = (char *)RTMemAllocZ(cbStateStr);
894 if (pszState)
895 {
896 rc = RTBase64Encode(pvState, cbState, pszState, cbStateStr, &cbStateStr);
897 if (RT_SUCCESS(rc))
898 {
899 /* Strip all new lines from the srting. */
900 size_t offStr = 0;
901 while (offStr < cbStateStr)
902 {
903#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
904 char *pszEol = strchr(&pszState[offStr], '\r');
905#else
906 char *pszEol = strchr(&pszState[offStr], '\n');
907#endif
908 if (pszEol)
909 {
910 offStr += pszEol - &pszState[offStr];
911 memmove(pszEol, &pszEol[RTBASE64_EOL_SIZE], cbStateStr - offStr - RTBASE64_EOL_SIZE);
912 cbStateStr -= RTBASE64_EOL_SIZE;
913 }
914 else
915 break;
916 }
917
918 const char s_szState[] = "{ \"State\": %s }";
919 pThis->pszResponse = RTStrAPrintf2(s_szState, pszState);
920 if (RT_UNLIKELY(!pThis->pszResponse))
921 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
922 }
923 else
924 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to encode the state as a base64 string");
925 RTMemFree(pszState);
926 }
927 else
928 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_NO_STR_MEMORY, "Request error: Failed to allocate a state string for the response");
929 RTMemFree(pvState);
930 }
931 else
932 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Exporting the state failed");
933 }
934
935 if (pFuzzRun->fStarted)
936 {
937 int rc2 = RTFuzzObsExecStart(pFuzzRun->hFuzzObs, pFuzzRun->cProcs);
938 if (RT_FAILURE(rc2))
939 rtFuzzCmdMasterErrorRc(pErrInfo, rc2, "Request error: Resuming the fuzzing process failed");
940 }
941 }
942
943 return rc;
944}
945
946
947/**
948 * Processes the "QueryStats" request.
949 *
950 * @returns IPRT status code.
951 * @param pThis The fuzzing master command state.
952 * @param hJsonValRoot The root node of the JSON request.
953 * @param pErrInfo Where to store the error information on failure, optional.
954 */
955static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
956{
957 PRTFUZZRUN pFuzzRun;
958 int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
959 if (RT_SUCCESS(rc))
960 {
961 RTFUZZOBSSTATS Stats;
962 rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &Stats);
963 if (RT_SUCCESS(rc))
964 {
965 const char s_szStats[] = "{ \"FuzzedInputsPerSec\": %u\n"
966 " \"FuzzedInputs\": %u\n"
967 " \"FuzzedInputsHang\": %u\n"
968 " \"FuzzedInputsCrash\": %u\n}";
969 pThis->pszResponse = RTStrAPrintf2(s_szStats, Stats.cFuzzedInputsPerSec,
970 Stats.cFuzzedInputs, Stats.cFuzzedInputsHang, Stats.cFuzzedInputsCrash);
971 if (RT_UNLIKELY(!pThis->pszResponse))
972 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
973 }
974 else
975 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
976 }
977
978 return rc;
979}
980
981
982/**
983 * Processes a JSON request.
984 *
985 * @returns IPRT status code.
986 * @param pThis The fuzzing master command state.
987 * @param hJsonValRoot The root node of the JSON request.
988 * @param pErrInfo Where to store the error information on failure, optional.
989 */
990static int rtFuzzCmdMasterProcessJsonReq(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
991{
992 RTJSONVAL hJsonValReq;
993 int rc = RTJsonValueQueryByName(hJsonRoot, "Request", &hJsonValReq);
994 if (RT_SUCCESS(rc))
995 {
996 const char *pszReq = RTJsonValueGetString(hJsonValReq);
997 if (pszReq)
998 {
999 if (!RTStrCmp(pszReq, "StartFuzzing"))
1000 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, pErrInfo);
1001 else if (!RTStrCmp(pszReq, "StopFuzzing"))
1002 rc = rtFuzzCmdMasterProcessJsonReqStop(pThis, hJsonRoot, pErrInfo);
1003 else if (!RTStrCmp(pszReq, "SuspendFuzzing"))
1004 rc = rtFuzzCmdMasterProcessJsonReqSuspend(pThis, hJsonRoot, pErrInfo);
1005 else if (!RTStrCmp(pszReq, "ResumeFuzzing"))
1006 rc = rtFuzzCmdMasterProcessJsonReqResume(pThis, hJsonRoot, pErrInfo);
1007 else if (!RTStrCmp(pszReq, "SaveFuzzingState"))
1008 rc = rtFuzzCmdMasterProcessJsonReqSaveState(pThis, hJsonRoot, pErrInfo);
1009 else if (!RTStrCmp(pszReq, "QueryStats"))
1010 rc = rtFuzzCmdMasterProcessJsonReqQueryStats(pThis, hJsonRoot, pErrInfo);
1011 else if (!RTStrCmp(pszReq, "Shutdown"))
1012 pThis->fShutdown = true;
1013 else
1014 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" contains unknown value \"%s\"", pszReq);
1015 }
1016 else
1017 rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_JSON_VALUE_INVALID_TYPE, "JSON request malformed: \"Request\" is not a string value");
1018
1019 RTJsonValueRelease(hJsonValReq);
1020 }
1021 else
1022 rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "JSON request malformed: Couldn't find \"Request\" value");
1023
1024 return rc;
1025}
1026
1027
1028/**
1029 * Loads a fuzzing configuration for immediate startup from the given file.
1030 *
1031 * @returns IPRT status code.
1032 * @param pThis The fuzzing master command state.
1033 * @param pszFuzzCfg The fuzzing config to load.
1034 */
1035static int rtFuzzCmdMasterFuzzCfgLoadFromFile(PRTFUZZCMDMASTER pThis, const char *pszFuzzCfg)
1036{
1037 RTJSONVAL hJsonRoot;
1038 int rc = RTJsonParseFromFile(&hJsonRoot, pszFuzzCfg, NULL);
1039 if (RT_SUCCESS(rc))
1040 {
1041 rc = rtFuzzCmdMasterProcessJsonReqStart(pThis, hJsonRoot, NULL);
1042 RTJsonValueRelease(hJsonRoot);
1043 }
1044 else
1045 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "JSON request malformed: Couldn't load file \"%s\"", pszFuzzCfg);
1046
1047 return rc;
1048}
1049
1050
1051/**
1052 * Destroys all running fuzzers for the given master state.
1053 *
1054 * @returns nothing.
1055 * @param pThis The fuzzing master command state.
1056 */
1057static void rtFuzzCmdMasterDestroy(PRTFUZZCMDMASTER pThis)
1058{
1059 RT_NOREF(pThis);
1060}
1061
1062
1063/**
1064 * Sends an ACK response to the client.
1065 *
1066 * @returns nothing.
1067 * @param hSocket The socket handle to send the ACK to.
1068 * @param pszResponse Additional response data.
1069 */
1070static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
1071{
1072 const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
1073 const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n \"Response\":\n";
1074 const char s_szSuccRespClose[] = "\n }\n";
1075 if (pszResponse)
1076 {
1077 RTSGSEG aSegs[3];
1078 RTSGBUF SgBuf;
1079 aSegs[0].pvSeg = (void *)s_szSuccResp;
1080 aSegs[0].cbSeg = sizeof(s_szSuccResp) - 1;
1081 aSegs[1].pvSeg = (void *)pszResponse;
1082 aSegs[1].cbSeg = strlen(pszResponse);
1083 aSegs[2].pvSeg = (void *)s_szSuccRespClose;
1084 aSegs[2].cbSeg = sizeof(s_szSuccRespClose) - 1;
1085
1086 RTSgBufInit(&SgBuf, &aSegs[0], RT_ELEMENTS(aSegs));
1087 RTTcpSgWrite(hSocket, &SgBuf);
1088 }
1089 else
1090 RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
1091}
1092
1093
1094/**
1095 * Sends an NACK response to the client.
1096 *
1097 * @returns nothing.
1098 * @param hSocket The socket handle to send the ACK to.
1099 * @param pErrInfo Optional error information to send along.
1100 */
1101static void rtFuzzCmdMasterTcpSendNAck(RTSOCKET hSocket, PRTERRINFO pErrInfo)
1102{
1103 const char s_szFail[] = "{ \"Status\": \"NACK\" }\n";
1104 const char s_szFailInfo[] = "{ \"Status\": \"NACK\"\n \"Information\": \"%s\" }\n";
1105
1106 if (pErrInfo)
1107 {
1108 char szTmp[_1K];
1109 ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
1110 if (cchResp > 0)
1111 RTTcpWrite(hSocket, szTmp, cchResp);
1112 else
1113 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1114 }
1115 else
1116 RTTcpWrite(hSocket, s_szFail, strlen(s_szFail));
1117}
1118
1119
1120/**
1121 * TCP server serving callback for a single connection.
1122 *
1123 * @returns IPRT status code.
1124 * @param hSocket The socket handle of the connection.
1125 * @param pvUser Opaque user data.
1126 */
1127static DECLCALLBACK(int) rtFuzzCmdMasterTcpServe(RTSOCKET hSocket, void *pvUser)
1128{
1129 PRTFUZZCMDMASTER pThis = (PRTFUZZCMDMASTER)pvUser;
1130 size_t cbReqMax = _32K;
1131 size_t cbReq = 0;
1132 uint8_t *pbReq = (uint8_t *)RTMemAllocZ(cbReqMax);
1133
1134 if (RT_LIKELY(pbReq))
1135 {
1136 uint8_t *pbCur = pbReq;
1137
1138 for (;;)
1139 {
1140 size_t cbThisRead = cbReqMax - cbReq;
1141 int rc = RTTcpRead(hSocket, pbCur, cbThisRead, &cbThisRead);
1142 if (RT_SUCCESS(rc))
1143 {
1144 cbReq += cbThisRead;
1145
1146 /* Check for a zero terminator marking the end of the request. */
1147 uint8_t *pbEnd = (uint8_t *)memchr(pbCur, 0, cbThisRead);
1148 if (pbEnd)
1149 {
1150 /* Adjust request size, data coming after the zero terminiator is ignored right now. */
1151 cbReq -= cbThisRead - (pbEnd - pbCur) + 1;
1152
1153 RTJSONVAL hJsonReq;
1154 RTERRINFOSTATIC ErrInfo;
1155 RTErrInfoInitStatic(&ErrInfo);
1156
1157 rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
1158 if (RT_SUCCESS(rc))
1159 {
1160 rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
1161 if (RT_SUCCESS(rc))
1162 rtFuzzCmdMasterTcpSendAck(hSocket, pThis->pszResponse);
1163 else
1164 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1165 RTJsonValueRelease(hJsonReq);
1166 }
1167 else
1168 rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
1169
1170 if (pThis->pszResponse)
1171 {
1172 RTStrFree(pThis->pszResponse);
1173 pThis->pszResponse = NULL;
1174 }
1175 break;
1176 }
1177 else if (cbReq == cbReqMax)
1178 {
1179 /* Try to increase the buffer. */
1180 uint8_t *pbReqNew = (uint8_t *)RTMemRealloc(pbReq, cbReqMax + _32K);
1181 if (RT_LIKELY(pbReqNew))
1182 {
1183 cbReqMax += _32K;
1184 pbReq = pbReqNew;
1185 pbCur = pbReq + cbReq;
1186 }
1187 else
1188 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1189 }
1190 else
1191 pbCur += cbThisRead;
1192 }
1193 else
1194 break;
1195 }
1196 }
1197 else
1198 rtFuzzCmdMasterTcpSendNAck(hSocket, NULL);
1199
1200 if (pbReq)
1201 RTMemFree(pbReq);
1202
1203 return pThis->fShutdown ? VERR_TCP_SERVER_STOP : VINF_SUCCESS;
1204}
1205
1206
1207/**
1208 * Mainloop for the fuzzing master.
1209 *
1210 * @returns Process exit code.
1211 * @param pThis The fuzzing master command state.
1212 * @param pszLoadCfg Initial config to load.
1213 */
1214static RTEXITCODE rtFuzzCmdMasterRun(PRTFUZZCMDMASTER pThis, const char *pszLoadCfg)
1215{
1216 if (pszLoadCfg)
1217 {
1218 int rc = rtFuzzCmdMasterFuzzCfgLoadFromFile(pThis, pszLoadCfg);
1219 if (RT_FAILURE(rc))
1220 return RTEXITCODE_FAILURE;
1221 }
1222
1223 /* Start up the control server. */
1224 int rc = RTTcpServerCreateEx(NULL, pThis->uPort, &pThis->hTcpSrv);
1225 if (RT_SUCCESS(rc))
1226 {
1227 do
1228 {
1229 rc = RTTcpServerListen(pThis->hTcpSrv, rtFuzzCmdMasterTcpServe, pThis);
1230 } while (rc != VERR_TCP_SERVER_STOP);
1231 }
1232
1233 RTTcpServerDestroy(pThis->hTcpSrv);
1234 rtFuzzCmdMasterDestroy(pThis);
1235 return RTEXITCODE_SUCCESS;
1236}
1237
1238
1239RTR3DECL(RTEXITCODE) RTFuzzCmdMaster(unsigned cArgs, char **papszArgs)
1240{
1241 /*
1242 * Parse the command line.
1243 */
1244 static const RTGETOPTDEF s_aOptions[] =
1245 {
1246 { "--fuzz-config", 'c', RTGETOPT_REQ_STRING },
1247 { "--temp-dir", 't', RTGETOPT_REQ_STRING },
1248 { "--results-dir", 'r', RTGETOPT_REQ_STRING },
1249 { "--listen-port", 'p', RTGETOPT_REQ_UINT16 },
1250 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
1251 { "--daemonized", 'Z', RTGETOPT_REQ_NOTHING },
1252 { "--help", 'h', RTGETOPT_REQ_NOTHING },
1253 { "--version", 'V', RTGETOPT_REQ_NOTHING },
1254 };
1255
1256 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1257 RTGETOPTSTATE GetState;
1258 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1259 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1260 if (RT_SUCCESS(rc))
1261 {
1262 /* Option variables: */
1263 bool fDaemonize = false;
1264 bool fDaemonized = false;
1265 const char *pszLoadCfg = NULL;
1266 RTFUZZCMDMASTER This;
1267
1268 RTListInit(&This.LstFuzzed);
1269 This.hTcpSrv = NIL_RTTCPSERVER;
1270 This.uPort = 4242;
1271 This.pszTmpDir = NULL;
1272 This.pszResultsDir = NULL;
1273 This.fShutdown = false;
1274 This.pszResponse = NULL;
1275
1276 /* Argument parsing loop. */
1277 bool fContinue = true;
1278 do
1279 {
1280 RTGETOPTUNION ValueUnion;
1281 int chOpt = RTGetOpt(&GetState, &ValueUnion);
1282 switch (chOpt)
1283 {
1284 case 0:
1285 fContinue = false;
1286 break;
1287
1288 case 'c':
1289 pszLoadCfg = ValueUnion.psz;
1290 break;
1291
1292 case 'p':
1293 This.uPort = ValueUnion.u16;
1294 break;
1295
1296 case 't':
1297 This.pszTmpDir = ValueUnion.psz;
1298 break;
1299
1300 case 'r':
1301 This.pszResultsDir = ValueUnion.psz;
1302 break;
1303
1304 case 'd':
1305 fDaemonize = true;
1306 break;
1307
1308 case 'Z':
1309 fDaemonized = true;
1310 fDaemonize = false;
1311 break;
1312
1313 case 'h':
1314 RTPrintf("Usage: to be written\nOption dump:\n");
1315 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
1316 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
1317 fContinue = false;
1318 break;
1319
1320 case 'V':
1321 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
1322 fContinue = false;
1323 break;
1324
1325 default:
1326 rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
1327 fContinue = false;
1328 break;
1329 }
1330 } while (fContinue);
1331
1332 if (rcExit == RTEXITCODE_SUCCESS)
1333 {
1334 /*
1335 * Daemonize ourselves if asked to.
1336 */
1337 if (fDaemonize)
1338 {
1339 rc = RTProcDaemonize(papszArgs, "--daemonized");
1340 if (RT_FAILURE(rc))
1341 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize: %Rrc\n", rc);
1342 }
1343 else
1344 rcExit = rtFuzzCmdMasterRun(&This, pszLoadCfg);
1345 }
1346 }
1347 else
1348 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
1349 return rcExit;
1350}
1351
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