VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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