VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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