VirtualBox

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

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

Runtime/RTFuzz: Started to extend the master command to provide a network interface for controlling the fuzzing process. The interface uses JSON requests and responses to control the fuzzing process, like creating a new fuzzing run and suspending, resuming and stopping it. The JSON request contains everything required (except for the binary itself) for easier control of multiple masters on different hosts via a central control mechanism (later integration into the validation kit).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.8 KB
Line 
1/* $Id: fuzz-observer.cpp 72571 2018-06-15 20:36:17Z 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 /** 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 {
883 pThis->paObsThreads = paObsThreads;
884 pThis->cThreads = cThreads;
885 }
886 else
887 RTMemFree(paObsThreads);
888 }
889
890 return rc;
891}
892
893
894/**
895 * Creates the global worker thread managing the input creation and other worker threads.
896 *
897 * @returns IPRT status code.
898 * @param pThis The internal fuzzing observer state.
899 */
900static int rtFuzzObsMasterCreate(PRTFUZZOBSINT pThis)
901{
902 pThis->fShutdown = false;
903
904 int rc = RTSemEventCreate(&pThis->hEvtGlobal);
905 if (RT_SUCCESS(rc))
906 {
907 rc = RTThreadCreate(&pThis->hThreadGlobal, rtFuzzObsMasterLoop, pThis, 0, RTTHREADTYPE_IO,
908 RTTHREADFLAGS_WAITABLE, "Fuzz-Master");
909 if (RT_SUCCESS(rc))
910 {
911 RTThreadUserWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT);
912 }
913 else
914 {
915 RTSemEventDestroy(pThis->hEvtGlobal);
916 pThis->hEvtGlobal = NIL_RTSEMEVENT;
917 }
918 }
919
920 return rc;
921}
922
923
924RTDECL(int) RTFuzzObsCreate(PRTFUZZOBS phFuzzObs)
925{
926 AssertPtrReturn(phFuzzObs, VERR_INVALID_POINTER);
927
928 int rc = VINF_SUCCESS;
929 PRTFUZZOBSINT pThis = (PRTFUZZOBSINT)RTMemAllocZ(sizeof(*pThis));
930 if (RT_LIKELY(pThis))
931 {
932 pThis->pszBinary = NULL;
933 pThis->papszArgs = NULL;
934 pThis->fFlags = 0;
935 pThis->msWaitMax = 1000;
936 pThis->hThreadGlobal = NIL_RTTHREAD;
937 pThis->hEvtGlobal = NIL_RTSEMEVENT;
938 pThis->bmEvt = 0;
939 pThis->cThreads = 0;
940 pThis->paObsThreads = NULL;
941 rc = RTFuzzCtxCreate(&pThis->hFuzzCtx);
942 if (RT_SUCCESS(rc))
943 {
944 *phFuzzObs = pThis;
945 return VINF_SUCCESS;
946 }
947
948 RTMemFree(pThis);
949 }
950 else
951 rc = VERR_NO_MEMORY;
952
953 return rc;
954}
955
956
957RTDECL(int) RTFuzzObsDestroy(RTFUZZOBS hFuzzObs)
958{
959 PRTFUZZOBSINT pThis = hFuzzObs;
960 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
961
962 RTFuzzObsExecStop(hFuzzObs);
963
964 /* Clean up all acquired resources. */
965 for (unsigned i = 0; i < pThis->cArgs; i++)
966 RTStrFree(pThis->papszArgs[i]);
967
968 RTMemFree(pThis->papszArgs);
969
970 if (pThis->hEvtGlobal != NIL_RTSEMEVENT)
971 RTSemEventDestroy(pThis->hEvtGlobal);
972
973 if (pThis->pszResultsDir)
974 RTStrFree(pThis->pszResultsDir);
975 if (pThis->pszTmpDir)
976 RTStrFree(pThis->pszTmpDir);
977 if (pThis->pszBinary)
978 RTStrFree(pThis->pszBinary);
979 RTFuzzCtxRelease(pThis->hFuzzCtx);
980 RTMemFree(pThis);
981 return VINF_SUCCESS;
982}
983
984
985RTDECL(int) RTFuzzObsQueryCtx(RTFUZZOBS hFuzzObs, PRTFUZZCTX phFuzzCtx)
986{
987 PRTFUZZOBSINT pThis = hFuzzObs;
988 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
989 AssertPtrReturn(phFuzzCtx, VERR_INVALID_POINTER);
990
991 RTFuzzCtxRetain(pThis->hFuzzCtx);
992 *phFuzzCtx = pThis->hFuzzCtx;
993 return VINF_SUCCESS;
994}
995
996
997RTDECL(int) RTFuzzObsSetTmpDirectory(RTFUZZOBS hFuzzObs, const char *pszTmp)
998{
999 PRTFUZZOBSINT pThis = hFuzzObs;
1000 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1001 AssertPtrReturn(pszTmp, VERR_INVALID_POINTER);
1002
1003 int rc = VINF_SUCCESS;
1004 pThis->pszTmpDir = RTStrDup(pszTmp);
1005 if (!pThis->pszTmpDir)
1006 rc = VERR_NO_STR_MEMORY;
1007 return rc;
1008}
1009
1010
1011RTDECL(int) RTFuzzObsSetResultDirectory(RTFUZZOBS hFuzzObs, const char *pszResults)
1012{
1013 PRTFUZZOBSINT pThis = hFuzzObs;
1014 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1015 AssertPtrReturn(pszResults, VERR_INVALID_POINTER);
1016
1017 int rc = VINF_SUCCESS;
1018 pThis->pszResultsDir = RTStrDup(pszResults);
1019 if (!pThis->pszResultsDir)
1020 rc = VERR_NO_STR_MEMORY;
1021 return rc;
1022}
1023
1024
1025RTDECL(int) RTFuzzObsSetTestBinary(RTFUZZOBS hFuzzObs, const char *pszBinary, uint32_t fFlags)
1026{
1027 PRTFUZZOBSINT pThis = hFuzzObs;
1028 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1029 AssertPtrReturn(pszBinary, VERR_INVALID_POINTER);
1030
1031 int rc = VINF_SUCCESS;
1032 pThis->fFlags = fFlags;
1033 pThis->pszBinary = RTStrDup(pszBinary);
1034 if (RT_UNLIKELY(!pThis->pszBinary))
1035 rc = VERR_NO_STR_MEMORY;
1036 return rc;
1037}
1038
1039
1040RTDECL(int) RTFuzzObsSetTestBinaryArgs(RTFUZZOBS hFuzzObs, const char * const *papszArgs, unsigned cArgs)
1041{
1042 PRTFUZZOBSINT pThis = hFuzzObs;
1043 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1044
1045 int rc = VINF_SUCCESS;
1046 char **papszArgsOld = pThis->papszArgs;
1047 if (papszArgs)
1048 {
1049 pThis->papszArgs = (char **)RTMemAllocZ(sizeof(char **) * (cArgs + 1));
1050 if (RT_LIKELY(pThis->papszArgs))
1051 {
1052 char **ppszOwn = pThis->papszArgs;
1053 const char * const *ppsz = papszArgs;
1054 while ( *ppsz != NULL
1055 && RT_SUCCESS(rc))
1056 {
1057 *ppszOwn = RTStrDup(*ppsz);
1058 if (RT_UNLIKELY(!*ppszOwn))
1059 {
1060 while (ppszOwn > pThis->papszArgs)
1061 {
1062 ppszOwn--;
1063 RTStrFree(*ppszOwn);
1064 }
1065 break;
1066 }
1067
1068 ppszOwn++;
1069 ppsz++;
1070 }
1071
1072 if (RT_FAILURE(rc))
1073 RTMemFree(pThis->papszArgs);
1074 }
1075 else
1076 rc = VERR_NO_MEMORY;
1077
1078 if (RT_FAILURE(rc))
1079 pThis->papszArgs = papszArgsOld;
1080 else
1081 pThis->cArgs = cArgs;
1082 }
1083 else
1084 {
1085 pThis->papszArgs = NULL;
1086 pThis->cArgs = 0;
1087 if (papszArgsOld)
1088 {
1089 char **ppsz = papszArgsOld;
1090 while (*ppsz != NULL)
1091 {
1092 RTStrFree(*ppsz);
1093 ppsz++;
1094 }
1095 RTMemFree(papszArgsOld);
1096 }
1097 }
1098
1099 return rc;
1100}
1101
1102
1103RTDECL(int) RTFuzzObsExecStart(RTFUZZOBS hFuzzObs, uint32_t cProcs)
1104{
1105 PRTFUZZOBSINT pThis = hFuzzObs;
1106 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1107 AssertReturn(cProcs <= sizeof(uint64_t) * 8, VERR_INVALID_PARAMETER);
1108 AssertReturn( (pThis->fFlags & RTFUZZ_OBS_BINARY_F_INPUT_FILE)
1109 || pThis->pszTmpDir != NULL,
1110 VERR_INVALID_STATE);
1111
1112 int rc = VINF_SUCCESS;
1113 if (!cProcs)
1114 cProcs = RT_MIN(RTMpGetPresentCoreCount(), sizeof(uint64_t) * 8);
1115
1116 /* Spin up the worker threads first. */
1117 rc = rtFuzzObsWorkersCreate(pThis, cProcs);
1118 if (RT_SUCCESS(rc))
1119 {
1120 /* Spin up the global thread. */
1121 rc = rtFuzzObsMasterCreate(pThis);
1122 }
1123
1124 return rc;
1125}
1126
1127
1128RTDECL(int) RTFuzzObsExecStop(RTFUZZOBS hFuzzObs)
1129{
1130 PRTFUZZOBSINT pThis = hFuzzObs;
1131 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1132
1133 /* Wait for the master thread to terminate. */
1134 if (pThis->hThreadGlobal != NIL_RTTHREAD)
1135 {
1136 ASMAtomicXchgBool(&pThis->fShutdown, true);
1137 RTSemEventSignal(pThis->hEvtGlobal);
1138 RTThreadWait(pThis->hThreadGlobal, RT_INDEFINITE_WAIT, NULL);
1139 pThis->hThreadGlobal = NIL_RTTHREAD;
1140 }
1141
1142 /* Destroy the workers. */
1143 if (pThis->paObsThreads)
1144 {
1145 for (unsigned i = 0; i < pThis->cThreads; i++)
1146 {
1147 PRTFUZZOBSTHRD pThrd = &pThis->paObsThreads[i];
1148 ASMAtomicXchgBool(&pThrd->fShutdown, true);
1149 RTThreadUserSignal(pThrd->hThread);
1150 RTThreadWait(pThrd->hThread, RT_INDEFINITE_WAIT, NULL);
1151 if (pThrd->hFuzzInput)
1152 RTFuzzInputRelease(pThrd->hFuzzInput);
1153 }
1154
1155 RTMemFree(pThis->paObsThreads);
1156 pThis->paObsThreads = NULL;
1157 pThis->cThreads = 0;
1158 }
1159
1160 RTSemEventDestroy(pThis->hEvtGlobal);
1161 pThis->hEvtGlobal = NIL_RTSEMEVENT;
1162 return VINF_SUCCESS;
1163}
1164
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