VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp@ 77487

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

Runtime/fuzz: Rewriting the core fuzzed input generator.

The new implementation stores only changes between mutations and not the
complete input. This saves memory (at the cost of a bit of increased complexity
when generating the input) and allows to generate mutations when no initial corpus
data is available which is useful when trying to fuzz data streams like network connections.

There are two modes when creating a new fuzzing context:

  • BLOB is used for input data like files where an initial corpus is available
  • STREAM is used for data streams like network connections
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.3 KB
Line 
1/* $Id: fuzz-observer.cpp 77482 2019-02-27 13:17:24Z vboxsync $ */
2/** @file
3 * IPRT - Fuzzing framework API, observer.
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/ctype.h>
37#include <iprt/dir.h>
38#include <iprt/err.h>
39#include <iprt/env.h>
40#include <iprt/file.h>
41#include <iprt/md5.h>
42#include <iprt/mem.h>
43#include <iprt/mp.h>
44#include <iprt/path.h>
45#include <iprt/pipe.h>
46#include <iprt/poll.h>
47#include <iprt/process.h>
48#include <iprt/semaphore.h>
49#include <iprt/stream.h>
50#include <iprt/string.h>
51#include <iprt/time.h>
52#include <iprt/thread.h>
53
54
55/** Poll ID for the reading end of the stdout pipe from the client process. */
56#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT 0
57/** Poll ID for the reading end of the stderr pipe from the client process. */
58#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR 1
59/** Poll ID for the writing end of the stdin pipe to the client process. */
60#define RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN 2
61
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66/** Pointer to the internal fuzzing observer state. */
67typedef struct RTFUZZOBSINT *PRTFUZZOBSINT;
68
69
70/**
71 * Observer thread state for one process.
72 */
73typedef struct RTFUZZOBSTHRD
74{
75 /** The thread handle. */
76 RTTHREAD hThread;
77 /** The observer ID. */
78 uint32_t idObs;
79 /** Flag whether to shutdown. */
80 volatile bool fShutdown;
81 /** Pointer to te global observer state. */
82 PRTFUZZOBSINT pFuzzObs;
83 /** Current fuzzer input. */
84 RTFUZZINPUT hFuzzInput;
85 /** Flag whether to keep the input. */
86 bool fKeepInput;
87 /** Flag whether a new input is waiting. */
88 volatile bool fNewInput;
89} RTFUZZOBSTHRD;
90/** Pointer to an observer thread state. */
91typedef RTFUZZOBSTHRD *PRTFUZZOBSTHRD;
92
93
94/**
95 * Internal fuzzing observer state.
96 */
97typedef struct RTFUZZOBSINT
98{
99 /** The fuzzing context used for this observer. */
100 RTFUZZCTX hFuzzCtx;
101 /** Temp directory for input files. */
102 char *pszTmpDir;
103 /** Results directory. */
104 char *pszResultsDir;
105 /** The binary to run. */
106 char *pszBinary;
107 /** Arguments to run the binary with, terminated by a NULL entry. */
108 char **papszArgs;
109 /** Number of arguments. */
110 uint32_t cArgs;
111 /** Maximum time to wait for the client to terminate until it is considered hung and killed. */
112 RTMSINTERVAL msWaitMax;
113 /** The channel the binary expects the input. */
114 RTFUZZOBSINPUTCHAN enmInputChan;
115 /** Flag whether to shutdown the master and all workers. */
116 volatile bool fShutdown;
117 /** Global observer thread handle. */
118 RTTHREAD hThreadGlobal;
119 /** The event semaphore handle for the global observer thread. */
120 RTSEMEVENT hEvtGlobal;
121 /** Notification event bitmap. */
122 volatile uint64_t bmEvt;
123 /** Number of threads created - one for each process. */
124 uint32_t cThreads;
125 /** Pointer to the array of observer thread states. */
126 PRTFUZZOBSTHRD paObsThreads;
127 /** Timestamp of the last stats query. */
128 uint64_t tsLastStats;
129 /** Last number of fuzzed inputs per second if we didn't gather enough data in between
130 * statistic queries. */
131 uint32_t cFuzzedInputsPerSecLast;
132 /** Fuzzing statistics. */
133 RTFUZZOBSSTATS Stats;
134} RTFUZZOBSINT;
135
136
137/**
138 * Stdout/Stderr buffer.
139 */
140typedef struct RTFUZZOBSSTDOUTERRBUF
141{
142 /** Current amount buffered. */
143 size_t cbBuf;
144 /** Maxmium amount to buffer. */
145 size_t cbBufMax;
146 /** Base pointer to the data buffer. */
147 uint8_t *pbBase;
148} RTFUZZOBSSTDOUTERRBUF;
149/** Pointer to a stdout/stderr buffer. */
150typedef RTFUZZOBSSTDOUTERRBUF *PRTFUZZOBSSTDOUTERRBUF;
151
152
153/**
154 * Worker execution context.
155 */
156typedef struct RTFUZZOBSEXECCTX
157{
158 /** The stdout pipe handle - reading end. */
159 RTPIPE hPipeStdoutR;
160 /** The stdout pipe handle - writing end. */
161 RTPIPE hPipeStdoutW;
162 /** The stderr pipe handle - reading end. */
163 RTPIPE hPipeStderrR;
164 /** The stderr pipe handle - writing end. */
165 RTPIPE hPipeStderrW;
166 /** The stdin pipe handle - reading end. */
167 RTPIPE hPipeStdinR;
168 /** The stind pipe handle - writing end. */
169 RTPIPE hPipeStdinW;
170 /** The stdout handle. */
171 RTHANDLE StdoutHandle;
172 /** The stderr handle. */
173 RTHANDLE StderrHandle;
174 /** The stdin handle. */
175 RTHANDLE StdinHandle;
176 /** The pollset to monitor. */
177 RTPOLLSET hPollSet;
178 /** The process to monitor. */
179 RTPROCESS hProc;
180 /** Execution time of the process. */
181 RTMSINTERVAL msExec;
182 /** Current input data pointer. */
183 uint8_t *pbInputCur;
184 /** Number of bytes left for the input. */
185 size_t cbInputLeft;
186 /** The stdout data buffer. */
187 RTFUZZOBSSTDOUTERRBUF StdOutBuf;
188 /** The stderr data buffer. */
189 RTFUZZOBSSTDOUTERRBUF StdErrBuf;
190 /** Modified arguments vector - variable in size. */
191 char *apszArgs[1];
192} RTFUZZOBSEXECCTX;
193/** Pointer to an execution context. */
194typedef RTFUZZOBSEXECCTX *PRTFUZZOBSEXECCTX;
195/** Pointer to an execution context pointer. */
196typedef PRTFUZZOBSEXECCTX *PPRTFUZZOBSEXECCTX;
197
198
199/**
200 * A variable descriptor.
201 */
202typedef struct RTFUZZOBSVARIABLE
203{
204 /** The variable. */
205 const char *pszVar;
206 /** Length of the variable in characters - excluding the terminator. */
207 uint32_t cchVar;
208 /** The replacement value. */
209 const char *pszVal;
210} RTFUZZOBSVARIABLE;
211/** Pointer to a variable descriptor. */
212typedef RTFUZZOBSVARIABLE *PRTFUZZOBSVARIABLE;
213
214
215/**
216 * Initializes the given stdout/stderr buffer.
217 *
218 * @returns nothing.
219 * @param pBuf The buffer to initialize.
220 */
221static void rtFuzzObsStdOutErrBufInit(PRTFUZZOBSSTDOUTERRBUF pBuf)
222{
223 pBuf->cbBuf = 0;
224 pBuf->cbBufMax = 0;
225 pBuf->pbBase = NULL;
226}
227
228
229/**
230 * Frees all allocated resources in the given stdout/stderr buffer.
231 *
232 * @returns nothing.
233 * @param pBuf The buffer to free.
234 */
235static void rtFuzzObsStdOutErrBufFree(PRTFUZZOBSSTDOUTERRBUF pBuf)
236{
237 if (pBuf->pbBase)
238 RTMemFree(pBuf->pbBase);
239}
240
241
242/**
243 * Clears the given stdout/stderr buffer.
244 *
245 * @returns nothing.
246 * @param pBuf The buffer to clear.
247 */
248static void rtFuzzObsStdOutErrBufClear(PRTFUZZOBSSTDOUTERRBUF pBuf)
249{
250 pBuf->cbBuf = 0;
251}
252
253
254/**
255 * Fills the given stdout/stderr buffer from the given pipe.
256 *
257 * @returns IPRT status code.
258 * @param pBuf The buffer to fill.
259 * @param hPipeRead The pipe to read from.
260 */
261static int rtFuzzObsStdOutErrBufFill(PRTFUZZOBSSTDOUTERRBUF pBuf, RTPIPE hPipeRead)
262{
263 int rc = VINF_SUCCESS;
264
265 size_t cbRead = 0;
266 size_t cbThisRead = 0;
267 do
268 {
269 cbThisRead = pBuf->cbBufMax - pBuf->cbBuf;
270 if (!cbThisRead)
271 {
272 /* Try to increase the buffer. */
273 uint8_t *pbNew = (uint8_t *)RTMemRealloc(pBuf->pbBase, pBuf->cbBufMax + _4K);
274 if (RT_LIKELY(pbNew))
275 {
276 pBuf->cbBufMax += _4K;
277 pBuf->pbBase = pbNew;
278 }
279 cbThisRead = pBuf->cbBufMax - pBuf->cbBuf;
280 }
281
282 if (cbThisRead)
283 {
284 rc = RTPipeRead(hPipeRead, pBuf->pbBase + pBuf->cbBuf, cbThisRead, &cbRead);
285 if (RT_SUCCESS(rc))
286 pBuf->cbBuf += cbRead;
287 }
288 else
289 rc = VERR_NO_MEMORY;
290 } while ( RT_SUCCESS(rc)
291 && cbRead == cbThisRead);
292
293 return rc;
294}
295
296
297/**
298 * Writes the given stdout/stderr buffer to the given filename.
299 *
300 * @returns IPRT status code.
301 * @param pBuf The buffer to write.
302 * @param pszFilename The filename to write the buffer to.
303 */
304static int rtFuzzStdOutErrBufWriteToFile(PRTFUZZOBSSTDOUTERRBUF pBuf, const char *pszFilename)
305{
306 RTFILE hFile;
307 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
308 if (RT_SUCCESS(rc))
309 {
310 rc = RTFileWrite(hFile, pBuf->pbBase, pBuf->cbBuf, NULL);
311 AssertRC(rc);
312 RTFileClose(hFile);
313
314 if (RT_FAILURE(rc))
315 RTFileDelete(pszFilename);
316 }
317
318 return rc;
319}
320
321
322/**
323 * Replaces a variable with its value.
324 *
325 * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
326 * @param ppszNew In/Out.
327 * @param pcchNew In/Out. (Messed up on failure.)
328 * @param offVar Variable offset.
329 * @param cchVar Variable length.
330 * @param pszValue The value.
331 * @param cchValue Value length.
332 */
333static int rtFuzzObsReplaceStringVariable(char **ppszNew, size_t *pcchNew, size_t offVar, size_t cchVar,
334 const char *pszValue, size_t cchValue)
335{
336 size_t const cchAfter = *pcchNew - offVar - cchVar;
337 if (cchVar < cchValue)
338 {
339 *pcchNew += cchValue - cchVar;
340 int rc = RTStrRealloc(ppszNew, *pcchNew + 1);
341 if (RT_FAILURE(rc))
342 return rc;
343 }
344
345 char *pszNew = *ppszNew;
346 memmove(&pszNew[offVar + cchValue], &pszNew[offVar + cchVar], cchAfter + 1);
347 memcpy(&pszNew[offVar], pszValue, cchValue);
348 return VINF_SUCCESS;
349}
350
351
352/**
353 * Replace the variables found in the source string, returning a new string that
354 * lives on the string heap.
355 *
356 * @returns IPRT status code.
357 * @param pszSrc The source string.
358 * @param paVars Pointer to the array of known variables.
359 * @param ppszNew Where to return the new string.
360 */
361static int rtFuzzObsReplaceStringVariables(const char *pszSrc, PRTFUZZOBSVARIABLE paVars, char **ppszNew)
362{
363 /* Lazy approach that employs memmove. */
364 int rc = VINF_SUCCESS;
365 size_t cchNew = strlen(pszSrc);
366 char *pszNew = RTStrDup(pszSrc);
367
368 if (paVars)
369 {
370 char *pszDollar = pszNew;
371 while ((pszDollar = strchr(pszDollar, '$')) != NULL)
372 {
373 if (pszDollar[1] == '{')
374 {
375 const char *pszEnd = strchr(&pszDollar[2], '}');
376 if (pszEnd)
377 {
378 size_t const cchVar = pszEnd - pszDollar + 1; /* includes "${}" */
379 size_t offDollar = pszDollar - pszNew;
380 PRTFUZZOBSVARIABLE pVar = paVars;
381 while (pVar->pszVar != NULL)
382 {
383 if ( cchVar == pVar->cchVar
384 && !memcmp(pszDollar, pVar->pszVar, cchVar))
385 {
386 size_t const cchValue = strlen(pVar->pszVal);
387 rc = rtFuzzObsReplaceStringVariable(&pszNew, &cchNew, offDollar,
388 cchVar, pVar->pszVal, cchValue);
389 offDollar += cchValue;
390 break;
391 }
392
393 pVar++;
394 }
395
396 pszDollar = &pszNew[offDollar];
397
398 if (RT_FAILURE(rc))
399 {
400 RTStrFree(pszNew);
401 *ppszNew = NULL;
402 return rc;
403 }
404 }
405 }
406 }
407 }
408
409 *ppszNew = pszNew;
410 return rc;
411}
412
413/**
414 * Prepares the argument vector for the child process.
415 *
416 * @returns IPRT status code.
417 * @param pThis The internal fuzzing observer state.
418 * @param pExecCtx The execution context to prepare the argument vector for.
419 * @param paVars Pointer to the array of known variables.
420 */
421static int rtFuzzObsExecCtxArgvPrepare(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTFUZZOBSVARIABLE paVars)
422{
423 int rc = VINF_SUCCESS;
424 for (unsigned i = 0; i < pThis->cArgs && RT_SUCCESS(rc); i++)
425 rc = rtFuzzObsReplaceStringVariables(pThis->papszArgs[i], paVars, &pExecCtx->apszArgs[i]);
426
427 return rc;
428}
429
430
431/**
432 * Creates a new execution context.
433 *
434 * @returns IPRT status code.
435 * @param ppExecCtx Where to store the pointer to the execution context on success.
436 * @param pThis The internal fuzzing observer state.
437 */
438static int rtFuzzObsExecCtxCreate(PPRTFUZZOBSEXECCTX ppExecCtx, PRTFUZZOBSINT pThis)
439{
440 int rc = VINF_SUCCESS;
441 PRTFUZZOBSEXECCTX pExecCtx = (PRTFUZZOBSEXECCTX)RTMemAllocZ(RT_UOFFSETOF_DYN(RTFUZZOBSEXECCTX, apszArgs[pThis->cArgs + 1]));
442 if (RT_LIKELY(pExecCtx))
443 {
444 pExecCtx->hPipeStdoutR = NIL_RTPIPE;
445 pExecCtx->hPipeStdoutW = NIL_RTPIPE;
446 pExecCtx->hPipeStderrR = NIL_RTPIPE;
447 pExecCtx->hPipeStderrW = NIL_RTPIPE;
448 pExecCtx->hPipeStdinR = NIL_RTPIPE;
449 pExecCtx->hPipeStdinW = NIL_RTPIPE;
450 pExecCtx->hPollSet = NIL_RTPOLLSET;
451 pExecCtx->hProc = NIL_RTPROCESS;
452 pExecCtx->msExec = 0;
453 rtFuzzObsStdOutErrBufInit(&pExecCtx->StdOutBuf);
454 rtFuzzObsStdOutErrBufInit(&pExecCtx->StdErrBuf);
455
456 rc = RTPollSetCreate(&pExecCtx->hPollSet);
457 if (RT_SUCCESS(rc))
458 {
459 rc = RTPipeCreate(&pExecCtx->hPipeStdoutR, &pExecCtx->hPipeStdoutW, RTPIPE_C_INHERIT_WRITE);
460 if (RT_SUCCESS(rc))
461 {
462 RTHANDLE Handle;
463 Handle.enmType = RTHANDLETYPE_PIPE;
464 Handle.u.hPipe = pExecCtx->hPipeStdoutR;
465 rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_READ, RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT);
466 AssertRC(rc);
467
468 rc = RTPipeCreate(&pExecCtx->hPipeStderrR, &pExecCtx->hPipeStderrW, RTPIPE_C_INHERIT_WRITE);
469 if (RT_SUCCESS(rc))
470 {
471 Handle.u.hPipe = pExecCtx->hPipeStderrR;
472 rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_READ, RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR);
473 AssertRC(rc);
474
475 /* Create the stdin pipe handles if not a file input. */
476 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN || pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
477 {
478 rc = RTPipeCreate(&pExecCtx->hPipeStdinR, &pExecCtx->hPipeStdinW, RTPIPE_C_INHERIT_READ);
479 if (RT_SUCCESS(rc))
480 {
481 pExecCtx->StdinHandle.enmType = RTHANDLETYPE_PIPE;
482 pExecCtx->StdinHandle.u.hPipe = pExecCtx->hPipeStdinR;
483
484 Handle.u.hPipe = pExecCtx->hPipeStdinW;
485 rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_WRITE, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
486 AssertRC(rc);
487 }
488 }
489 else
490 {
491 pExecCtx->StdinHandle.enmType = RTHANDLETYPE_PIPE;
492 pExecCtx->StdinHandle.u.hPipe = NIL_RTPIPE;
493 }
494
495 if (RT_SUCCESS(rc))
496 {
497 pExecCtx->StdoutHandle.enmType = RTHANDLETYPE_PIPE;
498 pExecCtx->StdoutHandle.u.hPipe = pExecCtx->hPipeStdoutW;
499 pExecCtx->StderrHandle.enmType = RTHANDLETYPE_PIPE;
500 pExecCtx->StderrHandle.u.hPipe = pExecCtx->hPipeStderrW;
501 *ppExecCtx = pExecCtx;
502 return VINF_SUCCESS;
503 }
504
505 RTPipeClose(pExecCtx->hPipeStderrR);
506 RTPipeClose(pExecCtx->hPipeStderrW);
507 }
508
509 RTPipeClose(pExecCtx->hPipeStdoutR);
510 RTPipeClose(pExecCtx->hPipeStdoutW);
511 }
512
513 RTPollSetDestroy(pExecCtx->hPollSet);
514 }
515
516 RTMemFree(pExecCtx);
517 }
518 else
519 rc = VERR_NO_MEMORY;
520
521 return rc;
522}
523
524
525/**
526 * Destroys the given execution context.
527 *
528 * @returns nothing.
529 * @param pThis The internal fuzzing observer state.
530 * @param pExecCtx The execution context to destroy.
531 */
532static void rtFuzzObsExecCtxDestroy(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx)
533{
534 RTPipeClose(pExecCtx->hPipeStdoutR);
535 RTPipeClose(pExecCtx->hPipeStdoutW);
536 RTPipeClose(pExecCtx->hPipeStderrR);
537 RTPipeClose(pExecCtx->hPipeStderrW);
538
539 if ( pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN
540 || pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
541 {
542 RTPipeClose(pExecCtx->hPipeStdinR);
543 RTPipeClose(pExecCtx->hPipeStdinW);
544 }
545
546 RTPollSetDestroy(pExecCtx->hPollSet);
547 char **ppszArg = &pExecCtx->apszArgs[0];
548 while (*ppszArg != NULL)
549 {
550 RTStrFree(*ppszArg);
551 ppszArg++;
552 }
553
554 rtFuzzObsStdOutErrBufFree(&pExecCtx->StdOutBuf);
555 rtFuzzObsStdOutErrBufFree(&pExecCtx->StdErrBuf);
556 RTMemFree(pExecCtx);
557}
558
559
560/**
561 * Runs the client binary pumping all data back and forth waiting for the client to finish.
562 *
563 * @returns IPRT status code.
564 * @retval VERR_TIMEOUT if the client didn't finish in the given deadline and was killed.
565 * @param pThis The internal fuzzing observer state.
566 * @param pExecCtx The execution context.
567 * @param pProcStat Where to store the process exit status on success.
568 */
569static int rtFuzzObsExecCtxClientRun(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTPROCSTATUS pProcStat)
570{
571 rtFuzzObsStdOutErrBufClear(&pExecCtx->StdOutBuf);
572 rtFuzzObsStdOutErrBufClear(&pExecCtx->StdErrBuf);
573
574 int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], RTENV_DEFAULT, 0 /*fFlags*/, &pExecCtx->StdinHandle,
575 &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, NULL, &pExecCtx->hProc);
576 if (RT_SUCCESS(rc))
577 {
578 uint64_t tsMilliesStart = RTTimeSystemMilliTS();
579 for (;;)
580 {
581 /* Wait a bit for something to happen on one of the pipes. */
582 uint32_t fEvtsRecv = 0;
583 uint32_t idEvt = 0;
584 rc = RTPoll(pExecCtx->hPollSet, 10 /*cMillies*/, &fEvtsRecv, &idEvt);
585 if (RT_SUCCESS(rc))
586 {
587 if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT)
588 {
589 Assert(fEvtsRecv & RTPOLL_EVT_READ);
590 rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdOutBuf, pExecCtx->hPipeStdoutR);
591 AssertRC(rc);
592 }
593 else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
594 {
595 Assert(fEvtsRecv & RTPOLL_EVT_READ);
596 rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdErrBuf, pExecCtx->hPipeStderrR);
597 AssertRC(rc);
598 }
599 else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN)
600 {
601 /* Feed the next input. */
602 Assert(fEvtsRecv & RTPOLL_EVT_WRITE);
603 size_t cbWritten = 0;
604 rc = RTPipeWrite(pExecCtx->hPipeStdinW, pExecCtx->pbInputCur, pExecCtx->cbInputLeft, &cbWritten);
605 if (RT_SUCCESS(rc))
606 {
607 pExecCtx->cbInputLeft -= cbWritten;
608 if (!pExecCtx->cbInputLeft)
609 {
610 /* Close stdin pipe. */
611 rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
612 AssertRC(rc);
613 RTPipeClose(pExecCtx->hPipeStdinW);
614 }
615 }
616 }
617 else
618 AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
619 }
620 else
621 Assert(rc == VERR_TIMEOUT);
622
623 /* Check the process status. */
624 rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
625 if (RT_SUCCESS(rc))
626 break;
627 else
628 {
629 Assert(rc == VERR_PROCESS_RUNNING);
630 /* Check whether we reached the limit. */
631 if (RTTimeSystemMilliTS() - tsMilliesStart > pThis->msWaitMax)
632 {
633 rc = VERR_TIMEOUT;
634 break;
635 }
636 }
637 } /* for (;;) */
638
639 /* Kill the process on a timeout. */
640 if (rc == VERR_TIMEOUT)
641 {
642 int rc2 = RTProcTerminate(pExecCtx->hProc);
643 AssertRC(rc2);
644 }
645 }
646
647 return rc;
648}
649
650
651/**
652 * Runs the fuzzing aware client binary pumping all data back and forth waiting for the client to crash.
653 *
654 * @returns IPRT status code.
655 * @retval VERR_TIMEOUT if the client didn't finish in the given deadline and was killed.
656 * @param pThis The internal fuzzing observer state.
657 * @param pExecCtx The execution context.
658 * @param pProcStat Where to store the process exit status on success.
659 */
660static int rtFuzzObsExecCtxClientRunFuzzingAware(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTPROCSTATUS pProcStat)
661{
662 rtFuzzObsStdOutErrBufClear(&pExecCtx->StdOutBuf);
663 rtFuzzObsStdOutErrBufClear(&pExecCtx->StdErrBuf);
664
665 int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], RTENV_DEFAULT, 0 /*fFlags*/, &pExecCtx->StdinHandle,
666 &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, NULL, &pExecCtx->hProc);
667 if (RT_SUCCESS(rc))
668 {
669 /* Send the initial fuzzing context state over to the client. */
670 void *pvState = NULL;
671 size_t cbState = 0;
672 rc = RTFuzzCtxStateExport(pThis->hFuzzCtx, &pvState, &cbState);
673 if (RT_SUCCESS(rc))
674 {
675 uint32_t cbStateWr = (uint32_t)cbState;
676 rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, &cbStateWr, sizeof(cbStateWr), NULL);
677 rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, pvState, cbState, NULL);
678 if (RT_SUCCESS(rc))
679 {
680 rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
681 AssertRC(rc);
682
683 uint64_t tsMilliesLastSignal = RTTimeSystemMilliTS();
684 uint32_t cFuzzedInputs = 0;
685 for (;;)
686 {
687 /* Wait a bit for something to happen on one of the pipes. */
688 uint32_t fEvtsRecv = 0;
689 uint32_t idEvt = 0;
690 rc = RTPoll(pExecCtx->hPollSet, 10 /*cMillies*/, &fEvtsRecv, &idEvt);
691 if (RT_SUCCESS(rc))
692 {
693 if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT)
694 {
695 Assert(fEvtsRecv & RTPOLL_EVT_READ);
696 for (;;)
697 {
698 char achBuf[512];
699 size_t cbRead = 0;
700 rc = RTPipeRead(pExecCtx->hPipeStdoutR, &achBuf[0], sizeof(achBuf), &cbRead);
701 if (RT_SUCCESS(rc))
702 {
703 if (!cbRead)
704 break;
705
706 tsMilliesLastSignal = RTTimeMilliTS();
707 for (unsigned i = 0; i < cbRead; i++)
708 {
709 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
710 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
711
712 if (achBuf[i] == '.')
713 cFuzzedInputs++;
714 else if (achBuf[i] == 'A')
715 {
716 /** @todo Advance our fuzzer to get the added input. */
717 }
718 }
719 }
720 else
721 break;
722 }
723 AssertRC(rc);
724 }
725 else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
726 {
727 Assert(fEvtsRecv & RTPOLL_EVT_READ);
728 rc = rtFuzzObsStdOutErrBufFill(&pExecCtx->StdErrBuf, pExecCtx->hPipeStderrR);
729 AssertRC(rc);
730 }
731 else
732 AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
733 }
734 else
735 Assert(rc == VERR_TIMEOUT);
736
737 /* Check the process status. */
738 rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
739 if (RT_SUCCESS(rc))
740 break;
741 else
742 {
743 Assert(rc == VERR_PROCESS_RUNNING);
744 /* Check when the last response from the client was. */
745 if (RTTimeSystemMilliTS() - tsMilliesLastSignal > pThis->msWaitMax)
746 {
747 rc = VERR_TIMEOUT;
748 break;
749 }
750 }
751 } /* for (;;) */
752
753 /* Kill the process on a timeout. */
754 if (rc == VERR_TIMEOUT)
755 {
756 int rc2 = RTProcTerminate(pExecCtx->hProc);
757 AssertRC(rc2);
758 }
759 }
760 }
761 }
762
763 RTHANDLE Handle;
764 Handle.enmType = RTHANDLETYPE_PIPE;
765 Handle.u.hPipe = pExecCtx->hPipeStdinW;
766 rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_WRITE, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
767 AssertRC(rc);
768
769 return rc;
770}
771
772
773/**
774 * Adds the input to the results directory.
775 *
776 * @returns IPRT status code.
777 * @param pThis The internal fuzzing observer state.
778 * @param hFuzzInput Fuzzing input handle to write.
779 * @param pExecCtx Execution context.
780 */
781static int rtFuzzObsAddInputToResults(PRTFUZZOBSINT pThis, RTFUZZINPUT hFuzzInput, PRTFUZZOBSEXECCTX pExecCtx)
782{
783 char aszDigest[RTMD5_STRING_LEN + 1];
784 int rc = RTFuzzInputQueryDigestString(hFuzzInput, &aszDigest[0], sizeof(aszDigest));
785 if (RT_SUCCESS(rc))
786 {
787 /* Create a directory. */
788 char szPath[RTPATH_MAX];
789 rc = RTPathJoin(szPath, sizeof(szPath), pThis->pszResultsDir, &aszDigest[0]);
790 AssertRC(rc);
791
792 rc = RTDirCreate(&szPath[0], 0700, 0 /*fCreate*/);
793 if (RT_SUCCESS(rc))
794 {
795 /* Write the input. */
796 char szTmp[RTPATH_MAX];
797 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "input");
798 AssertRC(rc);
799
800 rc = RTFuzzInputWriteToFile(hFuzzInput, &szTmp[0]);
801 if (RT_SUCCESS(rc))
802 {
803 /* Stdout and Stderr. */
804 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stdout");
805 AssertRC(rc);
806 rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
807 if (RT_SUCCESS(rc))
808 {
809 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stderr");
810 AssertRC(rc);
811 rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
812 }
813 }
814 }
815 }
816
817 return rc;
818}
819
820
821/**
822 * Fuzzing observer worker loop.
823 *
824 * @returns IPRT status code.
825 * @param hThrd The thread handle.
826 * @param pvUser Opaque user data.
827 */
828static DECLCALLBACK(int) rtFuzzObsWorkerLoop(RTTHREAD hThrd, void *pvUser)
829{
830 PRTFUZZOBSTHRD pObsThrd = (PRTFUZZOBSTHRD)pvUser;
831 PRTFUZZOBSINT pThis = pObsThrd->pFuzzObs;
832 PRTFUZZOBSEXECCTX pExecCtx = NULL;
833
834 int rc = rtFuzzObsExecCtxCreate(&pExecCtx, pThis);
835 if (RT_FAILURE(rc))
836 return rc;
837
838 while (!pObsThrd->fShutdown)
839 {
840 char szInput[RTPATH_MAX];
841
842 /* Wait for work. */
843 rc = RTThreadUserWait(hThrd, RT_INDEFINITE_WAIT);
844 AssertRC(rc);
845
846 if (pObsThrd->fShutdown)
847 break;
848
849 if (!ASMAtomicXchgBool(&pObsThrd->fNewInput, false))
850 continue;
851
852 AssertPtr(pObsThrd->hFuzzInput);
853
854 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
855 {
856 char szFilename[32];
857
858 ssize_t cbBuf = RTStrPrintf2(&szFilename[0], sizeof(szFilename), "%u", pObsThrd->idObs);
859 Assert(cbBuf > 0); RT_NOREF(cbBuf);
860
861 RT_ZERO(szInput);
862 rc = RTPathJoin(szInput, sizeof(szInput), pThis->pszTmpDir, &szFilename[0]);
863 AssertRC(rc);
864
865 rc = RTFuzzInputWriteToFile(pObsThrd->hFuzzInput, &szInput[0]);
866 if (RT_SUCCESS(rc))
867 {
868 RTFUZZOBSVARIABLE aVar[2] = {
869 { "${INPUT}", sizeof("${INPUT}") - 1, &szInput[0] },
870 { NULL, 0, NULL }
871 };
872 rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, &aVar[0]);
873 }
874 }
875 else if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN)
876 {
877 rc = RTFuzzInputQueryBlobData(pObsThrd->hFuzzInput, (void **)&pExecCtx->pbInputCur, &pExecCtx->cbInputLeft);
878 if (RT_SUCCESS(rc))
879 rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, NULL);
880 }
881
882 if (RT_SUCCESS(rc))
883 {
884 RTPROCSTATUS ProcSts;
885 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
886 rc = rtFuzzObsExecCtxClientRunFuzzingAware(pThis, pExecCtx, &ProcSts);
887 else
888 {
889 rc = rtFuzzObsExecCtxClientRun(pThis, pExecCtx, &ProcSts);
890 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
891 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
892 }
893
894 if (RT_SUCCESS(rc))
895 {
896 if (ProcSts.enmReason != RTPROCEXITREASON_NORMAL)
897 {
898 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsCrash);
899 rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
900 }
901 }
902 else if (rc == VERR_TIMEOUT)
903 {
904 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsHang);
905 rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
906 }
907 else
908 AssertFailed();
909
910 ASMAtomicXchgBool(&pObsThrd->fKeepInput, true);
911
912 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
913 RTFileDelete(&szInput[0]);
914 }
915
916 ASMAtomicBitSet(&pThis->bmEvt, pObsThrd->idObs);
917 RTSemEventSignal(pThis->hEvtGlobal);
918 }
919
920 rtFuzzObsExecCtxDestroy(pThis, pExecCtx);
921 return VINF_SUCCESS;
922}
923
924
925/**
926 * Fuzzing observer master worker loop.
927 *
928 * @returns IPRT status code.
929 * @param hThread The thread handle.
930 * @param pvUser Opaque user data.
931 */
932static DECLCALLBACK(int) rtFuzzObsMasterLoop(RTTHREAD hThread, void *pvUser)
933{
934 RT_NOREF(hThread);
935 int rc = VINF_SUCCESS;
936 PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)pvUser;
937
938 RTThreadUserSignal(hThread);
939
940 while ( !pThis->fShutdown
941 && RT_SUCCESS(rc))
942 {
943 uint64_t bmEvt = ASMAtomicXchgU64(&pThis->bmEvt, 0);
944 uint32_t idxObs = 0;
945 while (bmEvt != 0)
946 {
947 if (bmEvt & 0x1)
948 {
949 /* Create a new input for this observer and kick it. */
950 PRTFUZZOBSTHRD pObsThrd = &pThis->paObsThreads[idxObs];
951
952 /* Release the old input. */
953 if (pObsThrd->hFuzzInput)
954 {
955 if (pObsThrd->fKeepInput)
956 {
957 int rc2 = RTFuzzInputAddToCtxCorpus(pObsThrd->hFuzzInput);
958 Assert(RT_SUCCESS(rc2) || rc2 == VERR_ALREADY_EXISTS); RT_NOREF(rc2);
959 pObsThrd->fKeepInput= false;
960 }
961 RTFuzzInputRelease(pObsThrd->hFuzzInput);
962 }
963
964 rc = RTFuzzCtxInputGenerate(pThis->hFuzzCtx, &pObsThrd->hFuzzInput);
965 if (RT_SUCCESS(rc))
966 {
967 ASMAtomicWriteBool(&pObsThrd->fNewInput, true);
968 RTThreadUserSignal(pObsThrd->hThread);
969 }
970 }
971
972 idxObs++;
973 bmEvt >>= 1;
974 }
975
976 rc = RTSemEventWait(pThis->hEvtGlobal, RT_INDEFINITE_WAIT);
977 }
978
979 return VINF_SUCCESS;
980}
981
982
983/**
984 * Initializes the given worker thread structure.
985 *
986 * @returns IPRT status code.
987 * @param pThis The internal fuzzing observer state.
988 * @param iObs Observer ID.
989 * @param pObsThrd The observer thread structure.
990 */
991static int rtFuzzObsWorkerThreadInit(PRTFUZZOBSINT pThis, uint32_t idObs, PRTFUZZOBSTHRD pObsThrd)
992{
993 pObsThrd->pFuzzObs = pThis;
994 pObsThrd->hFuzzInput = NULL;
995 pObsThrd->idObs = idObs;
996 pObsThrd->fShutdown = false;
997
998 ASMAtomicBitSet(&pThis->bmEvt, idObs);
999 return RTThreadCreate(&pObsThrd->hThread, rtFuzzObsWorkerLoop, pObsThrd, 0, RTTHREADTYPE_IO,
1000 RTTHREADFLAGS_WAITABLE, "Fuzz-Worker");
1001}
1002
1003
1004/**
1005 * Creates the given amount of worker threads and puts them into waiting state.
1006 *
1007 * @returns IPRT status code.
1008 * @param pThis The internal fuzzing observer state.
1009 * @param cThreads Number of worker threads to create.
1010 */
1011static int rtFuzzObsWorkersCreate(PRTFUZZOBSINT pThis, uint32_t cThreads)
1012{
1013 int rc = VINF_SUCCESS;
1014 PRTFUZZOBSTHRD paObsThreads = (PRTFUZZOBSTHRD)RTMemAllocZ(cThreads * sizeof(RTFUZZOBSTHRD));
1015 if (RT_LIKELY(paObsThreads))
1016 {
1017 for (unsigned i = 0; i < cThreads && RT_SUCCESS(rc); i++)
1018 {
1019 rc = rtFuzzObsWorkerThreadInit(pThis, i, &paObsThreads[i]);
1020 if (RT_FAILURE(rc))
1021 {
1022 /* Rollback. */
1023
1024 }
1025 }
1026
1027 if (RT_SUCCESS(rc))
1028 {
1029 pThis->paObsThreads = paObsThreads;
1030 pThis->cThreads = cThreads;
1031 }
1032 else
1033 RTMemFree(paObsThreads);
1034 }
1035
1036 return rc;
1037}
1038
1039
1040/**
1041 * Creates the global worker thread managing the input creation and other worker threads.
1042 *
1043 * @returns IPRT status code.
1044 * @param pThis The internal fuzzing observer state.
1045 */
1046static int rtFuzzObsMasterCreate(PRTFUZZOBSINT pThis)
1047{
1048 pThis->fShutdown = false;
1049
1050 int rc = RTSemEventCreate(&pThis->hEvtGlobal);
1051 if (RT_SUCCESS(rc))
1052 {
1053 rc = RTThreadCreate(&pThis->hThreadGlobal, rtFuzzObsMasterLoop, pThis, 0, RTTHREADTYPE_IO,
1054 RTTHREADFLAGS_WAITABLE, "Fuzz-Master");
1055 if (RT_SUCCESS(rc))
1056 {
1057 RTThreadUserWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT);
1058 }
1059 else
1060 {
1061 RTSemEventDestroy(pThis->hEvtGlobal);
1062 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1063 }
1064 }
1065
1066 return rc;
1067}
1068
1069
1070RTDECL(int) RTFuzzObsCreate(PRTFUZZOBS phFuzzObs, RTFUZZCTXTYPE enmType)
1071{
1072 AssertPtrReturn(phFuzzObs, VERR_INVALID_POINTER);
1073
1074 int rc = VINF_SUCCESS;
1075 PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)RTMemAllocZ(sizeof(*pThis));
1076 if (RT_LIKELY(pThis))
1077 {
1078 pThis->pszBinary = NULL;
1079 pThis->papszArgs = NULL;
1080 pThis->msWaitMax = 1000;
1081 pThis->hThreadGlobal = NIL_RTTHREAD;
1082 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1083 pThis->bmEvt = 0;
1084 pThis->cThreads = 0;
1085 pThis->paObsThreads = NULL;
1086 pThis->tsLastStats = RTTimeMilliTS();
1087 pThis->Stats.cFuzzedInputsPerSec = 0;
1088 pThis->Stats.cFuzzedInputs = 0;
1089 pThis->Stats.cFuzzedInputsHang = 0;
1090 pThis->Stats.cFuzzedInputsCrash = 0;
1091 rc = RTFuzzCtxCreate(&pThis->hFuzzCtx, enmType);
1092 if (RT_SUCCESS(rc))
1093 {
1094 *phFuzzObs = pThis;
1095 return VINF_SUCCESS;
1096 }
1097
1098 RTMemFree(pThis);
1099 }
1100 else
1101 rc = VERR_NO_MEMORY;
1102
1103 return rc;
1104}
1105
1106
1107RTDECL(int) RTFuzzObsDestroy(RTFUZZOBS hFuzzObs)
1108{
1109 PRTFUZZOBSINT pThis = hFuzzObs;
1110 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1111
1112 RTFuzzObsExecStop(hFuzzObs);
1113
1114 /* Clean up all acquired resources. */
1115 for (unsigned i = 0; i < pThis->cArgs; i++)
1116 RTStrFree(pThis->papszArgs[i]);
1117
1118 RTMemFree(pThis->papszArgs);
1119
1120 if (pThis->hEvtGlobal != NIL_RTSEMEVENT)
1121 RTSemEventDestroy(pThis->hEvtGlobal);
1122
1123 if (pThis->pszResultsDir)
1124 RTStrFree(pThis->pszResultsDir);
1125 if (pThis->pszTmpDir)
1126 RTStrFree(pThis->pszTmpDir);
1127 if (pThis->pszBinary)
1128 RTStrFree(pThis->pszBinary);
1129 RTFuzzCtxRelease(pThis->hFuzzCtx);
1130 RTMemFree(pThis);
1131 return VINF_SUCCESS;
1132}
1133
1134
1135RTDECL(int) RTFuzzObsQueryCtx(RTFUZZOBS hFuzzObs, PRTFUZZCTX phFuzzCtx)
1136{
1137 PRTFUZZOBSINT pThis = hFuzzObs;
1138 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1139 AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
1140
1141 RTFuzzCtxRetain(pThis->hFuzzCtx);
1142 *phFuzzCtx = pThis->hFuzzCtx;
1143 return VINF_SUCCESS;
1144}
1145
1146
1147RTDECL(int) RTFuzzObsQueryStats(RTFUZZOBS hFuzzObs, PRTFUZZOBSSTATS pStats)
1148{
1149 PRTFUZZOBSINT pThis = hFuzzObs;
1150 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1151 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
1152
1153 uint64_t tsStatsQuery = RTTimeMilliTS();
1154 uint32_t cFuzzedInputsPerSec = ASMAtomicXchgU32(&pThis->Stats.cFuzzedInputsPerSec, 0);
1155
1156 pStats->cFuzzedInputsCrash = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsCrash);
1157 pStats->cFuzzedInputsHang = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsHang);
1158 pStats->cFuzzedInputs = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputs);
1159 uint64_t cPeriodSec = (tsStatsQuery - pThis->tsLastStats) / 1000;
1160 if (cPeriodSec)
1161 {
1162 pStats->cFuzzedInputsPerSec = cFuzzedInputsPerSec / cPeriodSec;
1163 pThis->cFuzzedInputsPerSecLast = pStats->cFuzzedInputsPerSec;
1164 pThis->tsLastStats = tsStatsQuery;
1165 }
1166 else
1167 pStats->cFuzzedInputsPerSec = pThis->cFuzzedInputsPerSecLast;
1168 return VINF_SUCCESS;
1169}
1170
1171
1172RTDECL(int) RTFuzzObsSetTmpDirectory(RTFUZZOBS hFuzzObs, const char *pszTmp)
1173{
1174 PRTFUZZOBSINT pThis = hFuzzObs;
1175 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1176 AssertPtrReturn(pszTmp, VERR_INVALID_POINTER);
1177
1178 int rc = VINF_SUCCESS;
1179 pThis->pszTmpDir = RTStrDup(pszTmp);
1180 if (!pThis->pszTmpDir)
1181 rc = VERR_NO_STR_MEMORY;
1182 return rc;
1183}
1184
1185
1186RTDECL(int) RTFuzzObsSetResultDirectory(RTFUZZOBS hFuzzObs, const char *pszResults)
1187{
1188 PRTFUZZOBSINT pThis = hFuzzObs;
1189 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1190 AssertPtrReturn(pszResults, VERR_INVALID_POINTER);
1191
1192 int rc = VINF_SUCCESS;
1193 pThis->pszResultsDir = RTStrDup(pszResults);
1194 if (!pThis->pszResultsDir)
1195 rc = VERR_NO_STR_MEMORY;
1196 return rc;
1197}
1198
1199
1200RTDECL(int) RTFuzzObsSetTestBinary(RTFUZZOBS hFuzzObs, const char *pszBinary, RTFUZZOBSINPUTCHAN enmInputChan)
1201{
1202 PRTFUZZOBSINT pThis = hFuzzObs;
1203 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1204 AssertPtrReturn(pszBinary, VERR_INVALID_POINTER);
1205
1206 int rc = VINF_SUCCESS;
1207 pThis->enmInputChan = enmInputChan;
1208 pThis->pszBinary = RTStrDup(pszBinary);
1209 if (RT_UNLIKELY(!pThis->pszBinary))
1210 rc = VERR_NO_STR_MEMORY;
1211 return rc;
1212}
1213
1214
1215RTDECL(int) RTFuzzObsSetTestBinaryArgs(RTFUZZOBS hFuzzObs, const char * const *papszArgs, unsigned cArgs)
1216{
1217 PRTFUZZOBSINT pThis = hFuzzObs;
1218 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1219
1220 int rc = VINF_SUCCESS;
1221 char **papszArgsOld = pThis->papszArgs;
1222 if (papszArgs)
1223 {
1224 pThis->papszArgs = (char **)RTMemAllocZ(sizeof(char **) * (cArgs + 1));
1225 if (RT_LIKELY(pThis->papszArgs))
1226 {
1227 for (unsigned i = 0; i < cArgs; i++)
1228 {
1229 pThis->papszArgs[i] = RTStrDup(papszArgs[i]);
1230 if (RT_UNLIKELY(!pThis->papszArgs[i]))
1231 {
1232 while (i > 0)
1233 {
1234 i--;
1235 RTStrFree(pThis->papszArgs[i]);
1236 }
1237 break;
1238 }
1239 }
1240
1241 if (RT_FAILURE(rc))
1242 RTMemFree(pThis->papszArgs);
1243 }
1244 else
1245 rc = VERR_NO_MEMORY;
1246
1247 if (RT_FAILURE(rc))
1248 pThis->papszArgs = papszArgsOld;
1249 else
1250 pThis->cArgs = cArgs;
1251 }
1252 else
1253 {
1254 pThis->papszArgs = NULL;
1255 pThis->cArgs = 0;
1256 if (papszArgsOld)
1257 {
1258 char **ppsz = papszArgsOld;
1259 while (*ppsz != NULL)
1260 {
1261 RTStrFree(*ppsz);
1262 ppsz++;
1263 }
1264 RTMemFree(papszArgsOld);
1265 }
1266 }
1267
1268 return rc;
1269}
1270
1271
1272RTDECL(int) RTFuzzObsExecStart(RTFUZZOBS hFuzzObs, uint32_t cProcs)
1273{
1274 PRTFUZZOBSINT pThis = hFuzzObs;
1275 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1276 AssertReturn(cProcs <= sizeof(uint64_t) * 8, VERR_INVALID_PARAMETER);
1277 AssertReturn( pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE
1278 || pThis->pszTmpDir != NULL,
1279 VERR_INVALID_STATE);
1280
1281 int rc = VINF_SUCCESS;
1282 if (!cProcs)
1283 cProcs = RT_MIN(RTMpGetPresentCoreCount(), sizeof(uint64_t) * 8);
1284
1285 /* Spin up the worker threads first. */
1286 rc = rtFuzzObsWorkersCreate(pThis, cProcs);
1287 if (RT_SUCCESS(rc))
1288 {
1289 /* Spin up the global thread. */
1290 rc = rtFuzzObsMasterCreate(pThis);
1291 }
1292
1293 return rc;
1294}
1295
1296
1297RTDECL(int) RTFuzzObsExecStop(RTFUZZOBS hFuzzObs)
1298{
1299 PRTFUZZOBSINT pThis = hFuzzObs;
1300 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1301
1302 /* Wait for the master thread to terminate. */
1303 if (pThis->hThreadGlobal != NIL_RTTHREAD)
1304 {
1305 ASMAtomicXchgBool(&pThis->fShutdown, true);
1306 RTSemEventSignal(pThis->hEvtGlobal);
1307 RTThreadWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT, NULL);
1308 pThis->hThreadGlobal = NIL_RTTHREAD;
1309 }
1310
1311 /* Destroy the workers. */
1312 if (pThis->paObsThreads)
1313 {
1314 for (unsigned i = 0; i < pThis->cThreads; i++)
1315 {
1316 PRTFUZZOBSTHRD pThrd = &pThis->paObsThreads[i];
1317 ASMAtomicXchgBool(&pThrd->fShutdown, true);
1318 RTThreadUserSignal(pThrd->hThread);
1319 RTThreadWait(pThrd->hThread, RT_INDEFINITE_WAIT, NULL);
1320 if (pThrd->hFuzzInput)
1321 RTFuzzInputRelease(pThrd->hFuzzInput);
1322 }
1323
1324 RTMemFree(pThis->paObsThreads);
1325 pThis->paObsThreads = NULL;
1326 pThis->cThreads = 0;
1327 }
1328
1329 RTSemEventDestroy(pThis->hEvtGlobal);
1330 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1331 return VINF_SUCCESS;
1332}
1333
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