CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
928 lines
31 KiB
C++
928 lines
31 KiB
C++
/*
|
|
* See the dyninst/COPYRIGHT file for copyright information.
|
|
*
|
|
* We provide the Paradyn Tools (below described as "Paradyn")
|
|
* on an AS IS basis, and do not warrant its validity or performance.
|
|
* We reserve the right to update, modify, or discontinue this
|
|
* software at any time. We shall have no obligation to supply such
|
|
* updates or modifications or any other form of support to you.
|
|
*
|
|
* By your use of Paradyn, you understand and agree that we (or any
|
|
* other person or entity with proprietary rights in Paradyn) are
|
|
* under no obligation to provide either maintenance services,
|
|
* update services, notices of latent defects, or correction of
|
|
* defects for Paradyn.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include "common/src/vgannotations.h"
|
|
#include "dyntypes.h"
|
|
#include "registers/x86_regs.h"
|
|
#include "IA_IAPI.h"
|
|
#include "util.h"
|
|
#include "Dereference.h"
|
|
#include "Immediate.h"
|
|
#include "BinaryFunction.h"
|
|
#include "debug_parse.h"
|
|
#include "IndirectAnalyzer.h"
|
|
#include "util.h"
|
|
#include "dyntypes.h"
|
|
#include "instructionAPI/h/syscalls.h"
|
|
#include "instructionAPI/h/interrupts.h"
|
|
|
|
#include <deque>
|
|
#include <map>
|
|
|
|
#include "Register.h"
|
|
#include "IA_x86.h"
|
|
#include "IA_power.h"
|
|
#include "IA_aarch64.h"
|
|
#include "IA_amdgpu.h"
|
|
|
|
using namespace Dyninst;
|
|
using namespace InstructionAPI;
|
|
using namespace Dyninst::InsnAdapter;
|
|
using namespace Dyninst::ParseAPI;
|
|
|
|
std::map<Architecture, RegisterAST::Ptr> IA_IAPI::framePtr;
|
|
std::map<Architecture, RegisterAST::Ptr> IA_IAPI::stackPtr;
|
|
std::map<Architecture, RegisterAST::Ptr> IA_IAPI::thePC;
|
|
|
|
|
|
IA_IAPI::IA_IAPI(const IA_IAPI &rhs)
|
|
: InstructionAdapter(rhs),
|
|
dec(rhs.dec),
|
|
allInsns(rhs.allInsns),
|
|
validCFT(rhs.validCFT),
|
|
cachedCFT(rhs.cachedCFT),
|
|
validLinkerStubState(rhs.validLinkerStubState),
|
|
cachedLinkerStubState(rhs.cachedLinkerStubState),
|
|
hascftstatus(rhs.hascftstatus),
|
|
tailCalls(rhs.tailCalls) {
|
|
//curInsnIter = allInsns.find(rhs.curInsnIter->first);
|
|
curInsnIter = allInsns.end()-1;
|
|
}
|
|
|
|
IA_IAPI &IA_IAPI::operator=(const IA_IAPI &rhs) {
|
|
dec = rhs.dec;
|
|
allInsns = rhs.allInsns;
|
|
//curInsnIter = allInsns.find(rhs.curInsnIter->first);
|
|
curInsnIter = allInsns.end()-1;
|
|
validCFT = rhs.validCFT;
|
|
cachedCFT = rhs.cachedCFT;
|
|
validLinkerStubState = rhs.validLinkerStubState;
|
|
cachedLinkerStubState = rhs.cachedLinkerStubState;
|
|
hascftstatus = rhs.hascftstatus;
|
|
tailCalls = rhs.tailCalls;
|
|
|
|
// InstructionAdapter members
|
|
current = rhs.current;
|
|
previous = rhs.previous;
|
|
parsedJumpTable = rhs.parsedJumpTable;
|
|
successfullyParsedJumpTable = rhs.successfullyParsedJumpTable;
|
|
isDynamicCall_ = rhs.isDynamicCall_;
|
|
checkedDynamicCall_ = rhs.checkedDynamicCall_;
|
|
isInvalidCallTarget_ = rhs.isInvalidCallTarget_;
|
|
checkedInvalidCallTarget_ = rhs.checkedInvalidCallTarget_;
|
|
_obj = rhs._obj;
|
|
_cr = rhs._cr;
|
|
_isrc = rhs._isrc;
|
|
_curBlk = rhs._curBlk;
|
|
|
|
return *this;
|
|
}
|
|
|
|
IA_IAPI* IA_IAPI::makePlatformIA_IAPI(Architecture arch,
|
|
InstructionDecoder dec_,
|
|
Address where_,
|
|
CodeObject * o,
|
|
CodeRegion * r,
|
|
InstructionSource *isrc,
|
|
Block * curBlk_) {
|
|
switch (arch) {
|
|
case Arch_x86:
|
|
case Arch_x86_64:
|
|
return new IA_x86(dec_, where_, o, r, isrc, curBlk_);
|
|
case Arch_ppc32:
|
|
case Arch_ppc64:
|
|
return new IA_power(dec_, where_, o, r, isrc, curBlk_);
|
|
case Arch_aarch64:
|
|
return new IA_aarch64(dec_, where_, o, r, isrc, curBlk_);
|
|
case Arch_amdgpu_gfx908:
|
|
case Arch_amdgpu_gfx90a:
|
|
case Arch_amdgpu_gfx940:
|
|
|
|
return new IA_amdgpu(dec_, where_, o, r, isrc, curBlk_);
|
|
|
|
default:
|
|
assert(!"unimplemented architecture");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
std::once_flag IA_IAPI::ptrInit;
|
|
|
|
void IA_IAPI::initASTs()
|
|
{
|
|
std::call_once(IA_IAPI::ptrInit, [&]{
|
|
if(framePtr.empty())
|
|
{
|
|
framePtr[Arch_x86] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_x86)));
|
|
framePtr[Arch_x86_64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_x86_64)));
|
|
framePtr[Arch_ppc32] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_ppc32)));
|
|
framePtr[Arch_ppc64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_ppc64)));
|
|
framePtr[Arch_aarch64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_aarch64)));
|
|
framePtr[Arch_amdgpu_gfx908] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_amdgpu_gfx908)));
|
|
framePtr[Arch_amdgpu_gfx90a] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_amdgpu_gfx90a)));
|
|
framePtr[Arch_amdgpu_gfx940] = RegisterAST::Ptr(new RegisterAST(MachRegister::getFramePointer(Arch_amdgpu_gfx940)));
|
|
}
|
|
if(stackPtr.empty())
|
|
{
|
|
stackPtr[Arch_x86] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_x86)));
|
|
stackPtr[Arch_x86_64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_x86_64)));
|
|
stackPtr[Arch_ppc32] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_ppc32)));
|
|
stackPtr[Arch_ppc64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_ppc64)));
|
|
stackPtr[Arch_aarch64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_aarch64)));
|
|
stackPtr[Arch_amdgpu_gfx908] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_amdgpu_gfx908)));
|
|
stackPtr[Arch_amdgpu_gfx90a] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_amdgpu_gfx90a)));
|
|
stackPtr[Arch_amdgpu_gfx940] = RegisterAST::Ptr(new RegisterAST(MachRegister::getStackPointer(Arch_amdgpu_gfx940)));
|
|
}
|
|
if(thePC.empty())
|
|
{
|
|
thePC[Arch_x86] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_x86)));
|
|
thePC[Arch_x86_64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_x86_64)));
|
|
thePC[Arch_ppc32] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_ppc32)));
|
|
thePC[Arch_ppc64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_ppc64)));
|
|
thePC[Arch_aarch64] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_aarch64)));
|
|
thePC[Arch_amdgpu_gfx908] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_amdgpu_gfx908)));
|
|
thePC[Arch_amdgpu_gfx90a] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_amdgpu_gfx90a)));
|
|
thePC[Arch_amdgpu_gfx940] = RegisterAST::Ptr(new RegisterAST(MachRegister::getPC(Arch_amdgpu_gfx940)));
|
|
}
|
|
ANNOTATE_HAPPENS_BEFORE(&IA_IAPI::ptrInit);
|
|
});
|
|
ANNOTATE_HAPPENS_AFTER(&IA_IAPI::ptrInit);
|
|
}
|
|
|
|
IA_IAPI::IA_IAPI(InstructionDecoder dec_,
|
|
Address where_,
|
|
CodeObject * o,
|
|
CodeRegion * r,
|
|
InstructionSource *isrc,
|
|
Block * curBlk_) :
|
|
InstructionAdapter(where_, o, r, isrc, curBlk_),
|
|
dec(dec_),
|
|
validCFT(false),
|
|
cachedCFT(std::make_pair(false, 0)),
|
|
validLinkerStubState(false),
|
|
cachedLinkerStubState(false)
|
|
{
|
|
hascftstatus.first = false;
|
|
tailCalls.clear();
|
|
|
|
//dyncompat::tuples::tie(curInsnIter, dyncompat::tuples::ignore) = allInsns.insert(std::make_pair(current, dec.decode()));
|
|
curInsnIter =
|
|
allInsns.insert(
|
|
allInsns.end(),
|
|
std::make_pair(current, dec.decode()));
|
|
|
|
initASTs();
|
|
}
|
|
|
|
void
|
|
IA_IAPI::reset(
|
|
InstructionDecoder dec_,
|
|
Address start,
|
|
CodeObject *o,
|
|
CodeRegion *r,
|
|
InstructionSource *isrc,
|
|
Block * curBlk_)
|
|
{
|
|
// reset the base
|
|
InstructionAdapter::reset(start,o,r,isrc,curBlk_);
|
|
|
|
dec = dec_;
|
|
validCFT = false;
|
|
cachedCFT = make_pair(false, 0);
|
|
validLinkerStubState = false;
|
|
hascftstatus.first = false;
|
|
tailCalls.clear();
|
|
|
|
allInsns.clear();
|
|
|
|
curInsnIter =
|
|
allInsns.insert(
|
|
allInsns.end(),
|
|
std::make_pair(current, dec.decode()));
|
|
|
|
initASTs();
|
|
}
|
|
|
|
|
|
void IA_IAPI::advance()
|
|
{
|
|
// if(!curInsn()) {
|
|
// parsing_printf("..... WARNING: failed to advance InstructionAdapter at 0x%lx, allInsns.size() = %d\n", current,
|
|
// allInsns.size());
|
|
// return;
|
|
// }
|
|
InstructionAdapter::advance();
|
|
current += curInsn().size();
|
|
|
|
curInsnIter =
|
|
allInsns.insert(
|
|
allInsns.end(),
|
|
std::make_pair(current, dec.decode()));
|
|
|
|
// if(!curInsn())
|
|
// {
|
|
// parsing_printf("......WARNING: after advance at 0x%lx, curInsn() NULL\n", current);
|
|
// }
|
|
validCFT = false;
|
|
validLinkerStubState = false;
|
|
hascftstatus.first = false;
|
|
tailCalls.clear();
|
|
}
|
|
|
|
bool IA_IAPI::retreat()
|
|
{
|
|
// if(!curInsn()) {
|
|
// parsing_printf("..... WARNING: failed to retreat InstructionAdapter at 0x%lx, allInsns.size() = %d\n", current,
|
|
// allInsns.size());
|
|
// return false;
|
|
// }
|
|
InstructionAdapter::retreat();
|
|
allInsns_t::iterator remove = curInsnIter;
|
|
if(curInsnIter != allInsns.begin()) {
|
|
--curInsnIter;
|
|
allInsns.erase(remove);
|
|
current = curInsnIter->first;
|
|
if(curInsnIter != allInsns.begin()) {
|
|
allInsns_t::iterator pit = curInsnIter;
|
|
--pit;
|
|
previous = curInsnIter->first;
|
|
} else {
|
|
previous = -1;
|
|
}
|
|
} else {
|
|
parsing_printf("..... WARNING: cowardly refusal to retreat past first instruction at 0x%lx\n", current);
|
|
return false;
|
|
}
|
|
|
|
/* blind duplication -- nate */
|
|
validCFT = false;
|
|
validLinkerStubState = false;
|
|
hascftstatus.first = false;
|
|
tailCalls.clear();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
size_t IA_IAPI::getSize() const
|
|
{
|
|
return curInsn().size();
|
|
}
|
|
|
|
bool IA_IAPI::hasCFT() const
|
|
{
|
|
parsing_printf("hasCFT called\n");
|
|
if(hascftstatus.first) {
|
|
parsing_printf("\t Returning cached entry: %d\n",hascftstatus.second);
|
|
return hascftstatus.second;
|
|
}
|
|
InsnCategory c = curInsn().getCategory();
|
|
hascftstatus.second = false;
|
|
if(c == c_BranchInsn ||
|
|
c == c_ReturnInsn) {
|
|
if ( likely ( ! (_obj->defensiveMode() && isNopJump()) ) ) {
|
|
parsing_printf("\t branch or return, ret true\n");
|
|
hascftstatus.second = true;
|
|
}
|
|
}
|
|
else if(c == c_CallInsn) {
|
|
if(isRealCall()) {
|
|
hascftstatus.second = true;
|
|
}
|
|
else if(isDynamicCall()) {
|
|
hascftstatus.second = true;
|
|
}
|
|
else if(simulateJump()) {
|
|
hascftstatus.second = true;
|
|
}
|
|
}
|
|
else if(c == c_SysEnterInsn)
|
|
{
|
|
hascftstatus.second = true;
|
|
}
|
|
else if (c == c_SyscallInsn)
|
|
{
|
|
hascftstatus.second = true;
|
|
}
|
|
|
|
hascftstatus.first = true;
|
|
return hascftstatus.second;
|
|
}
|
|
|
|
bool IA_IAPI::isAbort() const
|
|
{
|
|
entryID e = curInsn().getOperation().getID();
|
|
return e == e_int3 ||
|
|
e == e_hlt ||
|
|
e == e_ud2;
|
|
}
|
|
|
|
bool IA_IAPI::isInvalidInsn() const
|
|
{
|
|
entryID e = curInsn().getOperation().getID();
|
|
if(e == e_No_Entry)
|
|
{
|
|
parsing_printf("...WARNING: un-decoded instruction at 0x%lx\n", current);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* This function determines if a given instruction is weird enough that we've
|
|
* probably veered into non-code bytes and are parsing garbage.
|
|
* note: yes, some of the code in here is does low-level things like
|
|
* grab instruction bytes directly instead of relying on the parseAPI,
|
|
* but since this code executes for every parsed instruction, it needs
|
|
* to be efficient.
|
|
*/
|
|
bool IA_IAPI::isGarbageInsn() const
|
|
{
|
|
bool ret = false;
|
|
// GARBAGE PARSING HEURISTIC
|
|
if (unlikely(_obj->defensiveMode())) {
|
|
entryID e = curInsn().getOperation().getID();
|
|
switch (e) {
|
|
case e_arpl:
|
|
cerr << "REACHED AN ARPL AT "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
break;
|
|
case e_fisub:
|
|
cerr << "REACHED A FISUB AT "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
break;
|
|
case e_into:
|
|
cerr << "REACHED AN INTO AT "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
break;
|
|
case e_mov: {
|
|
set<RegisterAST::Ptr> regs;
|
|
curInsn().getWriteSet(regs);
|
|
for (set<RegisterAST::Ptr>::iterator rit = regs.begin();
|
|
rit != regs.end(); rit++)
|
|
{
|
|
if (Dyninst::isSegmentRegister((*rit)->getID().regClass())) {
|
|
cerr << "REACHED A MOV SEGMENT INSN AT "<< std::hex
|
|
<< current << std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case e_add:
|
|
if (2 == curInsn().size() &&
|
|
0 == curInsn().rawByte(0) &&
|
|
0 == curInsn().rawByte(1))
|
|
{
|
|
cerr << "REACHED A 0x0000 INSTRUCTION "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
}
|
|
break;
|
|
case e_push: // pushes of segment registers do not occur frequently in real code (and crash Rose)
|
|
#if 0 // instructionAPI implementation
|
|
set<RegisterAST::Ptr> regs;
|
|
curInsn()->getWriteSet(regs);
|
|
for (set<RegisterAST::Ptr>::iterator rit = regs.begin();
|
|
rit != regs.end(); rit++)
|
|
{
|
|
if (Dyninst::isSegmentRegister((*rit)->getID().regClass())) {
|
|
cerr << "REACHED A PUSH OF A SEGMENT REGISTER AT "<< std::hex
|
|
<< current << std::dec <<" COUNTING AS INVALID" << endl;
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
#else // faster raw-byte implementation
|
|
switch (curInsn().rawByte(0)) {
|
|
case 0x06:
|
|
case 0x0e:
|
|
case 0x16:
|
|
case 0x1e:
|
|
ret = true;
|
|
cerr << "REACHED A PUSH OF A SEGMENT REGISTER "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
break;
|
|
case 0x0f:
|
|
if (2 == curInsn().size() &&
|
|
((0xa0 == curInsn().rawByte(1)) || (0xa8 == curInsn().rawByte(1))))
|
|
{
|
|
ret = true;
|
|
cerr << "REACHED A 2-BYTE PUSH OF A SEGMENT REGISTER "<< std::hex << current
|
|
<< std::dec <<" COUNTING AS INVALID" << endl;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
bool IA_IAPI::isFrameSetupInsn() const
|
|
{
|
|
return isFrameSetupInsn(curInsn());
|
|
}
|
|
|
|
bool IA_IAPI::isDynamicCall() const
|
|
{
|
|
Instruction ci = curInsn();
|
|
if(ci.isValid() && (ci.getCategory() == c_CallInsn))
|
|
{
|
|
Address addr;
|
|
bool success;
|
|
std::tie(success, addr) = getCFT();
|
|
if (!success) {
|
|
parsing_printf("... Call 0x%lx is indirect\n", current);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IA_IAPI::isAbsoluteCall() const
|
|
{
|
|
Instruction ci = curInsn();
|
|
if(ci.getCategory() == c_CallInsn)
|
|
{
|
|
Expression::Ptr cft = ci.getControlFlowTarget();
|
|
if(cft && dyncompat::dynamic_pointer_cast<Immediate>(cft))
|
|
{
|
|
return true;
|
|
}
|
|
if (isDynamicCall()) {
|
|
return true; // indirect call targets are absolute
|
|
// (though unknown for now)
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IA_IAPI::isBranch() const
|
|
{
|
|
return curInsn().getCategory() == c_BranchInsn;
|
|
}
|
|
bool IA_IAPI::isCall() const
|
|
{
|
|
return curInsn().getCategory() == c_CallInsn;
|
|
}
|
|
|
|
bool IA_IAPI::isInterruptOrSyscall() const
|
|
{
|
|
return (Dyninst::InstructionAPI::isSoftwareInterrupt(curInsn()) || Dyninst::InstructionAPI::isSystemCall(curInsn()));
|
|
}
|
|
|
|
bool IA_IAPI::isSysEnter() const
|
|
{
|
|
Instruction ci = curInsn();
|
|
return (ci.getOperation().getID() == e_sysenter);
|
|
}
|
|
|
|
bool IA_IAPI::isIndirectJump() const {
|
|
Instruction ci = curInsn();
|
|
if(ci.getCategory() != c_BranchInsn) return false;
|
|
if(ci.allowsFallThrough()) return false;
|
|
bool valid;
|
|
Address target;
|
|
std::tie(valid, target) = getCFT();
|
|
if (valid) return false;
|
|
parsing_printf("... indirect jump at 0x%lx, delay parsing it\n", current);
|
|
return true;
|
|
}
|
|
|
|
void IA_IAPI::parseSyscall(std::vector<std::pair<Address, EdgeTypeEnum> >& outEdges) const
|
|
{
|
|
parsing_printf("[%s:%d] Treating syscall as call to sink w/ possible FT edge to next insn at 0x%lx\n",
|
|
FILE__, __LINE__, getAddr());
|
|
outEdges.push_back(std::make_pair((Address)-1,CALL));
|
|
outEdges.push_back(std::make_pair(getNextAddr(), CALL_FT));
|
|
}
|
|
|
|
void IA_IAPI::parseSysEnter(std::vector<std::pair<Address, EdgeTypeEnum> >& outEdges) const
|
|
{
|
|
IA_IAPI* scratch = this->clone();
|
|
|
|
do {
|
|
scratch->advance();
|
|
} while(scratch->isNop());
|
|
if(scratch->curInsn().getCategory() == c_BranchInsn)
|
|
{
|
|
parsing_printf("[%s:%d] Detected Linux-ish sysenter idiom at 0x%lx\n",
|
|
FILE__, __LINE__, getAddr());
|
|
outEdges.push_back(std::make_pair(scratch->getAddr(), COND_NOT_TAKEN));
|
|
scratch->advance();
|
|
outEdges.push_back(std::make_pair(scratch->getAddr(), CALL_FT));
|
|
}
|
|
else
|
|
{
|
|
parsing_printf("[%s:%d] Treating sysenter as call to kernel w/normal return to next insn at 0x%lx\n",
|
|
FILE__, __LINE__, getAddr());
|
|
outEdges.push_back(std::make_pair(getNextAddr(), CALL_FT));
|
|
}
|
|
delete scratch;
|
|
}
|
|
|
|
bool DEBUGGABLE(void) { return true; }
|
|
|
|
void IA_IAPI::getNewEdges(std::vector<std::pair< Address, EdgeTypeEnum> >& outEdges,
|
|
Function* context,
|
|
Block* currBlk,
|
|
unsigned int num_insns,
|
|
dyn_hash_map<Address, std::string> *plt_entries,
|
|
const set<Address>& knownTargets) const
|
|
{
|
|
Instruction ci = curInsn();
|
|
|
|
// Only call this on control flow instructions!
|
|
if(ci.getCategory() == c_CallInsn)
|
|
{
|
|
bool success;
|
|
Address target;
|
|
std::tie(success, target) = getCFT();
|
|
bool callEdge = true;
|
|
bool ftEdge = true;
|
|
if( success && !isDynamicCall() )
|
|
{
|
|
if ( ! isRealCall() )
|
|
callEdge = false;
|
|
|
|
if ( simulateJump() )
|
|
{
|
|
outEdges.push_back(std::make_pair(target, DIRECT));
|
|
callEdge = false;
|
|
ftEdge = false;
|
|
}
|
|
}
|
|
|
|
if ( unlikely(_obj->defensiveMode()) )
|
|
{
|
|
if (!success || isDynamicCall())
|
|
{
|
|
std::string empty;
|
|
if ( ! isIATcall(empty) )
|
|
ftEdge = false;
|
|
}
|
|
else if ( ! _isrc->isValidAddress(target) )
|
|
{
|
|
ftEdge = false;
|
|
}
|
|
}
|
|
|
|
if (callEdge) {
|
|
if (success) {
|
|
outEdges.push_back(std::make_pair(target, CALL));
|
|
} else {
|
|
// Cannot use address 0 to represent indirect call,
|
|
// because in .a files, address 0 can be legit function.
|
|
outEdges.push_back(std::make_pair(std::numeric_limits<Address>::max(), CALL));
|
|
}
|
|
}
|
|
|
|
if (ftEdge)
|
|
outEdges.push_back(std::make_pair(getAddr() + getSize(), CALL_FT));
|
|
|
|
// Any call site could be in try blocks
|
|
set<Address> catchStarts;
|
|
if (_obj->cs()->findCatchBlockByTryRange(getAddr(), catchStarts)) {
|
|
for (auto ait = catchStarts.begin(); ait != catchStarts.end(); ++ait) {
|
|
outEdges.push_back(std::make_pair(*ait, CATCH));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if(ci.getCategory() == c_BranchInsn)
|
|
{
|
|
if(ci.allowsFallThrough())
|
|
{
|
|
outEdges.push_back(std::make_pair(getCFT().second,
|
|
COND_TAKEN));
|
|
outEdges.push_back(std::make_pair(getNextAddr(), COND_NOT_TAKEN));
|
|
return;
|
|
}
|
|
|
|
bool valid;
|
|
Address target;
|
|
std::tie(valid, target) = getCFT();
|
|
// Direct jump
|
|
if (valid)
|
|
{
|
|
|
|
if(!isTailCall(context, DIRECT, num_insns, knownTargets))
|
|
{
|
|
if(plt_entries->find(target) == plt_entries->end())
|
|
{
|
|
outEdges.push_back(std::make_pair(target,DIRECT));
|
|
}
|
|
else
|
|
{
|
|
parsing_printf("%s[%d]: PLT tail call to %lx (%s)\n",
|
|
FILE__, __LINE__, target,
|
|
(*plt_entries)[target].c_str());
|
|
outEdges.push_back(std::make_pair(target, DIRECT));
|
|
tailCalls[DIRECT] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parsing_printf("%s[%d]: tail call to %lx\n",
|
|
FILE__, __LINE__, target);
|
|
outEdges.push_back(std::make_pair(target, DIRECT));
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
parsing_printf("... indirect jump at 0x%lx\n", current);
|
|
if( num_insns == 2 ) {
|
|
// Handle a pernicious indirect tail call idiom here
|
|
// What we've seen is mov (%rdi), %rax; jmp *%rax
|
|
// Anything that tries to go enum->jump table *should* need more than two
|
|
// instructions and has not been seen in the wild....
|
|
if(currBlk == context->entry())
|
|
{
|
|
parsing_printf("\tIndirect branch as 2nd insn of entry block, treating as tail call\n");
|
|
parsing_printf("%s[%d]: indirect tail call %s at 0x%lx\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current);
|
|
outEdges.push_back(std::make_pair((Address)-1,INDIRECT));
|
|
tailCalls[INDIRECT]=true;
|
|
// Fix the cache, because we're dumb.
|
|
tailCalls[DIRECT]=true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
parsing_printf("... uninstrumentable due to 0 size\n");
|
|
return;
|
|
}
|
|
}
|
|
if(isTailCall(context, INDIRECT, num_insns, knownTargets)) {
|
|
parsing_printf("%s[%d]: indirect tail call %s at 0x%lx\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current);
|
|
outEdges.push_back(std::make_pair((Address)-1,INDIRECT));
|
|
tailCalls[INDIRECT] = true;
|
|
return;
|
|
}
|
|
parsing_printf("%s[%d]: jump table candidate %s at 0x%lx\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current);
|
|
parsedJumpTable = true;
|
|
|
|
successfullyParsedJumpTable = parseJumpTable(context, currBlk, outEdges);
|
|
|
|
parsing_printf("Parsed jump table\n");
|
|
if(!successfullyParsedJumpTable || outEdges.empty()) {
|
|
outEdges.push_back(std::make_pair((Address)-1,INDIRECT));
|
|
parsing_printf("%s[%d]: unparsed jump table %s at 0x%lx in function %s UNINSTRUMENTABLE\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current, context->name().c_str());
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else if(ci.getCategory() == c_ReturnInsn)
|
|
{
|
|
parsing_printf("%s[%d]: return candidate %s at 0x%lx\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current);
|
|
if(ci.allowsFallThrough())
|
|
{
|
|
outEdges.push_back(std::make_pair(getNextAddr(), FALLTHROUGH));
|
|
}
|
|
else if (!isReturn(context, currBlk)) {
|
|
// If BLR is not a return, then it is a jump table
|
|
parsedJumpTable = true;
|
|
parsing_printf("%s[%d]: BLR jump table candidate %s at 0x%lx\n", FILE__, __LINE__,
|
|
ci.format().c_str(), current);
|
|
successfullyParsedJumpTable = parseJumpTable(context, currBlk, outEdges);
|
|
parsing_printf("Parsed BLR jump table\n");
|
|
if(!successfullyParsedJumpTable || outEdges.empty()) {
|
|
parsing_printf("%s[%d]: BLR unparsed jump table %s at 0x%lx in function %s UNINSTRUMENTABLE\n",
|
|
FILE__, __LINE__, ci.format().c_str(), current, context->name().c_str());
|
|
outEdges.push_back(std::make_pair((Address)-1,INDIRECT));
|
|
}
|
|
}
|
|
|
|
parsing_printf("Returning from parse out edges\n");
|
|
return;
|
|
}
|
|
else if(isSysEnter())
|
|
{
|
|
parseSysEnter(outEdges);
|
|
return;
|
|
} else if (DEBUGGABLE() && Dyninst::InstructionAPI::isSystemCall(curInsn())) {
|
|
parseSyscall(outEdges);
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "Unhandled instruction %s\n", ci.format().c_str());
|
|
assert(0);
|
|
}
|
|
|
|
bool IA_IAPI::isIPRelativeBranch() const
|
|
{
|
|
// These don't exist on IA32...
|
|
#if !defined(arch_x86_64)
|
|
return false;
|
|
#endif
|
|
Instruction ci = curInsn();
|
|
|
|
bool valid;
|
|
Address target;
|
|
std::tie(valid, target) = getCFT();
|
|
|
|
if(ci.getCategory() == c_BranchInsn &&
|
|
!valid) {
|
|
Expression::Ptr cft = ci.getControlFlowTarget();
|
|
if(cft->isUsed(thePC[_isrc->getArch()]))
|
|
{
|
|
parsing_printf("\tIP-relative indirect jump to %s at 0x%lx\n",
|
|
cft->format(ci.getArch()).c_str(), current);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
const Instruction & IA_IAPI::curInsn() const
|
|
{
|
|
return curInsnIter->second;
|
|
}
|
|
|
|
bool IA_IAPI::isLeave() const
|
|
{
|
|
Instruction ci = curInsn();
|
|
return ci.isValid() && (ci.getOperation().getID() == e_leave);
|
|
}
|
|
|
|
bool IA_IAPI::isDelaySlot() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const Instruction& IA_IAPI::getInstruction() const
|
|
{
|
|
return curInsn();
|
|
}
|
|
|
|
bool IA_IAPI::isRealCall() const
|
|
{
|
|
// Obviated by simulateJump
|
|
bool success;
|
|
Address addr;
|
|
std::tie(success, addr) = getCFT();
|
|
if (success &&
|
|
(addr == getNextAddr())) {
|
|
parsing_printf("... getting PC\n");
|
|
return false;
|
|
}
|
|
if(isThunk()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::map<Address, bool> IA_IAPI::thunkAtTarget;
|
|
|
|
|
|
bool IA_IAPI::isConditional() const
|
|
{
|
|
return curInsn().allowsFallThrough();
|
|
}
|
|
|
|
bool IA_IAPI::simulateJump() const
|
|
{
|
|
// obfuscated programs simulate jumps by calling into a block that
|
|
// discards the return address from the stack, we check for these
|
|
// fake calls in malware mode
|
|
if (_obj->defensiveMode() && !isDynamicCall()) {
|
|
return isFakeCall();
|
|
}
|
|
// TODO: we don't simulate jumps on x86 architectures; add logic as we need it.
|
|
return false;
|
|
}
|
|
|
|
std::pair<bool, Address> IA_IAPI::getFallthrough() const
|
|
{
|
|
return make_pair(true, curInsnIter->first + curInsnIter->second.size());
|
|
}
|
|
|
|
std::pair<bool, Address> IA_IAPI::getCFT() const
|
|
{
|
|
if(validCFT) return cachedCFT;
|
|
Expression::Ptr callTarget = curInsn().getControlFlowTarget();
|
|
if (!callTarget) return make_pair(false, 0);
|
|
// FIXME: templated bind(),dammit!
|
|
callTarget->bind(thePC[_isrc->getArch()].get(), Result(s64, current));
|
|
parsing_printf("%s[%d]: binding PC %s in %s to 0x%lx...", FILE__, __LINE__,
|
|
thePC[_isrc->getArch()]->format(curInsn().getArch()).c_str(), curInsn().format().c_str(), current);
|
|
|
|
Result actualTarget = callTarget->eval();
|
|
|
|
if(actualTarget.defined)
|
|
{
|
|
cachedCFT = std::make_pair(true, actualTarget.convert<Address>());
|
|
parsing_printf("SUCCESS (CFT=0x%lx)\n", cachedCFT.second);
|
|
}
|
|
else
|
|
{
|
|
cachedCFT = std::make_pair(false, 0);
|
|
parsing_printf("FAIL (CFT=0x%lx), callTarget exp: %s\n",
|
|
cachedCFT.second,callTarget->format(curInsn().getArch()).c_str());
|
|
}
|
|
validCFT = true;
|
|
|
|
if(isLinkerStub()) {
|
|
parsing_printf("Linker stub detected: Correcting CFT. (CFT=0x%lx)\n",
|
|
cachedCFT.second);
|
|
}
|
|
|
|
return cachedCFT;
|
|
}
|
|
|
|
bool IA_IAPI::isRelocatable(InstrumentableLevel lvl) const
|
|
{
|
|
Instruction ci = curInsn();
|
|
if(ci.isValid() && (ci.getCategory() == c_CallInsn))
|
|
{
|
|
if(!isDynamicCall())
|
|
{
|
|
bool valid; Address addr;
|
|
std::tie(valid, addr) = getCFT();
|
|
assert(valid);
|
|
if(!_isrc->isValidAddress(addr))
|
|
{
|
|
parsing_printf("... Call to 0x%lx is invalid (outside code or data)\n",
|
|
addr);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if(lvl == HAS_BR_INDIR)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool IA_IAPI::parseJumpTable(Dyninst::ParseAPI::Function * currFunc,
|
|
Dyninst::ParseAPI::Block* currBlk,
|
|
std::vector<std::pair< Address, Dyninst::ParseAPI::EdgeTypeEnum > >& outEdges) const
|
|
{
|
|
|
|
IndirectControlFlowAnalyzer icfa(currFunc, currBlk);
|
|
bool ret = icfa.NewJumpTableAnalysis(outEdges);
|
|
|
|
parsing_printf("Jump table parser returned %d, %lu edges\n", ret, outEdges.size());
|
|
for (auto oit = outEdges.begin(); oit != outEdges.end(); ++oit) parsing_printf("edge target at %lx\n", oit->first);
|
|
// Update statistics
|
|
currBlk->obj()->cs()->incrementCounter(PARSE_JUMPTABLE_COUNT);
|
|
if (!ret) currBlk->obj()->cs()->incrementCounter(PARSE_JUMPTABLE_FAIL);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
InstrumentableLevel IA_IAPI::getInstLevel(Function * context, unsigned int num_insns) const
|
|
{
|
|
InstrumentableLevel ret = InstructionAdapter::getInstLevel(context, num_insns);
|
|
/* if(ret == HAS_BR_INDIR && isIPRelativeBranch())
|
|
{
|
|
return NORMAL;
|
|
}*/
|
|
return ret;
|
|
}
|