VirtualBox

Changeset 72465 in vbox for trunk/src/VBox/Runtime/common


Ignore:
Timestamp:
Jun 6, 2018 9:12:23 PM (7 years ago)
Author:
vboxsync
Message:

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

Location:
trunk/src/VBox/Runtime/common/fuzz
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp

    r72454 r72465  
    3535#include <iprt/assert.h>
    3636#include <iprt/ctype.h>
     37#include <iprt/dir.h>
    3738#include <iprt/err.h>
    3839#include <iprt/env.h>
     
    4344#include <iprt/path.h>
    4445#include <iprt/pipe.h>
     46#include <iprt/poll.h>
    4547#include <iprt/process.h>
    4648#include <iprt/semaphore.h>
     
    4951#include <iprt/time.h>
    5052#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
    5161
    5262
     
    91101    /** Temp directory for input files. */
    92102    char                       *pszTmpDir;
     103    /** Results directory. */
     104    char                       *pszResultsDir;
    93105    /** The binary to run. */
    94106    char                       *pszBinary;
     
    97109    /** Number of arguments. */
    98110    uint32_t                    cArgs;
     111    /** Maximum time to wait for the client to terminate until it is considered hung and killed. */
     112    RTMSINTERVAL                msWaitMax;
    99113    /** Flags controlling how the binary is executed. */
    100114    uint32_t                    fFlags;
     
    114128
    115129
     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
    116690
    117691/**
     
    119693 *
    120694 * @returns IPRT status code.
    121  * @param   hThread               The thread handle.
     695 * @param   hThrd               The thread handle.
    122696 * @param   pvUser              Opaque user data.
    123697 */
    124 static DECLCALLBACK(int) rtFuzzObsWorkerLoop(RTTHREAD hThread, void *pvUser)
     698static DECLCALLBACK(int) rtFuzzObsWorkerLoop(RTTHREAD hThrd, void *pvUser)
    125699{
    126700    PRTFUZZOBSTHRD pObsThrd = (PRTFUZZOBSTHRD)pvUser;
    127701    PRTFUZZOBSINT pThis = pObsThrd->pFuzzObs;
    128     char **papszArgs = NULL;
    129     RTHANDLE NilHandle;
    130     NilHandle.enmType = RTHANDLETYPE_PIPE;
    131     NilHandle.u.hPipe = NIL_RTPIPE;
    132 
    133     if (!(pThis->fFlags & RTFUZZ_OBS_BINARY_F_INPUT_FILE))
    134     {
    135         papszArgs = (char **)RTMemAllocZ((pThis->cArgs + 1) * sizeof(char *));
    136         if (RT_LIKELY(papszArgs))
    137         {
    138             for (uint32_t i = 0; i < pThis->cArgs; i++)
    139                 papszArgs[i] = pThis->papszArgs[i];
    140         }
    141     }
    142     else
    143         papszArgs = pThis->papszArgs;
     702    PRTFUZZOBSEXECCTX pExecCtx = NULL;
     703
     704    int rc = rtFuzzObsExecCtxCreate(&pExecCtx, pThis);
     705    if (RT_FAILURE(rc))
     706        return rc;
    144707
    145708    while (!pObsThrd->fShutdown)
    146709    {
     710        char szInput[RTPATH_MAX];
     711
    147712        /* Wait for work. */
    148         int rc = RTThreadUserWait(hThread, RT_INDEFINITE_WAIT);
     713        rc = RTThreadUserWait(hThrd, RT_INDEFINITE_WAIT);
    149714        AssertRC(rc);
    150715
     
    152717            break;
    153718
    154         bool f = ASMAtomicXchgBool(&pObsThrd->fNewInput, false);
    155         if (!f)
     719        if (!ASMAtomicXchgBool(&pObsThrd->fNewInput, false))
    156720            continue;
    157721
    158722        AssertPtr(pObsThrd->hFuzzInput);
    159723
    160         /* Create the stdout/stderr pipe. */
    161         RTPIPE hPipeStdOutErrR;
    162         RTPIPE hPipeStdOutErrW;
    163         rc = RTPipeCreate(&hPipeStdOutErrR, &hPipeStdOutErrW, RTPIPE_C_INHERIT_WRITE);
     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
    164752        if (RT_SUCCESS(rc))
    165753        {
    166             RTHANDLE HandleStdOutErr;
    167             HandleStdOutErr.enmType = RTHANDLETYPE_PIPE;
    168             HandleStdOutErr.u.hPipe = hPipeStdOutErrW;
     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();
    169765
    170766            if (pThis->fFlags & RTFUZZ_OBS_BINARY_F_INPUT_FILE)
    171             {
    172                 char szInput[RTPATH_MAX];
    173                 char szDigest[RTMD5_STRING_LEN + 1];
    174                 char szFilename[sizeof(szDigest) + 32];
    175 
    176                 rc = RTFuzzInputQueryDigestString(pObsThrd->hFuzzInput, &szDigest[0], sizeof(szDigest));
    177                 AssertRC(rc);
    178 
    179                 ssize_t cbBuf = RTStrPrintf2(&szFilename[0], sizeof(szFilename), "%u-%s", pObsThrd->idObs, &szDigest[0]);
    180                 Assert(cbBuf > 0); RT_NOREF(cbBuf);
    181 
    182                 RT_ZERO(szInput);
    183                 rc = RTPathJoin(szInput, sizeof(szInput), pThis->pszTmpDir, &szFilename[0]);
    184                 AssertRC(rc);
    185 
    186                 rc = RTFuzzInputWriteToFile(pObsThrd->hFuzzInput, &szInput[0]);
    187                 if (RT_SUCCESS(rc))
    188                 {
    189                     papszArgs[pThis->cArgs - 1] = &szInput[0];
    190 
    191                     RTPROCESS hProc = NIL_RTPROCESS;
    192                     rc = RTProcCreateEx(pThis->pszBinary, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &NilHandle,
    193                                         NULL, NULL, NULL, NULL, &hProc);
    194                     if (RT_SUCCESS(rc))
    195                     {
    196                         RTPipeClose(hPipeStdOutErrW);
    197                         RTPROCSTATUS ProcStatus = { -1, RTPROCEXITREASON_ABEND };
    198                         rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
    199                         if (RT_SUCCESS(rc))
    200                         {
    201                             if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL)
    202                             {
    203                                 pObsThrd->fKeepInput = true;
    204                                 RTPrintf("Abnormal exit detected!\n");
    205                             }
    206                             else
    207                             {
    208                                 /*
    209                                  * Fuzzed inputs are only added if the client signalled a success in parsing the input.
    210                                  * Inputs which lead to errors are not added because it is assumed that the target
    211                                  * successfully verified the input data.
    212                                  */
    213                                 //if (ProcStatus.iStatus == RTEXITCODE_SUCCESS)
    214                                 //    pObsThrd->fKeepInput = true;
    215                             }
    216                         }
    217                         else
    218                             AssertFailed();
    219                     }
    220                     else
    221                         AssertFailed();
    222 
    223                     rc = RTFileDelete(&szInput[0]);
    224                     AssertRC(rc);
    225                 }
    226                 else
    227                     AssertFailed();
    228             }
    229             else
    230             {
    231                 RTPIPE hPipeR;
    232                 RTPIPE hPipeW;
    233                 rc = RTPipeCreate(&hPipeR, &hPipeW, RTPIPE_C_INHERIT_READ);
    234                 if (RT_SUCCESS(rc))
    235                 {
    236                     RTPROCESS hProc = NIL_RTPROCESS;
    237                     RTHANDLE Handle;
    238                     Handle.enmType = RTHANDLETYPE_PIPE;
    239                     Handle.u.hPipe = hPipeR;
    240                     rc = RTProcCreateEx(pThis->pszBinary, papszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &Handle,
    241                                         &HandleStdOutErr, &HandleStdOutErr, NULL, NULL, &hProc);
    242                     if (RT_SUCCESS(rc))
    243                     {
    244                         /* Pump the input data. */
    245                         RTPipeClose(hPipeStdOutErrW);
    246 
    247                         uint8_t *pbInput;
    248                         size_t  cbInput;
    249                         rc = RTFuzzInputQueryData(pObsThrd->hFuzzInput, (void **)&pbInput, &cbInput);
    250                         if (RT_SUCCESS(rc))
    251                         {
    252                             rc = RTPipeWriteBlocking(hPipeW, pbInput, cbInput, NULL);
    253                             RTPipeClose(hPipeW);
    254 
    255                             RTPROCSTATUS ProcStatus = { -1, RTPROCEXITREASON_ABEND };
    256                             rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
    257                             if (RT_SUCCESS(rc))
    258                             {
    259                                 if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL)
    260                                 {
    261                                     pObsThrd->fKeepInput = true;
    262                                     RTPrintf("Abnormal exit detected!\n");
    263                                 }
    264                                 else
    265                                 {
    266                                     /*
    267                                      * Fuzzed inputs are only added if the client signalled a success in parsing the input.
    268                                      * Inputs which lead to errors are not added because it is assumed that the target
    269                                      * successfully verified the input data.
    270                                      */
    271                                     if (ProcStatus.iStatus == RTEXITCODE_SUCCESS)
    272                                         pObsThrd->fKeepInput = true;
    273                                 }
    274                             }
    275                         }
    276                     }
    277 
    278                     RTPipeClose(hPipeW);
    279                 }
    280             }
    281 
    282             RTPipeClose(hPipeStdOutErrR);
    283         }
    284         else
    285             AssertFailed();
     767                RTFileDelete(&szInput[0]);
     768        }
    286769
    287770        ASMAtomicBitSet(&pThis->bmEvt, pObsThrd->idObs);
     
    289772    }
    290773
     774    rtFuzzObsExecCtxDestroy(pThis, pExecCtx);
    291775    return VINF_SUCCESS;
    292776}
     
    443927    if (RT_LIKELY(pThis))
    444928    {
    445         pThis->pszBinary   = NULL;
    446         pThis->papszArgs   = NULL;
    447         pThis->fFlags      = 0;
     929        pThis->pszBinary     = NULL;
     930        pThis->papszArgs     = NULL;
     931        pThis->fFlags        = 0;
     932        pThis->msWaitMax     = 1000;
    448933        pThis->hThreadGlobal = NIL_RTTHREAD;
    449         pThis->hEvtGlobal  = NIL_RTSEMEVENT;
    450         pThis->bmEvt       = 0;
    451         pThis->cThreads    = 0;
     934        pThis->hEvtGlobal    = NIL_RTSEMEVENT;
     935        pThis->bmEvt         = 0;
     936        pThis->cThreads      = 0;
    452937        pThis->paObsThreads  = NULL;
    453938        rc = RTFuzzCtxCreate(&pThis->hFuzzCtx);
     
    503988        RTSemEventDestroy(pThis->hEvtGlobal);
    504989
     990    if (pThis->pszResultsDir)
     991        RTStrFree(pThis->pszResultsDir);
     992    if (pThis->pszTmpDir)
     993        RTStrFree(pThis->pszTmpDir);
    505994    if (pThis->pszBinary)
    506995        RTStrFree(pThis->pszBinary);
     
    5321021    pThis->pszTmpDir = RTStrDup(pszTmp);
    5331022    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)
    5341037        rc = VERR_NO_STR_MEMORY;
    5351038    return rc;
  • trunk/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp

    r72454 r72465  
    7171 */
    7272static RTEXITCODE rtFuzzCmdMasterDoIt(const char *pszBinary, uint32_t cProcs, const char *pszInpSeedDir,
    73                                       size_t cbInputMax, const char * const *papszClientArgs, unsigned cClientArgs,
    74                                       bool fInputFile, const char *pszTmpDir)
     73                                      const char *pszResultsDir, size_t cbInputMax, const char * const *papszClientArgs,
     74                                      unsigned cClientArgs, bool fInputFile, const char *pszTmpDir)
    7575{
    7676    RTFUZZOBS hFuzzObs;
     
    9393
    9494            if (RT_SUCCESS(rc))
    95                 rc = RTFuzzObsSetTestBinary(hFuzzObs, pszBinary, fFlags);
     95                rc = RTFuzzObsSetResultDirectory(hFuzzObs, pszResultsDir);
    9696            if (RT_SUCCESS(rc))
    9797            {
    98                 rc = RTFuzzObsSetTestBinaryArgs(hFuzzObs, papszClientArgs, cClientArgs);
     98                rc = RTFuzzObsSetTestBinary(hFuzzObs, pszBinary, fFlags);
    9999                if (RT_SUCCESS(rc))
    100100                {
    101                     rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbInputMax);
     101                    rc = RTFuzzObsSetTestBinaryArgs(hFuzzObs, papszClientArgs, cClientArgs);
    102102                    if (RT_SUCCESS(rc))
    103103                    {
    104                         rc = RTFuzzCtxCorpusInputAddFromDirPath(hFuzzCtx, pszInpSeedDir);
     104                        rc = RTFuzzCtxCfgSetInputSeedMaximum(hFuzzCtx, cbInputMax);
    105105                        if (RT_SUCCESS(rc))
    106106                        {
    107                             rc = RTFuzzObsExecStart(hFuzzObs, cProcs);
     107                            rc = RTFuzzCtxCorpusInputAddFromDirPath(hFuzzCtx, pszInpSeedDir);
    108108                            if (RT_SUCCESS(rc))
    109109                            {
    110                                 RTThreadSleep(3600 * RT_MS_1SEC);
    111                                 RTFuzzObsExecStop(hFuzzObs);
     110                                rc = RTFuzzObsExecStart(hFuzzObs, cProcs);
     111                                if (RT_SUCCESS(rc))
     112                                {
     113                                    RTThreadSleep(3600 * RT_MS_1SEC);
     114                                    RTFuzzObsExecStop(hFuzzObs);
     115                                }
     116                                else
     117                                    rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to start fuzzing observer: %Rrc\n", rc);
    112118                            }
    113119                            else
    114                                 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to start fuzzing observer: %Rrc\n", rc);
     120                                rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to load corpus seeds from \"%s\": %Rrc\n", pszInpSeedDir, rc);
    115121                        }
    116122                        else
    117                             rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to load corpus seeds from \"%s\": %Rrc\n", pszInpSeedDir, rc);
     123                            rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set maximum input size to %zu: %Rrc\n", cbInputMax, rc);
    118124                    }
    119125                    else
    120                         rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set maximum input size to %zu: %Rrc\n", cbInputMax, rc);
     126                        rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set test program arguments: %Rrc\n", rc);
    121127                }
    122128                else
    123                     rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set test program arguments: %Rrc\n", rc);
     129                    rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set the specified binary as test program: %Rrc\n", rc);
    124130            }
    125             else
    126                 rc = rtFuzzCmdMasterErrorRc(NULL, rc, "Failed to set the specified binary as test program: %Rrc\n", rc);
    127131
    128132            RTFuzzCtxRelease(hFuzzCtx);
     
    153157        { "--input-seed-dir",                  's', RTGETOPT_REQ_STRING  },
    154158        { "--input-seed-size-max",             'm', RTGETOPT_REQ_UINT32  },
     159        { "--results-dir",                     'r', RTGETOPT_REQ_STRING  },
    155160        { "--args",                            'a', RTGETOPT_REQ_STRING  },
    156161        { "--help",                            'h', RTGETOPT_REQ_NOTHING },
     
    173178        bool        fInputFile = false;
    174179        const char *pszTmpDir = NULL;
     180        const char *pszResultsDir = NULL;
    175181
    176182        /* Argument parsing loop. */
     
    183189            {
    184190                case 0:
    185                     rcExit = rtFuzzCmdMasterDoIt(pszBinary, cProcs, pszInpSeedDir, cbInputMax,
     191                    rcExit = rtFuzzCmdMasterDoIt(pszBinary, cProcs, pszInpSeedDir, pszResultsDir, cbInputMax,
    186192                                                 papszClientArgs, cClientArgs, fInputFile, pszTmpDir);
    187193                    fContinue = false;
     
    199205                    pszTmpDir = ValueUnion.psz;
    200206                    fInputFile = true;
     207                    break;
     208
     209                case 'r':
     210                    pszResultsDir = ValueUnion.psz;
    201211                    break;
    202212
Note: See TracChangeset for help on using the changeset viewer.

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