VirtualBox

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

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

scm fix

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette