VirtualBox

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

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

Runtime/fuzz: Updates

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