VirtualBox

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

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

Runtime/fuzz: Some more statistics, add possibility to configure the environment of the target process through the job config, add possibility to read SanitizerCoverage generated reports to scan for changes in executed paths for inputs to evaluate which mutations are interesting for further use

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.0 KB
Line 
1/* $Id: fuzz-observer.cpp 77693 2019-03-13 21:24:29Z 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/** 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, &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 /*
499 * Can't add the stderr buffer with sancov enabled as it dumps the resulting report file path
500 * (which includes the always changing pid) to stderr...
501 */
502 if (!(pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_SANCOV))
503 {
504 rc = RTFuzzTgtStateAppendStderrFromPipe(pExecCtx->hTgtState, pExecCtx->hPipeStderrR);
505 AssertRC(rc);
506 }
507 else
508 {
509 /* Dump to /dev/null. */
510 char abBitBucket[_4K];
511 size_t cbRead = 0;
512 do
513 {
514 rc = RTPipeRead(pExecCtx->hPipeStderrR, &abBitBucket[0], sizeof(abBitBucket), &cbRead);
515 } while ( RT_SUCCESS(rc)
516 && cbRead == sizeof(abBitBucket));
517 }
518 }
519 else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN)
520 {
521 /* Feed the next input. */
522 Assert(fEvtsRecv & RTPOLL_EVT_WRITE);
523 size_t cbWritten = 0;
524 rc = RTPipeWrite(pExecCtx->hPipeStdinW, pExecCtx->pbInputCur, pExecCtx->cbInputLeft, &cbWritten);
525 if (RT_SUCCESS(rc))
526 {
527 pExecCtx->cbInputLeft -= cbWritten;
528 if (!pExecCtx->cbInputLeft)
529 {
530 /* Close stdin pipe. */
531 rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
532 AssertRC(rc);
533 RTPipeClose(pExecCtx->hPipeStdinW);
534 }
535 }
536 }
537 else
538 AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
539 }
540 else
541 Assert(rc == VERR_TIMEOUT);
542
543 /* Check the process status. */
544 rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
545 if (RT_SUCCESS(rc))
546 {
547 /* Add the coverage report to the sanitizer if enabled. */
548 if (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_SANCOV)
549 {
550 char szSanCovReport[RTPATH_MAX];
551 ssize_t cch = RTStrPrintf2(&szSanCovReport[0], sizeof(szSanCovReport),
552 "%s%c%s.%u.sancov",
553 pThis->pszTmpDir, RTPATH_SLASH,
554 pThis->pszBinaryFilename, pExecCtx->hProc);
555 Assert(cch > 0); RT_NOREF(cch);
556 rc = RTFuzzTgtStateAddSanCovReportFromFile(pExecCtx->hTgtState, &szSanCovReport[0]);
557 RTFileDelete(&szSanCovReport[0]);
558 }
559 break;
560 }
561 else
562 {
563 Assert(rc == VERR_PROCESS_RUNNING);
564 /* Check whether we reached the limit. */
565 if (RTTimeSystemMilliTS() - tsMilliesStart > pThis->msWaitMax)
566 {
567 rc = VERR_TIMEOUT;
568 break;
569 }
570 }
571 } /* for (;;) */
572
573 /* Kill the process on a timeout. */
574 if (rc == VERR_TIMEOUT)
575 {
576 int rc2 = RTProcTerminate(pExecCtx->hProc);
577 AssertRC(rc2);
578 }
579 }
580
581 return rc;
582}
583
584
585/**
586 * Runs the fuzzing aware client binary pumping all data back and forth waiting for the client to crash.
587 *
588 * @returns IPRT status code.
589 * @retval VERR_TIMEOUT if the client didn't finish in the given deadline and was killed.
590 * @param pThis The internal fuzzing observer state.
591 * @param pExecCtx The execution context.
592 * @param pProcStat Where to store the process exit status on success.
593 */
594static int rtFuzzObsExecCtxClientRunFuzzingAware(PRTFUZZOBSINT pThis, PRTFUZZOBSEXECCTX pExecCtx, PRTPROCSTATUS pProcStat)
595{
596 int rc = RTProcCreateEx(pThis->pszBinary, &pExecCtx->apszArgs[0], pExecCtx->hEnv, 0 /*fFlags*/, &pExecCtx->StdinHandle,
597 &pExecCtx->StdoutHandle, &pExecCtx->StderrHandle, NULL, NULL, &pExecCtx->hProc);
598 if (RT_SUCCESS(rc))
599 {
600 /* Send the initial fuzzing context state over to the client. */
601 void *pvState = NULL;
602 size_t cbState = 0;
603 rc = RTFuzzCtxStateExportToMem(pThis->hFuzzCtx, &pvState, &cbState);
604 if (RT_SUCCESS(rc))
605 {
606 uint32_t cbStateWr = (uint32_t)cbState;
607 rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, &cbStateWr, sizeof(cbStateWr), NULL);
608 rc = RTPipeWriteBlocking(pExecCtx->hPipeStdinW, pvState, cbState, NULL);
609 if (RT_SUCCESS(rc))
610 {
611 rc = RTPollSetRemove(pExecCtx->hPollSet, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
612 AssertRC(rc);
613
614 uint64_t tsMilliesLastSignal = RTTimeSystemMilliTS();
615 uint32_t cFuzzedInputs = 0;
616 for (;;)
617 {
618 /* Wait a bit for something to happen on one of the pipes. */
619 uint32_t fEvtsRecv = 0;
620 uint32_t idEvt = 0;
621 rc = RTPoll(pExecCtx->hPollSet, 10 /*cMillies*/, &fEvtsRecv, &idEvt);
622 if (RT_SUCCESS(rc))
623 {
624 if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDOUT)
625 {
626 Assert(fEvtsRecv & RTPOLL_EVT_READ);
627 for (;;)
628 {
629 char achBuf[512];
630 size_t cbRead = 0;
631 rc = RTPipeRead(pExecCtx->hPipeStdoutR, &achBuf[0], sizeof(achBuf), &cbRead);
632 if (RT_SUCCESS(rc))
633 {
634 if (!cbRead)
635 break;
636
637 tsMilliesLastSignal = RTTimeMilliTS();
638 for (unsigned i = 0; i < cbRead; i++)
639 {
640 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
641 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
642
643 if (achBuf[i] == '.')
644 cFuzzedInputs++;
645 else if (achBuf[i] == 'A')
646 {
647 /** @todo Advance our fuzzer to get the added input. */
648 }
649 }
650 }
651 else
652 break;
653 }
654 AssertRC(rc);
655 }
656 else if (idEvt == RTFUZZOBS_EXEC_CTX_POLL_ID_STDERR)
657 {
658 Assert(fEvtsRecv & RTPOLL_EVT_READ);
659 rc = RTFuzzTgtStateAppendStderrFromPipe(pExecCtx->hTgtState, pExecCtx->hPipeStderrR);
660 AssertRC(rc);
661 }
662 else
663 AssertMsgFailed(("Invalid poll ID returned: %u!\n", idEvt));
664 }
665 else
666 Assert(rc == VERR_TIMEOUT);
667
668 /* Check the process status. */
669 rc = RTProcWait(pExecCtx->hProc, RTPROCWAIT_FLAGS_NOBLOCK, pProcStat);
670 if (RT_SUCCESS(rc))
671 break;
672 else
673 {
674 Assert(rc == VERR_PROCESS_RUNNING);
675 /* Check when the last response from the client was. */
676 if (RTTimeSystemMilliTS() - tsMilliesLastSignal > pThis->msWaitMax)
677 {
678 rc = VERR_TIMEOUT;
679 break;
680 }
681 }
682 } /* for (;;) */
683
684 /* Kill the process on a timeout. */
685 if (rc == VERR_TIMEOUT)
686 {
687 int rc2 = RTProcTerminate(pExecCtx->hProc);
688 AssertRC(rc2);
689 }
690 }
691 }
692 }
693
694 RTHANDLE Handle;
695 Handle.enmType = RTHANDLETYPE_PIPE;
696 Handle.u.hPipe = pExecCtx->hPipeStdinW;
697 rc = RTPollSetAdd(pExecCtx->hPollSet, &Handle, RTPOLL_EVT_WRITE, RTFUZZOBS_EXEC_CTX_POLL_ID_STDIN);
698 AssertRC(rc);
699
700 return rc;
701}
702
703
704/**
705 * Adds the input to the results directory.
706 *
707 * @returns IPRT status code.
708 * @param pThis The internal fuzzing observer state.
709 * @param hFuzzInput Fuzzing input handle to write.
710 * @param pExecCtx Execution context.
711 */
712static int rtFuzzObsAddInputToResults(PRTFUZZOBSINT pThis, RTFUZZINPUT hFuzzInput, PRTFUZZOBSEXECCTX pExecCtx)
713{
714 char aszDigest[RTMD5_STRING_LEN + 1];
715 int rc = RTFuzzInputQueryDigestString(hFuzzInput, &aszDigest[0], sizeof(aszDigest));
716 if (RT_SUCCESS(rc))
717 {
718 /* Create a directory. */
719 char szPath[RTPATH_MAX];
720 rc = RTPathJoin(szPath, sizeof(szPath), pThis->pszResultsDir, &aszDigest[0]);
721 AssertRC(rc);
722
723 rc = RTDirCreate(&szPath[0], 0700, 0 /*fCreate*/);
724 if (RT_SUCCESS(rc))
725 {
726 /* Write the input. */
727 char szTmp[RTPATH_MAX];
728 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "input");
729 AssertRC(rc);
730
731 rc = RTFuzzInputWriteToFile(hFuzzInput, &szTmp[0]);
732 if (RT_SUCCESS(rc))
733 {
734 RT_NOREF(pExecCtx);
735#if 0
736 /* Stdout and Stderr. */
737 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stdout");
738 AssertRC(rc);
739 rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
740 if (RT_SUCCESS(rc))
741 {
742 rc = RTPathJoin(szTmp, sizeof(szTmp), &szPath[0], "stderr");
743 AssertRC(rc);
744 rc = rtFuzzStdOutErrBufWriteToFile(&pExecCtx->StdOutBuf, &szTmp[0]);
745 }
746#endif
747 }
748 }
749 }
750
751 return rc;
752}
753
754
755/**
756 * Fuzzing observer worker loop.
757 *
758 * @returns IPRT status code.
759 * @param hThrd The thread handle.
760 * @param pvUser Opaque user data.
761 */
762static DECLCALLBACK(int) rtFuzzObsWorkerLoop(RTTHREAD hThrd, void *pvUser)
763{
764 PRTFUZZOBSTHRD pObsThrd = (PRTFUZZOBSTHRD)pvUser;
765 PRTFUZZOBSINT pThis = pObsThrd->pFuzzObs;
766 PRTFUZZOBSEXECCTX pExecCtx = NULL;
767
768 int rc = rtFuzzObsExecCtxCreate(&pExecCtx, pThis);
769 if (RT_FAILURE(rc))
770 return rc;
771
772 char szInput[RTPATH_MAX]; RT_ZERO(szInput);
773 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
774 {
775 char szFilename[32];
776
777 ssize_t cbBuf = RTStrPrintf2(&szFilename[0], sizeof(szFilename), "%u", pObsThrd->idObs);
778 Assert(cbBuf > 0); RT_NOREF(cbBuf);
779
780 rc = RTPathJoin(szInput, sizeof(szInput), pThis->pszTmpDir, &szFilename[0]);
781 AssertRC(rc);
782
783 RTFUZZOBSVARIABLE aVar[2] =
784 {
785 { "${INPUT}", sizeof("${INPUT}") - 1, &szInput[0] },
786 { NULL, 0, NULL }
787 };
788 rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, &aVar[0]);
789 if (RT_FAILURE(rc))
790 return rc;
791 }
792
793 while (!pObsThrd->fShutdown)
794 {
795 /* Wait for work. */
796 if (!ASMAtomicReadU32(&pObsThrd->cInputs))
797 {
798 rc = RTThreadUserWait(hThrd, RT_INDEFINITE_WAIT);
799 AssertRC(rc);
800 }
801
802 if (pObsThrd->fShutdown)
803 break;
804
805 if (!ASMAtomicReadU32(&pObsThrd->cInputs))
806 continue;
807
808 uint32_t offRead = ASMAtomicReadU32(&pObsThrd->offQueueInputR);
809 RTFUZZINPUT hFuzzInput = pObsThrd->ahQueueInput[offRead];
810
811 ASMAtomicDecU32(&pObsThrd->cInputs);
812 offRead = (offRead + 1) % RT_ELEMENTS(pObsThrd->ahQueueInput);
813 ASMAtomicWriteU32(&pObsThrd->offQueueInputR, offRead);
814 if (!ASMAtomicBitTestAndSet(&pThis->bmEvt, pObsThrd->idObs))
815 RTSemEventSignal(pThis->hEvtGlobal);
816
817 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
818 rc = RTFuzzInputWriteToFile(hFuzzInput, &szInput[0]);
819 else if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_STDIN)
820 {
821 rc = RTFuzzInputQueryBlobData(hFuzzInput, (void **)&pExecCtx->pbInputCur, &pExecCtx->cbInputLeft);
822 if (RT_SUCCESS(rc))
823 rc = rtFuzzObsExecCtxArgvPrepare(pThis, pExecCtx, NULL);
824 }
825
826 if (RT_SUCCESS(rc))
827 {
828 RTPROCSTATUS ProcSts;
829 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FUZZING_AWARE_CLIENT)
830 rc = rtFuzzObsExecCtxClientRunFuzzingAware(pThis, pExecCtx, &ProcSts);
831 else
832 {
833 rc = rtFuzzObsExecCtxClientRun(pThis, pExecCtx, &ProcSts);
834 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
835 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
836 }
837
838 if (RT_SUCCESS(rc))
839 {
840 if (ProcSts.enmReason != RTPROCEXITREASON_NORMAL)
841 {
842 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsCrash);
843 rc = rtFuzzObsAddInputToResults(pThis, hFuzzInput, pExecCtx);
844 }
845 }
846 else if (rc == VERR_TIMEOUT)
847 {
848 ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsHang);
849 rc = rtFuzzObsAddInputToResults(pThis, hFuzzInput, pExecCtx);
850 }
851 else
852 AssertFailed();
853
854 /*
855 * Check whether we reached a known target state and add the input to the
856 * corpus in that case.
857 */
858 rc = RTFuzzTgtStateAddToRecorder(pExecCtx->hTgtState);
859 if (RT_SUCCESS(rc))
860 {
861 /* Add to corpus and create a new target state for the next run. */
862 RTFuzzInputAddToCtxCorpus(hFuzzInput);
863 RTFuzzTgtStateRelease(pExecCtx->hTgtState);
864 pExecCtx->hTgtState = NIL_RTFUZZTGTSTATE;
865 rc = RTFuzzTgtRecorderCreateNewState(pThis->hTgtRec, &pExecCtx->hTgtState);
866 AssertRC(rc);
867 }
868 else
869 {
870 Assert(rc == VERR_ALREADY_EXISTS);
871 /* Reset the state for the next run. */
872 rc = RTFuzzTgtStateReset(pExecCtx->hTgtState);
873 AssertRC(rc);
874 }
875 RTFuzzInputRelease(hFuzzInput);
876
877 if (pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE)
878 RTFileDelete(&szInput[0]);
879 }
880 }
881
882 rtFuzzObsExecCtxDestroy(pThis, pExecCtx);
883 return VINF_SUCCESS;
884}
885
886
887/**
888 * Fills the input queue of the given observer thread until it is full.
889 *
890 * @returns IPRT status code.
891 * @param pThis Pointer to the observer instance data.
892 * @param pObsThrd The observer thread instance to fill.
893 */
894static int rtFuzzObsMasterInputQueueFill(PRTFUZZOBSINT pThis, PRTFUZZOBSTHRD pObsThrd)
895{
896 int rc = VINF_SUCCESS;
897 uint32_t cInputsAdded = 0;
898 uint32_t cInputsAdd = RTFUZZOBS_THREAD_INPUT_QUEUE_MAX - ASMAtomicReadU32(&pObsThrd->cInputs);
899 uint32_t offW = ASMAtomicReadU32(&pObsThrd->offQueueInputW);
900
901 while ( cInputsAdded < cInputsAdd
902 && RT_SUCCESS(rc))
903 {
904 RTFUZZINPUT hFuzzInput = NIL_RTFUZZINPUT;
905 rc = RTFuzzCtxInputGenerate(pThis->hFuzzCtx, &hFuzzInput);
906 if (RT_SUCCESS(rc))
907 {
908 pObsThrd->ahQueueInput[offW] = hFuzzInput;
909 offW = (offW + 1) % RTFUZZOBS_THREAD_INPUT_QUEUE_MAX;
910 cInputsAdded++;
911 }
912 }
913
914 ASMAtomicWriteU32(&pObsThrd->offQueueInputW, offW);
915 ASMAtomicAddU32(&pObsThrd->cInputs, cInputsAdded);
916
917 return rc;
918}
919
920
921/**
922 * Fuzzing observer master worker loop.
923 *
924 * @returns IPRT status code.
925 * @param hThread The thread handle.
926 * @param pvUser Opaque user data.
927 */
928static DECLCALLBACK(int) rtFuzzObsMasterLoop(RTTHREAD hThread, void *pvUser)
929{
930 RT_NOREF(hThread);
931 int rc = VINF_SUCCESS;
932 PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)pvUser;
933
934 RTThreadUserSignal(hThread);
935
936 while ( !pThis->fShutdown
937 && RT_SUCCESS(rc))
938 {
939 uint64_t bmEvt = ASMAtomicXchgU64(&pThis->bmEvt, 0);
940 uint32_t idxObs = 0;
941 while (bmEvt != 0)
942 {
943 if (bmEvt & 0x1)
944 {
945 /* Create a new input for this observer and kick it. */
946 PRTFUZZOBSTHRD pObsThrd = &pThis->paObsThreads[idxObs];
947
948 rc = rtFuzzObsMasterInputQueueFill(pThis, pObsThrd);
949 if (RT_SUCCESS(rc))
950 RTThreadUserSignal(pObsThrd->hThread);
951 }
952
953 idxObs++;
954 bmEvt >>= 1;
955 }
956
957 rc = RTSemEventWait(pThis->hEvtGlobal, RT_INDEFINITE_WAIT);
958 }
959
960 return VINF_SUCCESS;
961}
962
963
964/**
965 * Initializes the given worker thread structure.
966 *
967 * @returns IPRT status code.
968 * @param pThis The internal fuzzing observer state.
969 * @param iObs Observer ID.
970 * @param pObsThrd The observer thread structure.
971 */
972static int rtFuzzObsWorkerThreadInit(PRTFUZZOBSINT pThis, uint32_t idObs, PRTFUZZOBSTHRD pObsThrd)
973{
974 pObsThrd->pFuzzObs = pThis;
975 pObsThrd->idObs = idObs;
976 pObsThrd->fShutdown = false;
977 pObsThrd->cInputs = 0;
978 pObsThrd->offQueueInputW = 0;
979 pObsThrd->offQueueInputR = 0;
980
981 ASMAtomicBitSet(&pThis->bmEvt, idObs);
982 return RTThreadCreate(&pObsThrd->hThread, rtFuzzObsWorkerLoop, pObsThrd, 0, RTTHREADTYPE_IO,
983 RTTHREADFLAGS_WAITABLE, "Fuzz-Worker");
984}
985
986
987/**
988 * Creates the given amount of worker threads and puts them into waiting state.
989 *
990 * @returns IPRT status code.
991 * @param pThis The internal fuzzing observer state.
992 * @param cThreads Number of worker threads to create.
993 */
994static int rtFuzzObsWorkersCreate(PRTFUZZOBSINT pThis, uint32_t cThreads)
995{
996 int rc = VINF_SUCCESS;
997 PRTFUZZOBSTHRD paObsThreads = (PRTFUZZOBSTHRD)RTMemAllocZ(cThreads * sizeof(RTFUZZOBSTHRD));
998 if (RT_LIKELY(paObsThreads))
999 {
1000 for (unsigned i = 0; i < cThreads && RT_SUCCESS(rc); i++)
1001 {
1002 rc = rtFuzzObsWorkerThreadInit(pThis, i, &paObsThreads[i]);
1003 if (RT_FAILURE(rc))
1004 {
1005 /* Rollback. */
1006
1007 }
1008 }
1009
1010 if (RT_SUCCESS(rc))
1011 {
1012 pThis->paObsThreads = paObsThreads;
1013 pThis->cThreads = cThreads;
1014 }
1015 else
1016 RTMemFree(paObsThreads);
1017 }
1018
1019 return rc;
1020}
1021
1022
1023/**
1024 * Creates the global worker thread managing the input creation and other worker threads.
1025 *
1026 * @returns IPRT status code.
1027 * @param pThis The internal fuzzing observer state.
1028 */
1029static int rtFuzzObsMasterCreate(PRTFUZZOBSINT pThis)
1030{
1031 pThis->fShutdown = false;
1032
1033 int rc = RTSemEventCreate(&pThis->hEvtGlobal);
1034 if (RT_SUCCESS(rc))
1035 {
1036 rc = RTThreadCreate(&pThis->hThreadGlobal, rtFuzzObsMasterLoop, pThis, 0, RTTHREADTYPE_IO,
1037 RTTHREADFLAGS_WAITABLE, "Fuzz-Master");
1038 if (RT_SUCCESS(rc))
1039 {
1040 RTThreadUserWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT);
1041 }
1042 else
1043 {
1044 RTSemEventDestroy(pThis->hEvtGlobal);
1045 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1046 }
1047 }
1048
1049 return rc;
1050}
1051
1052
1053/**
1054 * Sets up any configured sanitizers to cooperate with the observer.
1055 *
1056 * @returns IPRT status code.
1057 * @param pThis The internal fuzzing observer state.
1058 */
1059static int rtFuzzObsSetupSanitizerCfg(PRTFUZZOBSINT pThis)
1060{
1061 int rc = VINF_SUCCESS;
1062 bool fSep = false;
1063
1064 if (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_ASAN)
1065 {
1066 /*
1067 * Need to set abort_on_error=1 in ASAN_OPTIONS or
1068 * the sanitizer will call exit() instead of abort() and we
1069 * don't catch invalid memory accesses.
1070 */
1071 rc = RTStrAAppend(&pThis->pszSanitizerOpts, "abort_on_error=1");
1072 fSep = true;
1073 }
1074
1075 if ( RT_SUCCESS(rc)
1076 && (pThis->fSanitizers & RTFUZZOBS_SANITIZER_F_SANCOV))
1077 {
1078 /*
1079 * The coverage sanitizer will dump coverage information into a file
1080 * on process exit. Need to configure the directory where to dump it.
1081 */
1082 char aszSanCovCfg[_4K];
1083 ssize_t cch = RTStrPrintf2(&aszSanCovCfg[0], sizeof(aszSanCovCfg),
1084 "%scoverage=1:coverage_dir=%s",
1085 fSep ? ":" : "", pThis->pszTmpDir);
1086 if (cch > 0)
1087 rc = RTStrAAppend(&pThis->pszSanitizerOpts, &aszSanCovCfg[0]);
1088 else
1089 rc = VERR_BUFFER_OVERFLOW;
1090 fSep = true;
1091 }
1092
1093 if ( RT_SUCCESS(rc)
1094 && pThis->pszSanitizerOpts)
1095 {
1096 /* Add it to the environment. */
1097 if (pThis->hEnv == RTENV_DEFAULT)
1098 {
1099 /* Clone the environment to keep the default one untouched. */
1100 rc = RTEnvClone(&pThis->hEnv, RTENV_DEFAULT);
1101 }
1102 if (RT_SUCCESS(rc))
1103 rc = RTEnvSetEx(pThis->hEnv, "ASAN_OPTIONS", pThis->pszSanitizerOpts);
1104 }
1105
1106 return rc;
1107}
1108
1109
1110RTDECL(int) RTFuzzObsCreate(PRTFUZZOBS phFuzzObs, RTFUZZCTXTYPE enmType)
1111{
1112 AssertPtrReturn(phFuzzObs, VERR_INVALID_POINTER);
1113
1114 int rc = VINF_SUCCESS;
1115 PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)RTMemAllocZ(sizeof(*pThis));
1116 if (RT_LIKELY(pThis))
1117 {
1118 pThis->pszBinary = NULL;
1119 pThis->pszBinaryFilename = NULL;
1120 pThis->papszArgs = NULL;
1121 pThis->hEnv = RTENV_DEFAULT;
1122 pThis->msWaitMax = 1000;
1123 pThis->hThreadGlobal = NIL_RTTHREAD;
1124 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1125 pThis->bmEvt = 0;
1126 pThis->cThreads = 0;
1127 pThis->paObsThreads = NULL;
1128 pThis->tsLastStats = RTTimeMilliTS();
1129 pThis->Stats.cFuzzedInputsPerSec = 0;
1130 pThis->Stats.cFuzzedInputs = 0;
1131 pThis->Stats.cFuzzedInputsHang = 0;
1132 pThis->Stats.cFuzzedInputsCrash = 0;
1133 rc = RTFuzzCtxCreate(&pThis->hFuzzCtx, enmType);
1134 if (RT_SUCCESS(rc))
1135 {
1136 rc = RTFuzzTgtRecorderCreate(&pThis->hTgtRec);
1137 if (RT_SUCCESS(rc))
1138 {
1139 *phFuzzObs = pThis;
1140 return VINF_SUCCESS;
1141 }
1142 RTFuzzCtxRelease(pThis->hFuzzCtx);
1143 }
1144
1145 RTMemFree(pThis);
1146 }
1147 else
1148 rc = VERR_NO_MEMORY;
1149
1150 return rc;
1151}
1152
1153
1154RTDECL(int) RTFuzzObsDestroy(RTFUZZOBS hFuzzObs)
1155{
1156 PRTFUZZOBSINT pThis = hFuzzObs;
1157 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1158
1159 RTFuzzObsExecStop(hFuzzObs);
1160
1161 /* Clean up all acquired resources. */
1162 for (unsigned i = 0; i < pThis->cArgs; i++)
1163 RTStrFree(pThis->papszArgs[i]);
1164
1165 RTMemFree(pThis->papszArgs);
1166
1167 if (pThis->hEvtGlobal != NIL_RTSEMEVENT)
1168 RTSemEventDestroy(pThis->hEvtGlobal);
1169
1170 if (pThis->pszResultsDir)
1171 RTStrFree(pThis->pszResultsDir);
1172 if (pThis->pszTmpDir)
1173 RTStrFree(pThis->pszTmpDir);
1174 if (pThis->pszBinary)
1175 RTStrFree(pThis->pszBinary);
1176 if (pThis->pszSanitizerOpts)
1177 RTStrFree(pThis->pszSanitizerOpts);
1178 if (pThis->hEnv != RTENV_DEFAULT)
1179 {
1180 RTEnvDestroy(pThis->hEnv);
1181 pThis->hEnv = RTENV_DEFAULT;
1182 }
1183 RTFuzzTgtRecorderRelease(pThis->hTgtRec);
1184 RTFuzzCtxRelease(pThis->hFuzzCtx);
1185 RTMemFree(pThis);
1186 return VINF_SUCCESS;
1187}
1188
1189
1190RTDECL(int) RTFuzzObsQueryCtx(RTFUZZOBS hFuzzObs, PRTFUZZCTX phFuzzCtx)
1191{
1192 PRTFUZZOBSINT pThis = hFuzzObs;
1193 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1194 AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
1195
1196 RTFuzzCtxRetain(pThis->hFuzzCtx);
1197 *phFuzzCtx = pThis->hFuzzCtx;
1198 return VINF_SUCCESS;
1199}
1200
1201
1202RTDECL(int) RTFuzzObsQueryStats(RTFUZZOBS hFuzzObs, PRTFUZZOBSSTATS pStats)
1203{
1204 PRTFUZZOBSINT pThis = hFuzzObs;
1205 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1206 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
1207
1208 uint64_t tsStatsQuery = RTTimeMilliTS();
1209 uint32_t cFuzzedInputsPerSec = ASMAtomicXchgU32(&pThis->Stats.cFuzzedInputsPerSec, 0);
1210
1211 pStats->cFuzzedInputsCrash = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsCrash);
1212 pStats->cFuzzedInputsHang = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsHang);
1213 pStats->cFuzzedInputs = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputs);
1214 uint64_t cPeriodSec = (tsStatsQuery - pThis->tsLastStats) / 1000;
1215 if (cPeriodSec)
1216 {
1217 pStats->cFuzzedInputsPerSec = cFuzzedInputsPerSec / cPeriodSec;
1218 pThis->cFuzzedInputsPerSecLast = pStats->cFuzzedInputsPerSec;
1219 pThis->tsLastStats = tsStatsQuery;
1220 }
1221 else
1222 pStats->cFuzzedInputsPerSec = pThis->cFuzzedInputsPerSecLast;
1223 return VINF_SUCCESS;
1224}
1225
1226
1227RTDECL(int) RTFuzzObsSetTmpDirectory(RTFUZZOBS hFuzzObs, const char *pszTmp)
1228{
1229 PRTFUZZOBSINT pThis = hFuzzObs;
1230 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1231 AssertPtrReturn(pszTmp, VERR_INVALID_POINTER);
1232
1233 int rc = VINF_SUCCESS;
1234 pThis->pszTmpDir = RTStrDup(pszTmp);
1235 if (!pThis->pszTmpDir)
1236 rc = VERR_NO_STR_MEMORY;
1237 return rc;
1238}
1239
1240
1241RTDECL(int) RTFuzzObsSetResultDirectory(RTFUZZOBS hFuzzObs, const char *pszResults)
1242{
1243 PRTFUZZOBSINT pThis = hFuzzObs;
1244 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1245 AssertPtrReturn(pszResults, VERR_INVALID_POINTER);
1246
1247 int rc = VINF_SUCCESS;
1248 pThis->pszResultsDir = RTStrDup(pszResults);
1249 if (!pThis->pszResultsDir)
1250 rc = VERR_NO_STR_MEMORY;
1251 return rc;
1252}
1253
1254
1255RTDECL(int) RTFuzzObsSetTestBinary(RTFUZZOBS hFuzzObs, const char *pszBinary, RTFUZZOBSINPUTCHAN enmInputChan)
1256{
1257 PRTFUZZOBSINT pThis = hFuzzObs;
1258 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1259 AssertPtrReturn(pszBinary, VERR_INVALID_POINTER);
1260
1261 int rc = VINF_SUCCESS;
1262 pThis->enmInputChan = enmInputChan;
1263 pThis->pszBinary = RTStrDup(pszBinary);
1264 if (RT_UNLIKELY(!pThis->pszBinary))
1265 rc = VERR_NO_STR_MEMORY;
1266 else
1267 pThis->pszBinaryFilename = RTPathFilename(pThis->pszBinary);
1268 return rc;
1269}
1270
1271
1272RTDECL(int) RTFuzzObsSetTestBinaryArgs(RTFUZZOBS hFuzzObs, const char * const *papszArgs, unsigned cArgs)
1273{
1274 PRTFUZZOBSINT pThis = hFuzzObs;
1275 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1276
1277 int rc = VINF_SUCCESS;
1278 char **papszArgsOld = pThis->papszArgs;
1279 if (papszArgs)
1280 {
1281 pThis->papszArgs = (char **)RTMemAllocZ(sizeof(char **) * (cArgs + 1));
1282 if (RT_LIKELY(pThis->papszArgs))
1283 {
1284 for (unsigned i = 0; i < cArgs; i++)
1285 {
1286 pThis->papszArgs[i] = RTStrDup(papszArgs[i]);
1287 if (RT_UNLIKELY(!pThis->papszArgs[i]))
1288 {
1289 while (i > 0)
1290 {
1291 i--;
1292 RTStrFree(pThis->papszArgs[i]);
1293 }
1294 break;
1295 }
1296 }
1297
1298 if (RT_FAILURE(rc))
1299 RTMemFree(pThis->papszArgs);
1300 }
1301 else
1302 rc = VERR_NO_MEMORY;
1303
1304 if (RT_FAILURE(rc))
1305 pThis->papszArgs = papszArgsOld;
1306 else
1307 pThis->cArgs = cArgs;
1308 }
1309 else
1310 {
1311 pThis->papszArgs = NULL;
1312 pThis->cArgs = 0;
1313 if (papszArgsOld)
1314 {
1315 char **ppsz = papszArgsOld;
1316 while (*ppsz != NULL)
1317 {
1318 RTStrFree(*ppsz);
1319 ppsz++;
1320 }
1321 RTMemFree(papszArgsOld);
1322 }
1323 }
1324
1325 return rc;
1326}
1327
1328
1329RTDECL(int) RTFuzzObsSetTestBinaryEnv(RTFUZZOBS hFuzzObs, RTENV hEnv)
1330{
1331 PRTFUZZOBSINT pThis = hFuzzObs;
1332 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1333
1334 pThis->hEnv = hEnv;
1335 return VINF_SUCCESS;
1336}
1337
1338
1339RTDECL(int) RTFuzzObsSetTestBinarySanitizers(RTFUZZOBS hFuzzObs, uint32_t fSanitizers)
1340{
1341 PRTFUZZOBSINT pThis = hFuzzObs;
1342 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1343
1344 pThis->fSanitizers = fSanitizers;
1345 return VINF_SUCCESS;
1346}
1347
1348
1349RTDECL(int) RTFuzzObsSetTestBinaryTimeout(RTFUZZOBS hFuzzObs, RTMSINTERVAL msTimeoutMax)
1350{
1351 PRTFUZZOBSINT pThis = hFuzzObs;
1352 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1353
1354 pThis->msWaitMax = msTimeoutMax;
1355 return VINF_SUCCESS;
1356}
1357
1358
1359RTDECL(int) RTFuzzObsExecStart(RTFUZZOBS hFuzzObs, uint32_t cProcs)
1360{
1361 PRTFUZZOBSINT pThis = hFuzzObs;
1362 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1363 AssertReturn(cProcs <= sizeof(uint64_t) * 8, VERR_INVALID_PARAMETER);
1364 AssertReturn( pThis->enmInputChan == RTFUZZOBSINPUTCHAN_FILE
1365 || pThis->pszTmpDir != NULL,
1366 VERR_INVALID_STATE);
1367
1368 int rc = VINF_SUCCESS;
1369 if (!cProcs)
1370 cProcs = RT_MIN(RTMpGetPresentCoreCount(), sizeof(uint64_t) * 8);
1371
1372 rc = rtFuzzObsSetupSanitizerCfg(pThis);
1373 if (RT_SUCCESS(rc))
1374 {
1375 /* Spin up the worker threads first. */
1376 rc = rtFuzzObsWorkersCreate(pThis, cProcs);
1377 if (RT_SUCCESS(rc))
1378 {
1379 /* Spin up the global thread. */
1380 rc = rtFuzzObsMasterCreate(pThis);
1381 }
1382 }
1383
1384 return rc;
1385}
1386
1387
1388RTDECL(int) RTFuzzObsExecStop(RTFUZZOBS hFuzzObs)
1389{
1390 PRTFUZZOBSINT pThis = hFuzzObs;
1391 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1392
1393 /* Wait for the master thread to terminate. */
1394 if (pThis->hThreadGlobal != NIL_RTTHREAD)
1395 {
1396 ASMAtomicXchgBool(&pThis->fShutdown, true);
1397 RTSemEventSignal(pThis->hEvtGlobal);
1398 RTThreadWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT, NULL);
1399 pThis->hThreadGlobal = NIL_RTTHREAD;
1400 }
1401
1402 /* Destroy the workers. */
1403 if (pThis->paObsThreads)
1404 {
1405 for (unsigned i = 0; i < pThis->cThreads; i++)
1406 {
1407 PRTFUZZOBSTHRD pThrd = &pThis->paObsThreads[i];
1408 ASMAtomicXchgBool(&pThrd->fShutdown, true);
1409 RTThreadUserSignal(pThrd->hThread);
1410 RTThreadWait(pThrd->hThread, RT_INDEFINITE_WAIT, NULL);
1411 }
1412
1413 RTMemFree(pThis->paObsThreads);
1414 pThis->paObsThreads = NULL;
1415 pThis->cThreads = 0;
1416 }
1417
1418 RTSemEventDestroy(pThis->hEvtGlobal);
1419 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1420 return VINF_SUCCESS;
1421}
1422
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