Making a couple more analyses STORE-based INDIRECT aware

This commit is contained in:
caheckman 2019-07-01 10:15:27 -04:00
parent 29f207400d
commit e8fb8b3a19
4 changed files with 284 additions and 104 deletions

View file

@ -635,7 +635,7 @@ bool Funcdata::replaceVolatile(Varnode *vn)
return true; return true;
} }
/// \brief Check if the given Varnode only flows into INDIRECT ops /// \brief Check if the given Varnode only flows into call-based INDIRECT ops
/// ///
/// Flow is only followed through MULTIEQUAL ops. /// Flow is only followed through MULTIEQUAL ops.
/// \param vn is the given Varnode /// \param vn is the given Varnode
@ -654,8 +654,17 @@ bool Funcdata::checkIndirectUse(Varnode *vn)
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter; PcodeOp *op = *iter;
OpCode opc = op->code(); OpCode opc = op->code();
if (opc == CPUI_INDIRECT) continue; if (opc == CPUI_INDIRECT) {
if (opc == CPUI_MULTIEQUAL) { if (op->isIndirectStore()) {
// INDIRECT from a STORE is not a negative result but continue to follow data-flow
Varnode *outvn = op->getOut();
if (!outvn->isMark()) {
vlist.push_back(outvn);
outvn->setMark();
}
}
}
else if (opc == CPUI_MULTIEQUAL) {
Varnode *outvn = op->getOut(); Varnode *outvn = op->getOut();
if (!outvn->isMark()) { if (!outvn->isMark()) {
vlist.push_back(outvn); vlist.push_back(outvn);
@ -1294,11 +1303,12 @@ bool Funcdata::onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamT
/// \brief Test if the given trial Varnode is likely only used for parameter passing /// \brief Test if the given trial Varnode is likely only used for parameter passing
/// ///
/// Flow is followed from the Varnode itself and from ancestors the Varnode was copied from /// Flow is followed from the Varnode itself and from ancestors the Varnode was copied from
/// to see if it hits anything other than the CALL operation. /// to see if it hits anything other than the given CALL or RETURN operation.
/// \param maxlevel is the maximum number of times to recurse through ancestor copies /// \param maxlevel is the maximum number of times to recurse through ancestor copies
/// \param invn is the given trial Varnode to test /// \param invn is the given trial Varnode to test
/// \param op is the given CALL or RETURN
/// \param trial is the associated parameter trial object /// \param trial is the associated parameter trial object
/// \return \b true if the Varnode is only used for the CALL /// \return \b true if the Varnode is only used for the CALL/RETURN
bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn, bool Funcdata::ancestorOpUse(int4 maxlevel,const Varnode *invn,
const PcodeOp *op,ParamTrial &trial) const const PcodeOp *op,ParamTrial &trial) const

View file

@ -2432,7 +2432,7 @@ void ValueSetSolver::establishValueSets(const vector<Varnode *> &sinks,const vec
PcodeOp *op = vn->getDef(); PcodeOp *op = vn->getDef();
switch(op->code()) { // Distinguish ops where we can never predict an integer range switch(op->code()) { // Distinguish ops where we can never predict an integer range
case CPUI_INDIRECT: case CPUI_INDIRECT:
if (indirectAsCopy) { if (indirectAsCopy || op->isIndirectStore()) {
Varnode *inVn = op->getIn(0); Varnode *inVn = op->getIn(0);
if (!inVn->isMark()) { if (!inVn->isMark()) {
newValueSet(inVn,0); newValueSet(inVn,0);

View file

@ -15,9 +15,14 @@
*/ */
#include "subflow.hh" #include "subflow.hh"
/// \brief Return \e slot of constant if INT_OR op sets all bits in mask, otherwise -1
///
/// \param orop is the given CPUI_INT_OR op
/// \param mask is the given mask
/// \return constant slot or -1
int4 SubvariableFlow::doesOrSet(PcodeOp *orop,uintb mask) int4 SubvariableFlow::doesOrSet(PcodeOp *orop,uintb mask)
{ // Return index of constant if OR op sets bits in mask, otherwise -1 {
int4 index = (orop->getIn(1)->isConstant() ? 1 : 0); int4 index = (orop->getIn(1)->isConstant() ? 1 : 0);
if (!orop->getIn(index)->isConstant()) if (!orop->getIn(index)->isConstant())
return -1; return -1;
@ -27,9 +32,14 @@ int4 SubvariableFlow::doesOrSet(PcodeOp *orop,uintb mask)
return -1; return -1;
} }
/// \brief Return \e slot of constant if INT_AND op clears all bits in mask, otherwise -1
///
/// \param andop is the given CPUI_INT_AND op
/// \param mask is the given mask
/// \return constant slot or -1
int4 SubvariableFlow::doesAndClear(PcodeOp *andop,uintb mask) int4 SubvariableFlow::doesAndClear(PcodeOp *andop,uintb mask)
{ // Return index of constant if AND op clears bits in mask, otherwise -1 {
int4 index = (andop->getIn(1)->isConstant() ? 1 : 0); int4 index = (andop->getIn(1)->isConstant() ? 1 : 0);
if (!andop->getIn(index)->isConstant()) if (!andop->getIn(index)->isConstant())
return -1; return -1;
@ -39,9 +49,20 @@ int4 SubvariableFlow::doesAndClear(PcodeOp *andop,uintb mask)
return -1; return -1;
} }
/// \brief Add the given Varnode as a new node in the logical subgraph
///
/// A new ReplaceVarnode object is created, representing the given Varnode within
/// the logical subgraph, and returned. If an object representing the Varnode already
/// exists it is returned. A mask describing the subset of bits within the Varnode
/// representing the logical value is also passed in. This method also determines if
/// the new node needs to be added to the worklist for continued tracing.
/// \param vn is the given Varnode holding the logical value
/// \param mask is the given mask describing the bits of the logical value
/// \param inworklist will hold \b true if the new node should be traced further
/// \return the new subgraph variable node
SubvariableFlow::ReplaceVarnode *SubvariableFlow::setReplacement(Varnode *vn,uintb mask,bool &inworklist) SubvariableFlow::ReplaceVarnode *SubvariableFlow::setReplacement(Varnode *vn,uintb mask,bool &inworklist)
{ // Mark {
ReplaceVarnode *res; ReplaceVarnode *res;
if (vn->isMark()) { // Already seen before if (vn->isMark()) { // Already seen before
map<Varnode *,ReplaceVarnode>::iterator iter; map<Varnode *,ReplaceVarnode>::iterator iter;
@ -126,9 +147,15 @@ SubvariableFlow::ReplaceVarnode *SubvariableFlow::setReplacement(Varnode *vn,uin
return res; return res;
} }
/// \brief Create a logical subgraph operator node given its output variable node
///
/// \param opc is the opcode of the new logical operator
/// \param numparam is the number of parameters in the new operator
/// \param outrvn is the given output variable node
/// \return the new logical subgraph operator object
SubvariableFlow::ReplaceOp *SubvariableFlow::createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn) SubvariableFlow::ReplaceOp *SubvariableFlow::createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn)
{ // Create record for replacement op, given its replacement varnode output {
if (outrvn->def != (ReplaceOp *)0) if (outrvn->def != (ReplaceOp *)0)
return outrvn->def; return outrvn->def;
oplist.push_back(ReplaceOp()); oplist.push_back(ReplaceOp());
@ -142,9 +169,17 @@ SubvariableFlow::ReplaceOp *SubvariableFlow::createOp(OpCode opc,int4 numparam,R
return rop; return rop;
} }
/// \brief Create a logical subraph operator node given one of its input variable nodes
///
/// \param opc is the opcode of the new logical operator
/// \param numparam is the number of parameters in the new operator
/// \param inrvn is the given input variable node
/// \param slot is the input slot of the variable node
/// \return the new logical subgraph operator objects
SubvariableFlow::ReplaceOp *SubvariableFlow::createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot) SubvariableFlow::ReplaceOp *SubvariableFlow::createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot)
{ // Create record for replacement op, given one of its input replacement varnodes {
oplist.push_back(ReplaceOp()); oplist.push_back(ReplaceOp());
ReplaceOp *rop = &oplist.back(); ReplaceOp *rop = &oplist.back();
rop->op = op; rop->op = op;
@ -157,9 +192,17 @@ SubvariableFlow::ReplaceOp *SubvariableFlow::createOpDown(OpCode opc,int4 numpar
return rop; return rop;
} }
/// \brief Convert a new INDIRECT op into the logically trimmed variant of the given original PcodeOp
///
/// This method assumes the original op was an \e indirect \e creation. The input and output
/// Varnode for the new INDIRECT are not provided by this method. It only provides the
/// \e indirect \e effect input and patches up any active parameter recovery process.
/// \param newop is the new INDIRECT op to convert
/// \param oldop is the original INDIRECT op
/// \param out is the subgraph output variable node of the new INDIRECT
void SubvariableFlow::patchIndirect(PcodeOp *newop,PcodeOp *oldop, ReplaceVarnode *out) void SubvariableFlow::patchIndirect(PcodeOp *newop,PcodeOp *oldop, ReplaceVarnode *out)
{ // Finish converting -newop- into the logically trimmed variant of -oldop- {
PcodeOp *indop = PcodeOp::getOpFromConst(oldop->getIn(1)->getAddr()); PcodeOp *indop = PcodeOp::getOpFromConst(oldop->getIn(1)->getAddr());
bool possibleout = !oldop->getIn(0)->isIndirectZero(); bool possibleout = !oldop->getIn(0)->isIndirectZero();
Varnode *outvn = getReplaceVarnode(out); Varnode *outvn = getReplaceVarnode(out);
@ -176,9 +219,18 @@ void SubvariableFlow::patchIndirect(PcodeOp *newop,PcodeOp *oldop, ReplaceVarnod
} }
} }
/// \brief Determine if the given subgraph variable can act as a parameter to the given CALL op
///
/// We assume the variable flows as a parameter to the CALL. If the CALL doesn't lock the parameter
/// size, create a PatchRecord within the subgraph that allows the CALL to take the parameter
/// with its smaller logical size.
/// \param op is the given CALL op
/// \param rvn is the given subgraph variable acting as a parameter
/// \param slot is the input slot of the variable within the CALL
/// \return \b true if the parameter can be successfully trimmed to its logical size
bool SubvariableFlow::tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot) bool SubvariableFlow::tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
{ // -rvn- is flowing as parameter to the call -op-, determine if we can still trim the varnode to its logical size {
if (slot == 0) return false; if (slot == 0) return false;
FuncCallSpecs *fc = fd->getCallSpecs(op); FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0) return false; if (fc == (FuncCallSpecs *)0) return false;
@ -194,9 +246,17 @@ bool SubvariableFlow::tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
return true; return true;
} }
/// \brief Determine if the given subgraph variable can act as return value for the given RETURN op
///
/// We assume the variable flows the RETURN. If the return value size is not locked. Create a
/// PatchRecord within the subgraph that allows the RETURN to take a smaller logical value.
/// \param op is the given RETURN op
/// \param rvn is the given subgraph variable flowing to the RETURN
/// \param slot is the input slot of the subgraph variable
/// \return \b true if the return value can be successfully trimmed to its logical size
bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot) bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
{ // -rvn- flows to CPUI_RETURN (is probably being returned by function), determine if we can trim varnode to logical size {
if (slot == 0) return false; // Don't deal with actual return address container if (slot == 0) return false; // Don't deal with actual return address container
if (fd->getFuncProto().isOutputLocked()) return false; if (fd->getFuncProto().isOutputLocked()) return false;
@ -229,9 +289,16 @@ bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
return true; return true;
} }
/// \brief Determine if the given subgraph variable can act as a \e created value for the given INDIRECT op
///
/// Check if the INDIRECT is an \e indirect \e creation and is not representing a locked return value.
/// If we can, create the INDIRECT node in the subgraph representing the logical \e indirect \e creation.
/// \param op is the given INDIRECT
/// \param rvn is the given subgraph variable acting as the output of the INDIRECT
/// \return \b true if we can successfully trim the value to its logical size
bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn) bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn)
{ // -rvn- is defined by a CALL op, check if the call is actively recovering the return value and if we can trim {
if (!op->isIndirectCreation()) return false; if (!op->isIndirectCreation()) return false;
PcodeOp *indop = PcodeOp::getOpFromConst(op->getIn(1)->getAddr()); PcodeOp *indop = PcodeOp::getOpFromConst(op->getIn(1)->getAddr());
FuncCallSpecs *fc = fd->getCallSpecs(indop); FuncCallSpecs *fc = fd->getCallSpecs(indop);
@ -250,11 +317,13 @@ bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn)
return true; return true;
} }
/// Try to trace the logical variable through descendant Varnodes
/// creating new nodes in the logical subgraph and updating the worklist.
/// \param rvn is the given subgraph variable to trace
/// \return \b true if the logical value can be traced forward one level
bool SubvariableFlow::traceForward(ReplaceVarnode *rvn) bool SubvariableFlow::traceForward(ReplaceVarnode *rvn)
{ // Try to trace logical variable through descendant varnodes {
// updating list/map of replace_ops and replace_varnodes
// and the worklist
ReplaceOp *rop; ReplaceOp *rop;
PcodeOp *op; PcodeOp *op;
Varnode *outvn; Varnode *outvn;
@ -508,11 +577,13 @@ bool SubvariableFlow::traceForward(ReplaceVarnode *rvn)
return true; return true;
} }
/// Trace the logical value backward through one PcodeOp adding new nodes to the
/// logical subgraph and updating the worklist.
/// \param rvn is the given logical value to trace
/// \return \b true if the logical value can be traced backward one level
bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn) bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
{ // Trace backward through defining op one level {
// Update worklist, varmap, and oplist
// return false if the trace is aborted
PcodeOp *op = rvn->vn->getDef(); PcodeOp *op = rvn->vn->getDef();
if (op == (PcodeOp *)0) return true; // If vn is input if (op == (PcodeOp *)0) return true; // If vn is input
int4 sa; int4 sa;
@ -647,6 +718,7 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
} }
break; break;
case CPUI_INDIRECT: case CPUI_INDIRECT:
// TODO: This assumes that the INDIRECT is CALL-based. Add STORE-based logic.
if (aggressive) { if (aggressive) {
if (tryCallReturnPull(op,rvn)) if (tryCallReturnPull(op,rvn))
return true; return true;
@ -682,10 +754,13 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
return false; return false;
} }
/// Try to trace the logical variable through descendant Varnodes, updating the logical subgraph.
/// We assume (and check) that the logical variable has always been sign extended (sextstate) into its container.
/// \param rvn is the given subgraph variable to trace
/// \return \b true if the logical value can successfully traced forward one level
bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn) bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn)
{ // Try to trace the logical variable through descendant varnodes, updating map of replacement ops and varnodes {
// We assume (and check) that the logical variable has always been sign extended (sextstate) into its container
ReplaceOp *rop; ReplaceOp *rop;
PcodeOp *op; PcodeOp *op;
Varnode *outvn; Varnode *outvn;
@ -767,10 +842,13 @@ bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn)
return true; return true;
} }
/// Try to trace the logical variable up through its defining op, updating the logical subgraph.
/// We assume (and check) that the logical variable has always been sign extended (sextstate) into its container.
/// \param rvn is the given subgraph variable to trace
/// \return \b true if the logical value can successfully traced backward one level
bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn) bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn)
{ // Trace backward through defining op, one level, update worklist and map {
// We assume (and check) that the logical variable has always been sign extended (sextstate) into its container
PcodeOp *op = rvn->vn->getDef(); PcodeOp *op = rvn->vn->getDef();
if (op == (PcodeOp *)0) return true; // If vn is input if (op == (PcodeOp *)0) return true; // If vn is input
ReplaceOp *rop; ReplaceOp *rop;
@ -813,9 +891,20 @@ bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn)
return false; return false;
} }
bool SubvariableFlow::createLink(ReplaceOp *rop,uintb mask,int4 slot, /// \brief Add a new variable to the logical subgraph as an input to the given operation
Varnode *vn) ///
{ // Add a new varnode (and the edge which traced to it) to the worklist /// The subgraph is extended by the specified input edge, and a new variable node is created
/// if necessary or a preexisting node corresponding to the Varnode is used.
/// If the logical value described by the given mask cannot be made to line up with the
/// subgraph variable node, \b false is returned.
/// \param rop is the given operation
/// \param mask is the mask describing the logical value within the input Varnode
/// \param slot is the input slot of the Varnode to the operation
/// \param vn is the original input Varnode holding the logical value
/// \return \b true is the subgraph is successfully extended to the input
bool SubvariableFlow::createLink(ReplaceOp *rop,uintb mask,int4 slot,Varnode *vn)
{
bool inworklist; bool inworklist;
ReplaceVarnode *rep = setReplacement(vn,mask,inworklist); ReplaceVarnode *rep = setReplacement(vn,mask,inworklist);
if (rep == (ReplaceVarnode *)0) return false; if (rep == (ReplaceVarnode *)0) return false;
@ -837,10 +926,19 @@ bool SubvariableFlow::createLink(ReplaceOp *rop,uintb mask,int4 slot,
return true; return true;
} }
/// \brief Extend the logical subgraph through a given comparison operator if possible
///
/// Given the variable already in the subgraph that is compared and the other side of the
/// comparison, add the other side as a logical value to the subgraph and create a PatchRecord
/// for the comparison operation.
/// \param op is the given comparison operation
/// \param inrvn is the variable already in the logical subgraph
/// \param slot is the input slot to the comparison of the variable already in the subgraph
/// \param othervn is the Varnode holding the other side of the comparison
/// \return \b true if the logical subgraph can successfully be extended through the comparison
bool SubvariableFlow::createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4 slot,Varnode *othervn) bool SubvariableFlow::createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4 slot,Varnode *othervn)
{ // Add a new varnode to the worklist based on subvariable flow crossing a comparison operator -op- {
// -slot- is the slot of -inrvn- into the comparison. -othervn- is the other side of the comparison to be added
bool inworklist; bool inworklist;
ReplaceVarnode *rep = setReplacement(othervn,inrvn->mask,inworklist); ReplaceVarnode *rep = setReplacement(othervn,inrvn->mask,inworklist);
if (rep == (ReplaceVarnode *)0) return false; if (rep == (ReplaceVarnode *)0) return false;
@ -855,6 +953,13 @@ bool SubvariableFlow::createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4
return true; return true;
} }
/// \brief Add a constant variable node to the logical subgraph
///
/// Unlike other subgraph variable nodes, this one does not maintain a mirror with the original containing Varnode.
/// \param rop is the logical operation taking the constant as input
/// \param mask is the set of bits holding the logical value (within a bigger value)
/// \param slot is the input slot to the operation
/// \param val is the bigger constant value holding the logical value
SubvariableFlow::ReplaceVarnode *SubvariableFlow::addConstant(ReplaceOp *rop,uintb mask, SubvariableFlow::ReplaceVarnode *SubvariableFlow::addConstant(ReplaceOp *rop,uintb mask,
uint4 slot,uintb val) uint4 slot,uintb val)
{ // Add a constant to the replacement tree { // Add a constant to the replacement tree
@ -876,12 +981,15 @@ SubvariableFlow::ReplaceVarnode *SubvariableFlow::addConstant(ReplaceOp *rop,uin
return res; return res;
} }
/// \brief Create a new, non-shadowing, subgraph variable node as an operation output
///
/// The new node does not shadow a preexisting Varnode. Because the ReplaceVarnode record
/// is defined by rop (the -def- field is filled in) this can still be distinguished from a constant.
/// \param rop is the logical operation taking the new output
/// \param mask describes the logical value
void SubvariableFlow::createNewOut(ReplaceOp *rop,uintb mask) void SubvariableFlow::createNewOut(ReplaceOp *rop,uintb mask)
{ // Create a varnode output in the replacement graph that {
// does not shadow a preexisting varnode
// Because the ReplaceVarnode record is defined by rop
// (the -def- field is filled in) this can still be distinguished from a constant
newvarlist.push_back(ReplaceVarnode()); newvarlist.push_back(ReplaceVarnode());
ReplaceVarnode *res = &newvarlist.back(); ReplaceVarnode *res = &newvarlist.back();
res->vn = (Varnode *)0; res->vn = (Varnode *)0;
@ -892,10 +1000,16 @@ void SubvariableFlow::createNewOut(ReplaceOp *rop,uintb mask)
res->def = rop; res->def = rop;
} }
/// \brief Mark an operation where a subgraph variable is naturally copied into the original data-flow
///
/// If the operations naturally takes the given logical value as input but the output
/// doesn't need to be traced as a logical value, a subgraph terminator (PatchRecord) is created
/// noting this. The original PcodeOp will be converted to a COPY.
/// \param pullop is the PcodeOp pulling the logical value out of the subgraph
/// \param rvn is the given subgraph variable holding the logical value
void SubvariableFlow::addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn) void SubvariableFlow::addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn)
{ // Add a reference to the logical variable getting pulled {
// out of container flow
patchlist.push_back(PatchRecord()); patchlist.push_back(PatchRecord());
patchlist.back().type = 0; // Ultimately gets converted to a COPY patchlist.back().type = 0; // Ultimately gets converted to a COPY
patchlist.back().pullop = pullop; // Operation pulling the variable out patchlist.back().pullop = pullop; // Operation pulling the variable out
@ -903,6 +1017,14 @@ void SubvariableFlow::addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn)
pullcount += 1; // a true terminal modification pullcount += 1; // a true terminal modification
} }
/// \brief Mark an operation where a subgraph variable is naturally pulled into the original data-flow
///
/// If the operations naturally takes the given logical value as input but the output
/// doesn't need to be traced as a logical value, a subgraph terminator (PatchRecord) is created
/// noting this. The opcode of the operation will not change.
/// \param pullop is the PcodeOp pulling the logical value out of the subgraph
/// \param rvn is the given subgraph variable holding the logical value
/// \param slot is the input slot to the operation
void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot) void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot)
{ {
@ -914,9 +1036,16 @@ void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn
pullcount += 1; // a true terminal modification pullcount += 1; // a true terminal modification
} }
/// \brief Mark a subgraph bit variable flowing into an operation taking a boolean input
///
/// This doesn't count as a Varnode holding a logical value that needs to be patched (by itself).
/// A PatchRecord terminating the logical subgraph along the given edge is created.
/// \param pullup is the operation taking the boolean input
/// \param rvn is the given bit variable
/// \param slot is the input slot of the variable to the operation
void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot) void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot)
{ // Add a reference to the logical bit variable, flowing into a boolean operation {
patchlist.push_back(PatchRecord()); patchlist.push_back(PatchRecord());
patchlist.back().type = 2; // Make no change to the operator, just put in the new input patchlist.back().type = 2; // Make no change to the operator, just put in the new input
patchlist.back().pullop = pullop; // Operation pulling the variable out patchlist.back().pullop = pullop; // Operation pulling the variable out
@ -925,9 +1054,16 @@ void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 s
// this is not a true modification // this is not a true modification
} }
/// \brief Mark a subgraph variable flowing to an operation that expands it by padding with zero bits.
///
/// Data-flow along the specified edge within the logical subgraph is terminated by added a PatchRecord.
/// This doesn't count as a logical value that needs to be patched (by itself).
/// \param rvn is the given subgraph variable
/// \param pushop is the operation that pads the variable
/// \param sa is the amount the logical value is shifted to the left
void SubvariableFlow::addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4 sa) void SubvariableFlow::addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4 sa)
{ // Operations that expand the logical value to a larger value padded with zero bits {
patchlist.push_back(PatchRecord()); patchlist.push_back(PatchRecord());
patchlist.back().type = 3; patchlist.back().type = 3;
patchlist.back().in1 = rvn; patchlist.back().in1 = rvn;
@ -938,9 +1074,16 @@ void SubvariableFlow::addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4
// This is not a true modification because the output is still the expanded size // This is not a true modification because the output is still the expanded size
} }
/// \brief Mark subgraph variables flowing into a comparison operation
///
/// The operation accomplishes the logical comparison by comparing the larger containers.
/// A PatchRecord is created indicating that data-flow from the subgraph terminates at the comparison.
/// \param in1 is the first logical value to the comparison
/// \param in2 is the second logical value
/// \param op is the comparison operation
void SubvariableFlow::addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op) void SubvariableFlow::addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op)
{ // Operations that accomplish the logical comparison by comparing the larger container {
patchlist.push_back(PatchRecord()); patchlist.push_back(PatchRecord());
patchlist.back().type = 1; patchlist.back().type = 1;
patchlist.back().pullop = op; patchlist.back().pullop = op;
@ -949,10 +1092,15 @@ void SubvariableFlow::addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,Pc
pullcount += 1; pullcount += 1;
} }
/// \brief Replace an input Varnode in the subgraph with a temporary register
///
/// This is used to avoid overlapping input Varnode errors. The temporary register
/// is typically short lived and gets quickly eliminated in favor of the new
/// logically sized Varnode.
/// \param rvn is the logical variable to replace
void SubvariableFlow::replaceInput(ReplaceVarnode *rvn) void SubvariableFlow::replaceInput(ReplaceVarnode *rvn)
{ // Replace input in the subgraph with temporaries, so {
// we don't get overlapping varnode errors
Varnode *newvn = fd->newUnique(rvn->vn->getSize()); Varnode *newvn = fd->newUnique(rvn->vn->getSize());
newvn = fd->setInputVarnode(newvn); newvn = fd->setInputVarnode(newvn);
fd->totalReplace(rvn->vn,newvn); fd->totalReplace(rvn->vn,newvn);
@ -960,10 +1108,15 @@ void SubvariableFlow::replaceInput(ReplaceVarnode *rvn)
rvn->vn = newvn; rvn->vn = newvn;
} }
/// \brief Decide if we use the same memory range of the original Varnode for the logical replacement
///
/// Usually the logical Varnode can use the \e true storage bytes that hold the value,
/// but there are a few corner cases where we want to use a new temporary register to hold the value.
/// \param rvn is the subgraph variable
/// \return \b true if the same memory range can be used to hold the value
bool SubvariableFlow::useSameAddress(ReplaceVarnode *rvn) bool SubvariableFlow::useSameAddress(ReplaceVarnode *rvn)
{ // Decide whether we use (a portion of) the same memory location {
// of the original varnode when creating its replacement
if (rvn->vn->isInput()) return true; if (rvn->vn->isInput()) return true;
// If we trim an addrtied varnode, because of required merges, we increase chance of conflicting forms for one variable // If we trim an addrtied varnode, because of required merges, we increase chance of conflicting forms for one variable
if (rvn->vn->isAddrTied()) return false; if (rvn->vn->isAddrTied()) return false;
@ -980,9 +1133,13 @@ bool SubvariableFlow::useSameAddress(ReplaceVarnode *rvn)
return false; // If more of the varnode is consumed than is in just this flow return false; // If more of the varnode is consumed than is in just this flow
} }
/// \brief Calculcate address of replacement Varnode for given subgraph variable node
///
/// \param rvn is the given subgraph variable node
/// \return the address of the new logical Varnode
Address SubvariableFlow::getReplacementAddress(ReplaceVarnode *rvn) const Address SubvariableFlow::getReplacementAddress(ReplaceVarnode *rvn) const
{ // Calculcate the starting address for the replacement varnode of -rvn- {
Address addr = rvn->vn->getAddr(); Address addr = rvn->vn->getAddr();
int4 sa = leastsigbit_set(rvn->mask) / 8; // Number of bytes value is shifted into container int4 sa = leastsigbit_set(rvn->mask) / 8; // Number of bytes value is shifted into container
if (addr.isBigEndian()) if (addr.isBigEndian())
@ -992,11 +1149,15 @@ Address SubvariableFlow::getReplacementAddress(ReplaceVarnode *rvn) const
return addr; return addr;
} }
/// \brief Build the logical Varnode which will replace its original containing Varnode
///
/// This is the main routine for converting a logical variable in the subgraph into
/// an actual Varnode object.
/// \param rvn is the logical variable
/// \return the (new or existing) Varnode object
Varnode *SubvariableFlow::getReplaceVarnode(ReplaceVarnode *rvn) Varnode *SubvariableFlow::getReplaceVarnode(ReplaceVarnode *rvn)
{ // Get the actual varnode associated with a replacement varnode {
// either by recycling a previously built one or creating
// one on the spot
if (rvn->replacement != (Varnode *)0) if (rvn->replacement != (Varnode *)0)
return rvn->replacement; return rvn->replacement;
// Only a constant if BOTH replacement and vn fields are null // Only a constant if BOTH replacement and vn fields are null
@ -1021,6 +1182,10 @@ Varnode *SubvariableFlow::getReplaceVarnode(ReplaceVarnode *rvn)
return rvn->replacement; return rvn->replacement;
} }
/// The subgraph is extended from the variable node at the top of the worklist.
/// Data-flow is traced forward and backward one level, possibly extending the subgraph
/// and adding new nodes to the worklist.
/// \return \b true if the node was successfully processed
bool SubvariableFlow::processNextWork(void) bool SubvariableFlow::processNextWork(void)
{ {

View file

@ -13,69 +13,74 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/// \file subflow.hh
/// \brief Classes for reducing/splitting Varnodes containing smaller logical values
#ifndef __SUBVARIABLE_FLOW__ #ifndef __SUBVARIABLE_FLOW__
#define __SUBVARIABLE_FLOW__ #define __SUBVARIABLE_FLOW__
#include "funcdata.hh" #include "funcdata.hh"
// Structures for splitting big varnodes carrying smaller logical /// \brief Class for shrinking big Varnodes carrying smaller logical values
// variables. Given a root within the syntax tree and dimensions ///
// of a logical variable, this class traces the flow of this /// Given a root within the syntax tree and dimensions
// logical variable through its containing varnodes. It then /// of a logical variable, this class traces the flow of this
// creates a subgraph of this flow, where there is a correspondence /// logical variable through its containing Varnodes. It then
// between nodes in the subgraph and nodes in the original graph /// creates a subgraph of this flow, where there is a correspondence
// containing the logical variable. When doReplacement is called, /// between nodes in the subgraph and nodes in the original graph
// this subgraph is duplicated as a new separate piece within the /// containing the logical variable. When doReplacement is called,
// syntax tree. Ops are replaced to reflect the manipulation of /// this subgraph is duplicated as a new separate piece within the
// of the logical variable, rather than the containing variable. /// syntax tree. Ops are replaced to reflect the manipulation of
// Operations in the original graph which pluck out the logical /// of the logical variable, rather than the containing variable.
// variable from the containing variable, are replaced with copies /// Operations in the original graph which pluck out the logical
// from the corresponding node in the new section of the graph, /// variable from the containing variable, are replaced with copies
// which frequently causes the operations on the original container /// from the corresponding node in the new section of the graph,
// varnodes to becomes deadcode. /// which frequently causes the operations on the original container
/// Varnodes to becomes dead code.
class SubvariableFlow { class SubvariableFlow {
class ReplaceOp; class ReplaceOp;
/// \brief Placeholder node for Varnode holding a smaller logical value
class ReplaceVarnode { class ReplaceVarnode {
friend class SubvariableFlow; friend class SubvariableFlow;
Varnode *vn; // Varnode being split Varnode *vn; ///< Varnode being shrunk
Varnode *replacement; // The new subvariable varnode Varnode *replacement; ///< The new smaller Varnode
uintb mask; // Bits of the logical subvariable uintb mask; ///< Bits making up the logical sub-variable
uintb val; // Value of constant (vn==NULL) uintb val; ///< Value of constant (when vn==NULL)
ReplaceOp *def; // Defining op for new varnode ReplaceOp *def; ///< Defining op for new Varnode
}; };
/// \brief Placeholder node for PcodeOp operating on smaller logical values
class ReplaceOp { class ReplaceOp {
friend class SubvariableFlow; friend class SubvariableFlow;
PcodeOp *op; // op getting paralleled PcodeOp *op; ///< op getting paralleled
PcodeOp *replacement; // The new op PcodeOp *replacement; ///< The new op
OpCode opc; // type of new op OpCode opc; ///< Opcode of the new op
int4 numparams; int4 numparams; ///< Number of parameters in (new) op
ReplaceVarnode *output; // varnode output ReplaceVarnode *output; ///< Varnode output
vector<ReplaceVarnode *> input; // varnode inputs vector<ReplaceVarnode *> input; ///< Varnode inputs
}; };
class PatchRecord { // Operation where logical value is part of input, but output remains as is /// \brief Operation with a new logical value as (part of) input, but output Varnode is unchanged
class PatchRecord {
friend class SubvariableFlow; friend class SubvariableFlow;
int4 type; // 0=COPY 1=compare 2=call 3=AND/SHIFT int4 type; ///< 0=COPY 1=compare 2=call 3=AND/SHIFT
PcodeOp *pullop; // Op being affected PcodeOp *pullop; ///< Op being affected
ReplaceVarnode *in1; // The logical variable input ReplaceVarnode *in1; ///< The logical variable input
ReplaceVarnode *in2; // (optional second parameter) ReplaceVarnode *in2; ///< (optional second parameter)
int4 slot; // slot being affected or other parameter int4 slot; ///< slot being affected or other parameter
}; };
int4 flowsize; // Size of the data-flow int4 flowsize; ///< Size of the lgoical data-flow in bytes
int4 bitsize; // Number of bits in logical variable int4 bitsize; ///< Number of bits in logical variable
bool returnsTraversed; // Have we tried to flow logical value across CPUI_RETURNs bool returnsTraversed; ///< Have we tried to flow logical value across CPUI_RETURNs
bool aggressive; // Do we "know" initial seed point must be a sub variable bool aggressive; ///< Do we "know" initial seed point must be a sub variable
bool sextrestrictions; // Check for logical variables that are always sign extended into their container bool sextrestrictions; ///< Check for logical variables that are always sign extended into their container
Funcdata *fd; Funcdata *fd; ///< Containing function
map<Varnode *,ReplaceVarnode> varmap; map<Varnode *,ReplaceVarnode> varmap; ///< Map from original Varnodes to the overlaying subgraph nodes
list<ReplaceVarnode> newvarlist; list<ReplaceVarnode> newvarlist; ///< Storage for subgraph variable nodes
list<ReplaceOp> oplist; list<ReplaceOp> oplist; ///< Storage for subgraph op nodes
list<PatchRecord> patchlist; // Operations getting patched (but no flow thru) list<PatchRecord> patchlist; ///< Operations getting patched (but with no flow thru)
vector<ReplaceVarnode *> worklist; vector<ReplaceVarnode *> worklist; ///< Subgraph variable nodes still needing to be traced
int4 pullcount; // Number of instructions pulling out the logical value int4 pullcount; ///< Number of instructions pulling out the logical value
static int4 doesOrSet(PcodeOp *orop,uintb mask); static int4 doesOrSet(PcodeOp *orop,uintb mask);
static int4 doesAndClear(PcodeOp *andop,uintb mask); static int4 doesAndClear(PcodeOp *andop,uintb mask);
Address getReplacementAddress(ReplaceVarnode *rvn) const; Address getReplacementAddress(ReplaceVarnode *rvn) const;
@ -86,10 +91,10 @@ class SubvariableFlow {
bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot); bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot); bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn); bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn);
bool traceForward(ReplaceVarnode *rvn); bool traceForward(ReplaceVarnode *rvn); ///< Trace the logical data-flow forward for the given subgraph variable
bool traceBackward(ReplaceVarnode *rvn); bool traceBackward(ReplaceVarnode *rvn); ///< Trace the logical data-flow backward for the given subgraph variable
bool traceForwardSext(ReplaceVarnode *rvn); bool traceForwardSext(ReplaceVarnode *rvn); ///< Trace logical data-flow forward assuming sign-extensions
bool traceBackwardSext(ReplaceVarnode *rvn); bool traceBackwardSext(ReplaceVarnode *rvn); ///< Trace logical data-flow backward assuming sign-extensions
bool createLink(ReplaceOp *rop,uintb mask,int4 slot,Varnode *vn); bool createLink(ReplaceOp *rop,uintb mask,int4 slot,Varnode *vn);
bool createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4 slot,Varnode *othervn); bool createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4 slot,Varnode *othervn);
void addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn); void addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn);
@ -102,7 +107,7 @@ class SubvariableFlow {
void replaceInput(ReplaceVarnode *rvn); void replaceInput(ReplaceVarnode *rvn);
bool useSameAddress(ReplaceVarnode *rvn); bool useSameAddress(ReplaceVarnode *rvn);
Varnode *getReplaceVarnode(ReplaceVarnode *rvn); Varnode *getReplaceVarnode(ReplaceVarnode *rvn);
bool processNextWork(void); bool processNextWork(void); ///< Extend the subgraph from the next node in the worklist
public: public:
SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext); SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext);
bool doTrace(void); bool doTrace(void);