VirtualBox

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

Last change on this file since 107713 was 106061, checked in by vboxsync, 5 months ago

Copyright year updates by scm.

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