diff --git a/libs2ecore/src/S2EExecutor.cpp b/libs2ecore/src/S2EExecutor.cpp index fc023a4d..f9e78c7a 100644 --- a/libs2ecore/src/S2EExecutor.cpp +++ b/libs2ecore/src/S2EExecutor.cpp @@ -818,10 +818,10 @@ void S2EExecutor::stateSwitchTimerCallback(void *opaque) { void S2EExecutor::validPathSearcherStateSwitchCallback(void *opaque) { S2EExecutor *c = (S2EExecutor *) opaque; - // assert(env->current_tb == nullptr); + assert(env->current_tb == nullptr); if (g_s2e_state) { - // c->doLoadBalancing(); + c->doLoadBalancing(); S2EExecutionState *nextState = c->selectNextState(g_s2e_state); if (nextState) { g_s2e_state = nextState; @@ -830,9 +830,9 @@ void S2EExecutor::validPathSearcherStateSwitchCallback(void *opaque) { return; } } + libcpu_mod_timer(c->m_stateSwitchTimer, libcpu_get_clock_ms(host_clock) + 100); } - void S2EExecutor::initializeStateSwitchTimer() { m_stateSwitchTimer = libcpu_new_timer_ms(host_clock, &stateSwitchTimerCallback, this); libcpu_mod_timer(m_stateSwitchTimer, libcpu_get_clock_ms(host_clock) + 100); @@ -1928,7 +1928,11 @@ void S2EExecutor::setupTimersHandler() { bool S2EExecutor::suspendState(S2EExecutionState *state) { if (searcher) { searcher->removeState(state, nullptr); + g_s2e->getDebugStream() << "[states' size " << getStatesCount() << "] " + << "\n"; size_t r = states.erase(state); + g_s2e->getDebugStream() << "[states' size " << getStatesCount() << "] " + << "\n"; assert(r == 1); return true; } diff --git a/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.cpp b/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.cpp index a682904e..21f4871a 100644 --- a/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.cpp +++ b/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.cpp @@ -17,6 +17,58 @@ namespace plugins { S2E_DEFINE_PLUGIN(ValidPathSearcher, "ValidPathSearcher S2E plugin", "ValidPathSearcher", "ARMFunctionMonitor"); +class ValidPathSearcherState : public PluginState { +private: + uint64_t m_stateTbNum; + std::pair m_lstFork; + std::vector m_callStk; + +public: + ValidPathSearcherState() { + } + virtual ~ValidPathSearcherState() { + } + static PluginState *factory(Plugin *, S2EExecutionState *) { + return new ValidPathSearcherState(); + } + ValidPathSearcherState *clone() const { + return new ValidPathSearcherState(*this); + } + + inline uint64_t getStateTbNum() const { + return m_stateTbNum; + } + inline void incStateTbNum() { + m_stateTbNum++; + } + + inline uint32_t getLastForkCallerPC() const { + return m_lstFork.first; + }; + inline uint32_t getLastForkForkPC() const { + return m_lstFork.second; + }; + inline void setLastFork(uint32_t callerPc, uint32_t forkPc) { + m_lstFork = {callerPc, forkPc}; + }; + + inline std::vector getCallStack() const { + return m_callStk; + } + inline uint32_t sizeCallStack() const { + return m_callStk.size(); + } + inline uint32_t topCallstack() const { + return m_callStk.back(); + } + inline void pushCallStack(uint32_t callerPC) { + m_callStk.emplace_back(callerPC); + } + inline void popCallStack() { + m_callStk.pop_back(); + } +}; + void ValidPathSearcher::initialize() { s2e()->getExecutor()->setSearcher(this); initConnection(); @@ -29,121 +81,144 @@ klee::ExecutionState &ValidPathSearcher::selectState() { getDebugStream() << "selectState " << "[current stateID: " << m_curState->getID() << "] " << "\n"; + m_isSelfSwitchedState = false; + const uint32_t tmpId = m_lstStateId; + m_lstStateId = m_curState->getID(); if (m_selfSwitch) { m_selfSwitch = false; - selectSelfSwitchedState(); - return *m_curState; - } - if (selectUnvisitedState()) { + if (m_isSelfSwitchedState) { + if (!selectLastState()) { + m_lstStateId = tmpId; + } + return *m_curState; + } + if (!selectCousinState()) { + m_lstStateId = tmpId; + return *m_curState; + } return *m_curState; } - if (selectValidState()) { + if (selectUnvisitedState() || selectValidState()) { return *m_curState; } - getDebugStream() << "no option" << "\n"; + m_lstStateId = tmpId; return *m_curState; } void ValidPathSearcher::update(klee::ExecutionState *current, const klee::StateSet &addedStates, const klee::StateSet &removedStates) { + if (!m_searcherActive) { + m_curState = dynamic_cast(current); + getDebugStream() << "[start stateID: " << m_curState->getID() << "] " + << "\n"; + m_searcherActive = true; + } + for (auto it : removedStates) { S2EExecutionState *removedState = dynamic_cast(it); auto found = m_idStateMap.find(removedState->getID()); if (found != m_idStateMap.end()) { - found->second.flag = INVALID; getDebugStream() << "[invalid stateID: " << found->second.state->getID() << "] " << "\n"; + m_idStateMap.erase(found); } } } bool ValidPathSearcher::empty() { - // for (const auto &it : m_idStateMap) { - // if (it.second.flag == UNVISITED || it.second.flag == VALID) { - // getDebugStream() << "empty" - // << "\n"; - // return false; - // } - // } return false; } void ValidPathSearcher::onStateFork(S2EExecutionState *state, const std::vector &newStates, const std::vector> &newConditions) { - getDebugStream() << "onStateFork" << '\n'; - + getDebugStream(state) << "onStateFork" << '\n'; + DECLARE_PLUGINSTATE(ValidPathSearcherState, state); + const uint64_t stateTbNum = plgState->getStateTbNum(); const uint32_t forkPC = state->regs()->getPc(); - getDebugStream() << "[forkPC: " << hexval(forkPC) << "] " - << "[stateID: " << hexval(state->getID()) << "] " - << "[new stateID: " << newStates[0]->getID() << "] " - << "[new stateID: " << newStates[1]->getID() << "] " - << "\n"; - insertToForkStates(m_callerPC, forkPC, newStates[0]); - insertToForkStates(m_callerPC, forkPC, newStates[1]); + const uint32_t id0 = newStates[0]->getID(); + const uint32_t id1 = newStates[1]->getID(); + plgState->setLastFork(m_callerPC, forkPC); + getDebugStream(state) << "[callerPC: " << hexval(m_callerPC) << "] " + << "[forkPC: " << hexval(forkPC) << "] " + << "[stateID: " << state->getID() << "] " + << "\n" + << "\t\t[new stateID: " << id0 << "] " + << "[condition: " << newConditions[0] << "] " + << "\n" + << "\t\t[new stateID: " << id1 << "] " + << "[condition: " << newConditions[1] << "] " + << "\n"; + + m_forkStates[m_callerPC][forkPC].push_back({id0, id1, plgState->sizeCallStack()}); + m_idStateMap[id0] = {newStates[0], forkPC, UNVISITED, 0, stateTbNum}; + m_idStateMap[id1] = {newStates[1], forkPC, UNVISITED, 0, stateTbNum}; m_idStateMap[state->getID()].flag = VALID; - m_searcherActive = true; + m_isSelfSwitchedState = false; } void ValidPathSearcher::onTranslateBlockEnd(ExecutionSignal *signal, S2EExecutionState *state, TranslationBlock *tb, uint64_t pc, bool isStatic, uint64_t staticTargetPc) { - // getDebugStream() << "onTranslateBlockEnd" << '\n'; - // - m_tbNum++; - // getDebugStream() << "[the number of blocks: " << m_tbNum << "]" - // << "\n"; + getDebugStream(state) << "onTranslateBlockEnd" + << "\n"; + DECLARE_PLUGINSTATE(ValidPathSearcherState, state); + plgState->incStateTbNum(); + m_idStateMap[state->getID()].tbNum = plgState->getStateTbNum(); + getDebugStream(state) << "[the number of blocks: " << plgState->getStateTbNum() << "]" + << "\n"; } void ValidPathSearcher::onARMFunctionCall(S2EExecutionState *state, uint32_t pcCaller, uint64_t pcCtxHashVal, uint32_t pcReturn) { - getDebugStream() << "onARMFunctionCall" - << "\n"; + getDebugStream(state) << "onARMFunctionCall" + << "\n"; + DECLARE_PLUGINSTATE(ValidPathSearcherState, state); m_callerPC = pcCaller; m_functionCalRetMap[pcReturn] = pcCaller; + plgState->pushCallStack(pcCaller); + for (const auto &it : plgState->getCallStack()) { + getDebugStream(state) << "[caller pc: " << hexval(it) << "] " + << "\n"; + } - getDebugStream() << "[caller pc: " << hexval(pcCaller) << "] " - << "[return pc: " << hexval(pcReturn) << "] " - << "[pc and context hash: " << hexval(pcCtxHashVal) << "] " - << "\n"; + getDebugStream(state) << "[caller pc: " << hexval(pcCaller) << "] " + << "[return pc: " << hexval(pcReturn) << "] " + << "[pc and context hash: " << hexval(pcCtxHashVal) << "] " + << "\n"; } void ValidPathSearcher::onARMFunctionReturn(S2EExecutionState *state, uint32_t pcReturn) { // for switch based on level of function. - getDebugStream() << "onARMFunctionReturn" - << "\n"; + getDebugStream(state) << "onARMFunctionReturn" + << "\n"; + DECLARE_PLUGINSTATE(ValidPathSearcherState, state); m_curState = state; m_returnPC = pcReturn; + const uint32_t callerPC = m_functionCalRetMap[m_returnPC]; + getDebugStream(state) << "[caller pc: " << hexval(callerPC) << "] " + << "[return pc: " << hexval(pcReturn) << "] " + << "\n"; if (!m_searcherActive) { + plgState->popCallStack(); return; } - // m_selfSwitch = true; - - if (m_selfSwitch) { - getDebugStream() << "[states' size " << s2e()->getExecutor()->getStatesCount() << "] " - << "\n"; - - if (g_s2e_state) { - S2EExecutionState *nextState = s2e()->getExecutor()->selectNextState(g_s2e_state); - if (nextState) { - g_s2e_state = nextState; - - } else { - // Do not reschedule the timer anymore - return; - } - } - getDebugStream() << "[states' size " << s2e()->getExecutor()->getStatesCount() << "] " - << "\n"; - - // s2e()->getExecutor()->selectSearcherState(m_curState) - // s2e()->getExecutor()->selectNextState(m_curState); - // s2e()->getExecutor()->suspendState(m_curState); - // s2e()->getExecutor()->setCpuExitRequest(); + const std::vector callStack = plgState->getCallStack(); + if (callStack.size() <= SELF_SWITCH_CALL_STACK_LVL) { + plgState->popCallStack(); + return; } + const uint32_t targetCallerPc = callStack[callStack.size() - SELF_SWITCH_CALL_STACK_LVL]; + const uint32_t lstForkCallerPc = plgState->getLastForkCallerPC(); + if (targetCallerPc == lstForkCallerPc) { + m_selfSwitch = true; + triggerSelfSwitch(); + } + plgState->popCallStack(); + return; } void ValidPathSearcher::initConnection() { @@ -157,46 +232,41 @@ void ValidPathSearcher::initConnection() { onARMFunctionConn->onARMFunctionReturnEvent.connect(sigc::mem_fun(*this, &ValidPathSearcher::onARMFunctionReturn)); } -bool ValidPathSearcher::insertToForkStates(const uint32_t callerPC, const uint32_t forkPC, S2EExecutionState *state) { - const auto &foundSameCallerPC = m_forkStateItems.find(callerPC); - if (foundSameCallerPC != m_forkStateItems.end()) { - const auto &foundSameForkPC = foundSameCallerPC->second.find(forkPC); - if (foundSameForkPC != foundSameCallerPC->second.end()) { - if (foundSameForkPC->second.size() >= 2) { - return false; - } - } - } - m_forkStateItems[callerPC][forkPC].push_back(state); - m_idStateMap[state->getID()] = {state, forkPC, UNVISITED, 0}; - return true; -} - -bool ValidPathSearcher::selectSelfSwitchedState() { - const uint32_t callerPC = m_functionCalRetMap[m_returnPC]; - const uint32_t forkPC = m_idStateMap[m_curState->getID()].forkPC; - getDebugStream() << "self switch " - << "[callerPC: " << hexval(callerPC) << "] " - << "[forkPC: " << hexval(forkPC) << "] " - << "\n"; - - for (auto &it : m_forkStateItems[callerPC][forkPC]) { - const int id = it->getID(); - if (m_curState->getID() != id && m_idStateMap[id].flag != INVALID) { - if (m_idStateMap[id].switchCnt >= MAX_SWITCH_CNT) { - m_idStateMap[id].flag = INVALID; - continue; - } - m_idStateMap[id].switchCnt++; - m_idStateMap[id].flag = VALID; - m_curState = m_idStateMap[id].state; - getDebugStream() << "[new self-switched StateID: " << m_curState->getID() << "] " +bool ValidPathSearcher::selectCousinState() { + DECLARE_PLUGINSTATE(ValidPathSearcherState, m_curState); + const uint32_t lstForkCallerPc = plgState->getLastForkCallerPC(); + const uint32_t lstForkForkPc = plgState->getLastForkForkPC(); + for (auto it = m_forkStates[lstForkCallerPc][lstForkForkPc].rbegin(); + it != m_forkStates[lstForkCallerPc][lstForkForkPc].rend(); ++it) { + if (it->id1 == m_curState->getID() && m_idStateMap[it->id2].flag == VALID) { + m_curState = m_idStateMap[it->id2].state; + getDebugStream() << "[switch cousin stateID: " << m_curState->getID() << "] " + << "\n"; + m_isSelfSwitchedState = true; + return true; + } else if (it->id2 == m_curState->getID() && m_idStateMap[it->id1].flag == VALID) { + m_curState = m_idStateMap[it->id1].state; + getDebugStream() << "[switch cousin stateID: " << m_curState->getID() << "] " << "\n"; + m_isSelfSwitchedState = true; return true; + } else { + continue; } } + getDebugStream() << "no option for select cousin state" + << "\n"; + return false; +} - getDebugStream() << "no option for self switch" +bool ValidPathSearcher::selectLastState() { + if (m_idStateMap[m_lstStateId].flag == VALID) { + m_curState = m_idStateMap[m_lstStateId].state; + getDebugStream() << "[switch last stateID: " << m_curState->getID() << "] " + << "\n"; + return true; + } + getDebugStream() << "no option for select last state" << "\n"; return false; } @@ -236,5 +306,41 @@ bool ValidPathSearcher::selectValidState() { return false; } +void ValidPathSearcher::triggerSelfSwitch() { + env->current_tb = nullptr; + s2e()->getExecutor()->validPathSearcherStateSwitchCallback(s2e()->getExecutor()); + s2e()->getExecutor()->setCpuExitRequest(); +} + +void ValidPathSearcher::printForkStates() { + getDebugStream() << "printForkStates" + << "\n"; + for (const auto &it : m_forkStates) { + getDebugStream() << "[callerPC: " << hexval(it.first) << "] " + << "\n"; + for (const auto &it2 : it.second) { + getDebugStream() << "\t[forkPC: " << hexval(it2.first) << "] " + << "\n"; + for (const auto &it3 : it2.second) { + getDebugStream() << "\t\t[stateID: " << it3.id1 << ", " << it3.id2 << "] " + << "[call stack level: " << it3.callStkLvl << "] " + << "\n"; + } + } + } +} + +void ValidPathSearcher::printStateItems() { + getDebugStream() << "printStateMap" + << "\n"; + for (const auto &it : m_idStateMap) { + getDebugStream() << "[stateID: " << it.second.state->getID() << "] " + << "[forkPC: " << hexval(it.second.forkPC) << "] " + << "[flag: " << it.second.flag << "] " + << "[switchCnt: " << it.second.switchCnt << "] " + << "\n"; + } +} + } // namespace plugins } // namespace s2e diff --git a/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.h b/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.h index 2309f310..225a4c20 100644 --- a/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.h +++ b/libs2eplugins/src/s2e/Plugins/Searchers/ValidPathSearcher.h @@ -36,10 +36,18 @@ struct StateItem { uint32_t forkPC; uint32_t flag; uint32_t switchCnt; + uint64_t tbNum; +}; + +struct ForkStateInfo { + uint32_t id1; + uint32_t id2; + uint32_t callStkLvl; }; enum StatePhase { UNVISITED, INVALID, VALID }; const int MAX_SWITCH_CNT = 20; +const int SELF_SWITCH_CALL_STACK_LVL = 2; class ValidPathSearcher : public Plugin, public klee::Searcher { S2E_PLUGIN @@ -55,23 +63,29 @@ class ValidPathSearcher : public Plugin, public klee::Searcher { sigc::connection m_onTranslateBlockEndConn; ARMFunctionMonitor *onARMFunctionConn; - uint32_t m_tbNum; uint32_t m_callerPC; uint32_t m_returnPC; + uint32_t m_lstStateId; S2EExecutionState *m_curState; bool m_selfSwitch; + bool m_isSelfSwitchedState; bool m_searcherActive; - std::map>> m_forkStateItems; + std::map>> m_forkStates; std::map m_idStateMap; std::map m_functionCalRetMap; void initConnection(); - bool insertToForkStates(const uint32_t callerPC, const uint32_t forkPC, S2EExecutionState *state); - bool selectSelfSwitchedState(); + bool selectCousinState(); + bool selectLastState(); bool selectUnvisitedState(); bool selectValidState(); + void triggerSelfSwitch(); + + void printForkStates(); + void printStateItems(); + // callbacks void onStateFork(S2EExecutionState *state, const std::vector &newStates, const std::vector> &newConditions);