static bool PrePatchTimeoutSyscall(uint32_t tid, CONTEXT* ctxt, SYSCALL_STANDARD std, int syscall) {
    assert(!inFakeTimeoutMode[tid]);  // canary: this will probably fail...
    int64_t waitNsec = 0;

    // Per-syscall manipulation. This code either succeeds, fakes timeout value and sets waitNsec, or returns false
    int timeoutArg = getTimeoutArg(syscall);
    if (syscall == SYS_futex) {
        // Check preconditions
        assert(timeoutArg == 3);
        int* uaddr = (int*) PIN_GetSyscallArgument(ctxt, std, 0);
        int op = (int) PIN_GetSyscallArgument(ctxt, std, 1);
        const struct timespec* timeout = (const struct timespec*) PIN_GetSyscallArgument(ctxt, std, 3);

        //info("FUTEX op %d  waitOp %d uaddr %p ts %p", op, isFutexWaitOp(op), uaddr, timeout);
        if (!(uaddr && isFutexWaitOp(op) && timeout)) return false;  // not a timeout FUTEX_WAIT

        waitNsec = timeout->tv_sec*1000000000L + timeout->tv_nsec;

        if (op | FUTEX_CLOCK_REALTIME) {
            // NOTE: FUTEX_CLOCK_REALTIME is not a documented interface AFAIK, but looking at the Linux source code + with some verification, this is the xlat
            uint32_t domain = zinfo->procArray[procIdx]->getClockDomain();
            uint64_t simNs = cyclesToNs(zinfo->globPhaseCycles);
            uint64_t offsetNs = simNs + zinfo->clockDomainInfo[domain].realtimeOffsetNs;
            //info(" REALTIME FUTEX: %ld %ld %ld %ld", waitNsec, simNs, offsetNs, waitNsec-offsetNs);
            waitNsec = (waitNsec > (int64_t)offsetNs)? (waitNsec - offsetNs) : 0;
        }

        if (waitNsec <= 0) return false;  // while technically waiting, this does not block. I'm guessing this is done for trylocks? It's weird.

        fakeTimeouts[tid].tv_sec = 0;
        fakeTimeouts[tid].tv_nsec = 20*1000*1000;  // timeout every 20ms of actual host time
        PIN_SetSyscallArgument(ctxt, std, 3, (ADDRINT)&fakeTimeouts[tid]);
    } else {
        assert(syscall == SYS_epoll_wait || syscall == SYS_epoll_pwait || syscall == SYS_poll);
        int timeout = (int) PIN_GetSyscallArgument(ctxt, std, timeoutArg);
        if (timeout <= 0) return false;
        //info("[%d] pre-patch epoll_wait/pwait", tid);

        PIN_SetSyscallArgument(ctxt, std, timeoutArg, 20); // 20ms timeout
        waitNsec = ((uint64_t)timeout)*1000*1000;  // timeout is in ms
    }

    //info("[%d] pre-patch %s (%d) waitNsec = %ld", tid, GetSyscallName(syscall), syscall, waitNsec);

    uint64_t waitCycles = waitNsec*zinfo->freqMHz/1000;
    uint64_t waitPhases = waitCycles/zinfo->phaseLength;
    if (waitPhases < 2) waitPhases = 2;  // at least wait 2 phases; this should basically eliminate the chance that we get a SIGSYS before we start executing the syscal instruction
    uint64_t wakeupPhase = zinfo->numPhases + waitPhases;

    /*volatile uint32_t* futexWord =*/ zinfo->sched->markForSleep(procIdx, tid, wakeupPhase);  // we still want to mark for sleep, bear with me...
    inFakeTimeoutMode[tid] = true;
    return true;
}
Beispiel #2
0
VOID OnSyscallEntry(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, VOID *v) 
{
    ADDRINT sysnum = PIN_GetSyscallNumber(ctxt, std);
    ADDRINT arg0 = PIN_GetSyscallArgument(ctxt, std, 0);

#ifdef TARGET_BSD
    // If this is the system call dispatcher, then use arg0 as the system call number
    if (sysnum == SYS___syscall)
    {
        sysnum = arg0;
        arg0 = PIN_GetSyscallArgument(ctxt, std, 1);
    }
#endif

    if (sysnum == SYS_open &&
        strncmp(reinterpret_cast<char *>(arg0), "does-not-exist1", sizeof("does-not-exist1")-1) == 0)
    {
        PIN_SetSyscallNumber(ctxt, std, SYS_getpid);
    }

    if (IsSigaction(sysnum) && (arg0 == SIGUSR1))
    {
        PIN_SetSyscallNumber(ctxt, std, SYS_getpid);
    }

    if (sysnum == SYS_open &&
        strncmp(reinterpret_cast<char *>(arg0), "does-not-exist2", sizeof("does-not-exist2")-1) == 0)
    {
        PIN_SetSyscallNumber(ctxt, std, SYS_exit);
        PIN_SetSyscallArgument(ctxt, std, 0, 0);
    }
}
Beispiel #3
0
uint64_t Scheduler::getFutexWakePhase(bool realtime, FutexInfo fi, CONTEXT* ctxt, SYSCALL_STANDARD std) {
    int64_t waitNsec = 0;
    uint64_t wakeupPhase = 0;
    waitNsec = fi.timeout.tv_sec*1000000000L + fi.timeout.tv_nsec;
    if (fi.op & FUTEX_CLOCK_REALTIME || realtime) {
        uint32_t domain = zinfo->procArray[procIdx]->getClockDomain();
        uint64_t simNs = cyclesToNs(zinfo->globPhaseCycles);
        uint64_t offsetNs = simNs + zinfo->clockDomainInfo[domain].realtimeOffsetNs;
        warn(" REALTIME FUTEX: %ld %ld %ld %ld", waitNsec, simNs, offsetNs, waitNsec-offsetNs);
        waitNsec = (waitNsec > (int64_t)offsetNs)? (waitNsec - offsetNs) : 0;
    }
    if (waitNsec > 0) {
        struct timespec fakeTimeouts = (struct timespec){0}; //Never timeout.
        PIN_SetSyscallArgument(ctxt, std, 3, (ADDRINT)&fakeTimeouts);
        uint64_t waitCycles = waitNsec*zinfo->freqMHz/1000;
        uint64_t waitPhases = waitCycles/zinfo->phaseLength;
        wakeupPhase = zinfo->numPhases + waitPhases;
    }
    return wakeupPhase;
}

bool Scheduler::futexSynchronized(uint32_t pid, uint32_t tid, FutexInfo fi) {
    futex_lock(&schedLock);
    uint32_t gid = getGid(pid, tid);
    ThreadInfo* th = gidMap[gid];
    futex_unlock(&schedLock);
    while (true) {
        int futex_res = syscall(SYS_futex, th->futexWord, FUTEX_WAIT, 1 /*a racing thread waking us up will change value to 0, and we won't block*/, nullptr, nullptr, 0);
        if (futex_res == 0 || th->futexWord != 1) break;
    }
    join(pid, tid);
    return true;
}
static bool PostPatchTimeoutSyscall(uint32_t tid, CONTEXT* ctxt, SYSCALL_STANDARD std, int syscall, ADDRINT prevIp, ADDRINT timeoutArgVal) {
    assert(inFakeTimeoutMode[tid]);
    int res = (int)PIN_GetSyscallNumber(ctxt, std);

    // Decide if it timed out
    bool timedOut;
    if (syscall == SYS_futex) {
        timedOut = (res == -ETIMEDOUT);
    } else {
        timedOut = (res == 0);
    }

    bool isSleeping = zinfo->sched->isSleeping(procIdx, tid);

    // Decide whether to retry
    bool retrySyscall;
    if (!timedOut) {
        if (isSleeping) zinfo->sched->notifySleepEnd(procIdx, tid);
        retrySyscall = false;
    } else {
        retrySyscall = isSleeping;
    }

    if (retrySyscall && zinfo->procArray[procIdx]->isInFastForward()) {
        warn("[%d] Fast-forwarding started, not retrying timeout syscall (%s)", tid, GetSyscallName(syscall));
        retrySyscall = false;
        assert(isSleeping);
        zinfo->sched->notifySleepEnd(procIdx, tid);
    }

    if (retrySyscall) {
        // ADDRINT curIp = PIN_GetContextReg(ctxt, REG_INST_PTR);
        //info("[%d] post-patch, retrying, IP: 0x%lx -> 0x%lx", tid, curIp, prevIp);
        PIN_SetContextReg(ctxt, REG_INST_PTR, prevIp);
        PIN_SetSyscallNumber(ctxt, std, syscall);
    } else {
        // Restore timeout arg
        PIN_SetSyscallArgument(ctxt, std, getTimeoutArg(syscall), timeoutArgVal);
        inFakeTimeoutMode[tid] = false;

        // Restore arg? I don't think we need this!
        /*if (syscall == SYS_futex) {
            PIN_SetSyscallNumber(ctxt, std, -ETIMEDOUT);
        } else {
            assert(syscall == SYS_epoll_wait || syscall == SYS_epoll_pwait || syscall == SYS_poll);
            PIN_SetSyscallNumber(ctxt, std, 0); //no events returned
        }*/
    }

    //info("[%d] post-patch %s (%d), timedOut %d, sleeping (orig) %d, retrying %d, orig res %d, patched res %d", tid, GetSyscallName(syscall), syscall, timedOut, isSleeping, retrySyscall, res, (int)PIN_GetSyscallNumber(ctxt, std));
    return retrySyscall;
}