mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Different widening strategies
This commit is contained in:
parent
8565f43e06
commit
32793712fe
3 changed files with 173 additions and 64 deletions
|
@ -2483,6 +2483,16 @@ void IfcAnalyzeRange::execute(istream &s)
|
||||||
if (dcp->fd == (Funcdata *)0)
|
if (dcp->fd == (Funcdata *)0)
|
||||||
throw IfaceExecutionError("No function selected");
|
throw IfaceExecutionError("No function selected");
|
||||||
|
|
||||||
|
bool useFullWidener;
|
||||||
|
string token;
|
||||||
|
s >> ws >> token;
|
||||||
|
if (token == "full")
|
||||||
|
useFullWidener = true;
|
||||||
|
else if (token == "partial") {
|
||||||
|
useFullWidener = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw IfaceParseError("Must specify \"full\" or \"partial\" widening");
|
||||||
Varnode *vn = iface_read_varnode(dcp,s);
|
Varnode *vn = iface_read_varnode(dcp,s);
|
||||||
vector<Varnode *> sinks;
|
vector<Varnode *> sinks;
|
||||||
vector<PcodeOp *> reads;
|
vector<PcodeOp *> reads;
|
||||||
|
@ -2495,7 +2505,14 @@ void IfcAnalyzeRange::execute(istream &s)
|
||||||
Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace());
|
Varnode *stackReg = dcp->fd->findSpacebaseInput(dcp->conf->getStackSpace());
|
||||||
ValueSetSolver vsSolver;
|
ValueSetSolver vsSolver;
|
||||||
vsSolver.establishValueSets(sinks, reads, stackReg, false);
|
vsSolver.establishValueSets(sinks, reads, stackReg, false);
|
||||||
vsSolver.solve(10000);
|
if (useFullWidener) {
|
||||||
|
WidenerFull widener;
|
||||||
|
vsSolver.solve(10000,widener);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WidenerNone widener;
|
||||||
|
vsSolver.solve(10000,widener);
|
||||||
|
}
|
||||||
list<ValueSet>::const_iterator iter;
|
list<ValueSet>::const_iterator iter;
|
||||||
for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) {
|
for(iter=vsSolver.beginValueSets();iter!=vsSolver.endValueSets();++iter) {
|
||||||
(*iter).printRaw(*status->optr);
|
(*iter).printRaw(*status->optr);
|
||||||
|
|
|
@ -1265,7 +1265,7 @@ bool CircleRange::pushForwardBinary(OpCode opc,const CircleRange &in1,const Circ
|
||||||
step = 1; // Lose any step
|
step = 1; // Lose any step
|
||||||
if (in1.left < in1.right) {
|
if (in1.left < in1.right) {
|
||||||
left = in1.left >> sa;
|
left = in1.left >> sa;
|
||||||
right = in1.right >> sa;
|
right = ((in1.right - in1.step) >> sa) + 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
left = 0;
|
left = 0;
|
||||||
|
@ -1483,56 +1483,6 @@ void ValueSet::addEquation(int4 slot,int4 type,const CircleRange &constraint)
|
||||||
equations.insert(iter,Equation(slot,type,constraint));
|
equations.insert(iter,Equation(slot,type,constraint));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For an iteration that isn't stabilizing attempt to widen \b this value set
|
|
||||||
/// so that it stabilizes more rapidly on future iterations. For initial iterations,
|
|
||||||
/// targeted widening is attempted using landmarks, otherwise \b this is widened
|
|
||||||
/// to \e everything.
|
|
||||||
/// \param newRange is the changed range for the current iteration
|
|
||||||
void ValueSet::doWidening(const CircleRange &newRange)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (count < 2) {
|
|
||||||
range = newRange;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (count == 2) {
|
|
||||||
if (!equations.empty()) { // Look for landmark
|
|
||||||
const Equation &landmark(equations.back());
|
|
||||||
if (landmark.slot == numParams && typeCode == landmark.typeCode) {
|
|
||||||
bool leftIsStable = range.getMin() == newRange.getMin();
|
|
||||||
range = newRange; // Preserve any new step information
|
|
||||||
if (landmark.range.contains(range)) {
|
|
||||||
range.widen(landmark.range,leftIsStable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
CircleRange constraint = landmark.range;
|
|
||||||
constraint.invert();
|
|
||||||
if (constraint.contains(range)) {
|
|
||||||
range.widen(constraint,leftIsStable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (count <5) {
|
|
||||||
range = newRange;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setFull(); // In all other cases expand to everything
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Give \b this value set a chance to reset its counter do to looping
|
|
||||||
void ValueSet::looped(void)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (count >= 2)
|
|
||||||
count = 2; // Reset to point just after any widening
|
|
||||||
else
|
|
||||||
count = 0; // Delay widening, if we haven't performed it yet
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Examine the input value sets that determine \b this set and decide if it
|
/// Examine the input value sets that determine \b this set and decide if it
|
||||||
/// is relative. In general, \b this will be relative if any of its inputs are.
|
/// is relative. In general, \b this will be relative if any of its inputs are.
|
||||||
/// Certain combinations are indeterminate, which this method flags by calling
|
/// Certain combinations are indeterminate, which this method flags by calling
|
||||||
|
@ -1578,11 +1528,11 @@ void ValueSet::computeTypeCode(void)
|
||||||
/// operator defining the Varnode attached to \b this value set and pushing them
|
/// operator defining the Varnode attached to \b this value set and pushing them
|
||||||
/// forward through the operator.
|
/// forward through the operator.
|
||||||
/// \return \b true if there was a change to \b this value set
|
/// \return \b true if there was a change to \b this value set
|
||||||
bool ValueSet::iterate(void)
|
bool ValueSet::iterate(Widener &widener)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!vn->isWritten()) return false;
|
if (!vn->isWritten()) return false;
|
||||||
if (range.isFull()) return false;
|
if (widener.checkFreeze(*this)) return false;
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
computeTypeCode();
|
computeTypeCode();
|
||||||
count += 1; // Count this iteration
|
count += 1; // Count this iteration
|
||||||
|
@ -1683,13 +1633,30 @@ bool ValueSet::iterate(void)
|
||||||
|
|
||||||
if (res == range)
|
if (res == range)
|
||||||
return false;
|
return false;
|
||||||
if (partHead != (Partition *)0)
|
if (partHead != (Partition *)0) {
|
||||||
doWidening(res);
|
if (!widener.doWidening(*this, range, res))
|
||||||
|
setFull();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
range = res;
|
range = res;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If a landmark was associated with \b this value set, return its range,
|
||||||
|
/// otherwise return null.
|
||||||
|
/// \return the landmark range or null
|
||||||
|
const CircleRange *ValueSet::getLandMark(void) const
|
||||||
|
|
||||||
|
{
|
||||||
|
if (equations.empty())
|
||||||
|
return (const CircleRange *)0;
|
||||||
|
|
||||||
|
const Equation &landmark(equations.back());
|
||||||
|
if (landmark.slot != numParams || typeCode != landmark.typeCode)
|
||||||
|
return (const CircleRange *)0;
|
||||||
|
return &landmark.range;
|
||||||
|
}
|
||||||
|
|
||||||
/// \param s is the stream to print to
|
/// \param s is the stream to print to
|
||||||
void ValueSet::printRaw(ostream &s) const
|
void ValueSet::printRaw(ostream &s) const
|
||||||
|
|
||||||
|
@ -1766,6 +1733,76 @@ void ValueSetRead::printRaw(ostream &s) const
|
||||||
range.printRaw(s);
|
range.printRaw(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int4 WidenerFull::determineIterationReset(const ValueSet &valueSet)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (valueSet.getCount() >= widenIteration)
|
||||||
|
return widenIteration; // Reset to point just after any widening
|
||||||
|
return 0; // Delay widening, if we haven't performed it yet
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidenerFull::checkFreeze(const ValueSet &valueSet)
|
||||||
|
|
||||||
|
{
|
||||||
|
return valueSet.getRange().isFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidenerFull::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (valueSet.getCount() < widenIteration) {
|
||||||
|
range = newRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (valueSet.getCount() == widenIteration) {
|
||||||
|
const CircleRange *landmark = valueSet.getLandMark();
|
||||||
|
if (landmark != (const CircleRange *)0) {
|
||||||
|
bool leftIsStable = range.getMin() == newRange.getMin();
|
||||||
|
range = newRange; // Preserve any new step information
|
||||||
|
if (landmark->contains(range)) {
|
||||||
|
range.widen(*landmark,leftIsStable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CircleRange constraint = *landmark;
|
||||||
|
constraint.invert();
|
||||||
|
if (constraint.contains(range)) {
|
||||||
|
range.widen(constraint,leftIsStable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (valueSet.getCount() < fullIteration) {
|
||||||
|
range = newRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // Indicate that constrained widening failed (set to full)
|
||||||
|
}
|
||||||
|
|
||||||
|
int4 WidenerNone::determineIterationReset(const ValueSet &valueSet)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (valueSet.getCount() >= freezeIteration)
|
||||||
|
return freezeIteration; // Reset to point just after any widening
|
||||||
|
return valueSet.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidenerNone::checkFreeze(const ValueSet &valueSet)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (valueSet.getRange().isFull())
|
||||||
|
return true;
|
||||||
|
return (valueSet.getCount() >= freezeIteration);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WidenerNone::doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)
|
||||||
|
|
||||||
|
{
|
||||||
|
range = newRange;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Construct an iterator over the outbound edges of the given ValueSet node
|
/// \brief Construct an iterator over the outbound edges of the given ValueSet node
|
||||||
///
|
///
|
||||||
/// Mostly this just forwards the ValueSets attached to output Varnodes
|
/// Mostly this just forwards the ValueSets attached to output Varnodes
|
||||||
|
@ -2324,7 +2361,8 @@ void ValueSetSolver::establishValueSets(const vector<Varnode *> &sinks,const vec
|
||||||
/// The ValueSets are recalculated in the established topological ordering, with looping
|
/// The ValueSets are recalculated in the established topological ordering, with looping
|
||||||
/// at various levels until a fixed point is reached.
|
/// at various levels until a fixed point is reached.
|
||||||
/// \param max is the maximum number of iterations to allow before forcing termination
|
/// \param max is the maximum number of iterations to allow before forcing termination
|
||||||
void ValueSetSolver::solve(int4 max)
|
/// \param widener is the Widening strategy to use to accelerate stabilization
|
||||||
|
void ValueSetSolver::solve(int4 max,Widener &widener)
|
||||||
|
|
||||||
{
|
{
|
||||||
maxIterations = max;
|
maxIterations = max;
|
||||||
|
@ -2343,10 +2381,11 @@ void ValueSetSolver::solve(int4 max)
|
||||||
componentStack.push_back(curSet->partHead);
|
componentStack.push_back(curSet->partHead);
|
||||||
curComponent = curSet->partHead;
|
curComponent = curSet->partHead;
|
||||||
curComponent->isDirty = false;
|
curComponent->isDirty = false;
|
||||||
curComponent->startNode->looped(); // Reset component counter upon entry
|
// Reset component counter upon entry
|
||||||
|
curComponent->startNode->count = widener.determineIterationReset(*curComponent->startNode);
|
||||||
}
|
}
|
||||||
if (curComponent != (Partition *)0) {
|
if (curComponent != (Partition *)0) {
|
||||||
if (curSet->iterate())
|
if (curSet->iterate(widener))
|
||||||
curComponent->isDirty = true;
|
curComponent->isDirty = true;
|
||||||
if (curComponent->stopNode != curSet) {
|
if (curComponent->stopNode != curSet) {
|
||||||
curSet = curSet->next;
|
curSet = curSet->next;
|
||||||
|
@ -2377,7 +2416,7 @@ void ValueSetSolver::solve(int4 max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
curSet->iterate();
|
curSet->iterate(widener);
|
||||||
curSet = curSet->next;
|
curSet = curSet->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class Partition; // Forward declaration
|
class Partition; // Forward declaration
|
||||||
|
class Widener; // Forward declaration
|
||||||
|
|
||||||
/// \brief A range of values attached to a Varnode within a data-flow subsystem
|
/// \brief A range of values attached to a Varnode within a data-flow subsystem
|
||||||
///
|
///
|
||||||
|
@ -139,11 +140,11 @@ private:
|
||||||
void setVarnode(Varnode *v,int4 tCode); ///< Attach \b this to given Varnode and set initial values
|
void setVarnode(Varnode *v,int4 tCode); ///< Attach \b this to given Varnode and set initial values
|
||||||
void addEquation(int4 slot,int4 type,const CircleRange &constraint); ///< Insert an equation restricting \b this value set
|
void addEquation(int4 slot,int4 type,const CircleRange &constraint); ///< Insert an equation restricting \b this value set
|
||||||
void addLandmark(int4 type,const CircleRange &constraint) { addEquation(numParams,type,constraint); } ///< Add a widening landmark
|
void addLandmark(int4 type,const CircleRange &constraint) { addEquation(numParams,type,constraint); } ///< Add a widening landmark
|
||||||
void doWidening(const CircleRange &newRange); ///< Widen the value set so fixed point is reached sooner
|
|
||||||
void looped(void); ///< Mark that iteration has looped back to \b this
|
|
||||||
void computeTypeCode(void); ///< Figure out if \b this value set is absolute or relative
|
void computeTypeCode(void); ///< Figure out if \b this value set is absolute or relative
|
||||||
bool iterate(void); ///< Regenerate \b this value set from operator inputs
|
bool iterate(Widener &widener); ///< Regenerate \b this value set from operator inputs
|
||||||
public:
|
public:
|
||||||
|
int4 getCount(void) const { return count; } ///< Get the current iteration count
|
||||||
|
const CircleRange *getLandMark(void) const; ///< Get any \e landmark range
|
||||||
int4 getTypeCode(void) const { return typeCode; } ///< Return '0' for normal constant, '1' for spacebase relative
|
int4 getTypeCode(void) const { return typeCode; } ///< Return '0' for normal constant, '1' for spacebase relative
|
||||||
Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode attached to \b this ValueSet
|
Varnode *getVarnode(void) const { return vn; } ///< Get the Varnode attached to \b this ValueSet
|
||||||
const CircleRange &getRange(void) const { return range; } ///< Get the actual range of values
|
const CircleRange &getRange(void) const { return range; } ///< Get the actual range of values
|
||||||
|
@ -185,6 +186,58 @@ public:
|
||||||
void printRaw(ostream &s) const; ///< Write a text description of \b to the given stream
|
void printRaw(ostream &s) const; ///< Write a text description of \b to the given stream
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Widener {
|
||||||
|
public:
|
||||||
|
virtual ~Widener(void) {} ///< Destructor
|
||||||
|
|
||||||
|
/// \brief Upon entering a fresh partition, determine how the given ValueSet count should be reset
|
||||||
|
///
|
||||||
|
/// \param valueSet is the given value set
|
||||||
|
/// \return the value of the iteration counter to reset to
|
||||||
|
virtual int4 determineIterationReset(const ValueSet &valueSet)=0;
|
||||||
|
|
||||||
|
/// \brief Check if the given value set has been frozen for the remainder of the iteration process
|
||||||
|
///
|
||||||
|
/// \param valueSet is the given value set
|
||||||
|
/// \return \b true if the valueSet will no longer change
|
||||||
|
virtual bool checkFreeze(const ValueSet &valueSet)=0;
|
||||||
|
|
||||||
|
/// \brief For an iteration that isn't stabilizing attempt to widen the given ValueSet
|
||||||
|
///
|
||||||
|
/// Change the given range based on its previous iteration so that it stabilizes more
|
||||||
|
/// rapidly on future iterations.
|
||||||
|
/// \param valueSet is the given value set
|
||||||
|
/// \param range is the previous form of the given range (and storage for the widening result)
|
||||||
|
/// \param newRange is the current iteration of the given range
|
||||||
|
/// \return \b true if widening succeeded
|
||||||
|
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Class for doing normal widening
|
||||||
|
///
|
||||||
|
/// Widening is attempted at a specific iteration. If a landmark is available, it is used
|
||||||
|
/// to do a controlled widening, holding the stable range boundary constant. Otherwise a
|
||||||
|
/// full range is produced. At a later iteration, a full range is produced automatically.
|
||||||
|
class WidenerFull : public Widener {
|
||||||
|
int4 widenIteration; ///< The iteration at which widening is attempted
|
||||||
|
int4 fullIteration; ///< The iteration at which a full range is produced
|
||||||
|
public:
|
||||||
|
WidenerFull(void) { widenIteration = 2; fullIteration = 5; } ///< Constructor with default iterations
|
||||||
|
WidenerFull(int4 wide,int4 full) { widenIteration = wide; fullIteration = full; } ///< Constructor specifying iterations
|
||||||
|
virtual int4 determineIterationReset(const ValueSet &valueSet);
|
||||||
|
virtual bool checkFreeze(const ValueSet &valueSet);
|
||||||
|
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
class WidenerNone : public Widener {
|
||||||
|
int4 freezeIteration; ///< The iteration at which all change ceases
|
||||||
|
public:
|
||||||
|
WidenerNone(void) { freezeIteration = 3; }
|
||||||
|
virtual int4 determineIterationReset(const ValueSet &valueSet);
|
||||||
|
virtual bool checkFreeze(const ValueSet &valueSet);
|
||||||
|
virtual bool doWidening(const ValueSet &valueSet,CircleRange &range,const CircleRange &newRange);
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Class the determines a ValueSet for each Varnode in a data-flow system
|
/// \brief Class the determines a ValueSet for each Varnode in a data-flow system
|
||||||
///
|
///
|
||||||
/// This class uses \e value \e set \e analysis to calculate (an overestimation of)
|
/// This class uses \e value \e set \e analysis to calculate (an overestimation of)
|
||||||
|
@ -235,7 +288,7 @@ class ValueSetSolver {
|
||||||
public:
|
public:
|
||||||
void establishValueSets(const vector<Varnode *> &sinks,const vector<PcodeOp *> &reads,Varnode *stackReg,bool indirectAsCopy);
|
void establishValueSets(const vector<Varnode *> &sinks,const vector<PcodeOp *> &reads,Varnode *stackReg,bool indirectAsCopy);
|
||||||
int4 getNumIterations(void) const { return numIterations; } ///< Get the current number of iterations
|
int4 getNumIterations(void) const { return numIterations; } ///< Get the current number of iterations
|
||||||
void solve(int4 max); ///< Iterate the ValueSet system until it stabilizes
|
void solve(int4 max,Widener &widener); ///< Iterate the ValueSet system until it stabilizes
|
||||||
list<ValueSet>::const_iterator beginValueSets(void) const { return valueNodes.begin(); } ///< Start of all ValueSets in the system
|
list<ValueSet>::const_iterator beginValueSets(void) const { return valueNodes.begin(); } ///< Start of all ValueSets in the system
|
||||||
list<ValueSet>::const_iterator endValueSets(void) const { return valueNodes.end(); } ///< End of all ValueSets in the system
|
list<ValueSet>::const_iterator endValueSets(void) const { return valueNodes.end(); } ///< End of all ValueSets in the system
|
||||||
map<SeqNum,ValueSetRead>::const_iterator beginValueSetReads(void) const { return readNodes.begin(); } ///< Start of ValueSetReads
|
map<SeqNum,ValueSetRead>::const_iterator beginValueSetReads(void) const { return readNodes.begin(); } ///< Start of ValueSetReads
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue