VirtualBox

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

Last change on this file since 72465 was 72465, checked in by vboxsync, 7 years ago

Runtime/RTFuzz: Save fuzzed inputs which cause crashes or client hangs, rewrite client execution code to support looking for timeouts and capturing stdout/stderr of the fuzzed process for inspection. Also make it possible to specify precisely where the input filename is given on the command line using a ${INPUT} placeholder

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