VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use