mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5934 Fix handling of integer minimum in SBORROW, SLESS rules, add
SCARRY rule.
This commit is contained in:
parent
c7b125b1c7
commit
8217593e87
17 changed files with 898 additions and 670 deletions
|
@ -1184,329 +1184,4 @@ void PcodeOpBank::clear(void)
|
|||
uniqid = 0;
|
||||
}
|
||||
|
||||
static int4 functionalEqualityLevel0(Varnode *vn1,Varnode *vn2)
|
||||
|
||||
{ // Return 0 if -vn1- and -vn2- must hold same value
|
||||
// Return -1 if they definitely don't hold same value
|
||||
// Return 1 if the same value depends on ops writing to -vn1- and -vn2-
|
||||
if (vn1==vn2) return 0;
|
||||
if (vn1->getSize() != vn2->getSize()) return -1;
|
||||
if (vn1->isConstant()) {
|
||||
if (vn2->isConstant()) {
|
||||
return (vn1->getOffset() == vn2->getOffset()) ? 0 : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (vn2->isConstant()) return -1;
|
||||
if (vn1->isWritten() && vn2->isWritten()) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// \brief Try to determine if \b vn1 and \b vn2 contain the same value
|
||||
///
|
||||
/// Return:
|
||||
/// - -1, if they do \b not, or if it can't be immediately verified
|
||||
/// - 0, if they \b do hold the same value
|
||||
/// - >0, if the result is contingent on additional varnode pairs having the same value
|
||||
/// In the last case, the varnode pairs are returned as (res1[i],res2[i]),
|
||||
/// where the return value is the number of pairs.
|
||||
/// \param vn1 is the first Varnode to compare
|
||||
/// \param vn2 is the second Varnode
|
||||
/// \param res1 is a reference to the first returned Varnode
|
||||
/// \param res2 is a reference to the second returned Varnode
|
||||
/// \return the result of the comparison
|
||||
int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2)
|
||||
|
||||
{
|
||||
int4 testval = functionalEqualityLevel0(vn1,vn2);
|
||||
if (testval != 1) return testval;
|
||||
PcodeOp *op1 = vn1->getDef();
|
||||
PcodeOp *op2 = vn2->getDef();
|
||||
OpCode opc = op1->code();
|
||||
|
||||
if (opc != op2->code()) return -1;
|
||||
|
||||
int4 num = op1->numInput();
|
||||
if (num != op2->numInput()) return -1;
|
||||
if (op1->isMarker()) return -1;
|
||||
if (op2->isCall()) return -1;
|
||||
if (opc == CPUI_LOAD) {
|
||||
// FIXME: We assume two loads produce the same
|
||||
// result if the address is the same and the loads
|
||||
// occur in the same instruction
|
||||
if (op1->getAddr() != op2->getAddr()) return -1;
|
||||
}
|
||||
if (num >= 3) {
|
||||
if (opc != CPUI_PTRADD) return -1; // If this is a PTRADD
|
||||
if (op1->getIn(2)->getOffset() != op2->getIn(2)->getOffset()) return -1; // Make sure the elsize constant is equal
|
||||
num = 2; // Otherwise treat as having 2 inputs
|
||||
}
|
||||
for(int4 i=0;i<num;++i) {
|
||||
res1[i] = op1->getIn(i);
|
||||
res2[i] = op2->getIn(i);
|
||||
}
|
||||
|
||||
testval = functionalEqualityLevel0(res1[0],res2[0]);
|
||||
if (testval == 0) { // A match locks in this comparison ordering
|
||||
if (num==1) return 0;
|
||||
testval = functionalEqualityLevel0(res1[1],res2[1]);
|
||||
if (testval==0) return 0;
|
||||
if (testval < 0) return -1;
|
||||
res1[0] = res1[1]; // Match is contingent on second pair
|
||||
res2[0] = res2[1];
|
||||
return 1;
|
||||
}
|
||||
if (num == 1) return testval;
|
||||
int4 testval2 = functionalEqualityLevel0(res1[1],res2[1]);
|
||||
if (testval2 == 0) { // A match locks in this comparison ordering
|
||||
return testval;
|
||||
}
|
||||
int4 unmatchsize;
|
||||
if ((testval==1)&&(testval2==1))
|
||||
unmatchsize = 2;
|
||||
else
|
||||
unmatchsize = -1;
|
||||
|
||||
if (!op1->isCommutative()) return unmatchsize;
|
||||
// unmatchsize must be 2 or -1 here on a commutative operator,
|
||||
// try flipping
|
||||
int4 comm1 = functionalEqualityLevel0(res1[0],res2[1]);
|
||||
int4 comm2 = functionalEqualityLevel0(res1[1],res2[0]);
|
||||
if ((comm1==0) && (comm2==0))
|
||||
return 0;
|
||||
if ((comm1<0)||(comm2<0))
|
||||
return unmatchsize;
|
||||
if (comm1==0) { // AND (comm2==1)
|
||||
res1[0] = res1[1]; // Left over unmatch is res1[1] and res2[0]
|
||||
return 1;
|
||||
}
|
||||
if (comm2==0) { // AND (comm1==1)
|
||||
res2[0] = res2[1]; // Left over unmatch is res1[0] and res2[1]
|
||||
return 1;
|
||||
}
|
||||
// If we reach here (comm1==1) AND (comm2==1)
|
||||
if (unmatchsize == 2) // If the original ordering wasn't impossible
|
||||
return 2; // Prefer the original ordering
|
||||
Varnode *tmpvn = res2[0]; // Otherwise swap the ordering
|
||||
res2[0] = res2[1];
|
||||
res2[1] = tmpvn;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/// \brief Determine if two Varnodes hold the same value
|
||||
///
|
||||
/// Only return \b true if it can be immediately determined they are equivalent
|
||||
/// \param vn1 is the first Varnode
|
||||
/// \param vn2 is the second Varnode
|
||||
/// \return true if they are provably equal
|
||||
bool functionalEquality(Varnode *vn1,Varnode *vn2)
|
||||
|
||||
{
|
||||
Varnode *buf1[2];
|
||||
Varnode *buf2[2];
|
||||
return (functionalEqualityLevel(vn1,vn2,buf1,buf2)==0);
|
||||
}
|
||||
|
||||
/// \brief Return true if vn1 and vn2 are verifiably different values
|
||||
///
|
||||
/// This is actually a rather speculative test
|
||||
/// \param vn1 is the first Varnode to compare
|
||||
/// \param vn2 is the second Varnode
|
||||
/// \param depth is the maximum level to recurse while testing
|
||||
/// \return \b true if they are different
|
||||
bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||
|
||||
{
|
||||
PcodeOp *op1,*op2;
|
||||
int4 i,num;
|
||||
|
||||
if (vn1 == vn2) return false;
|
||||
if ((!vn1->isWritten())||(!vn2->isWritten())) {
|
||||
if (vn1->isConstant() && vn2->isConstant())
|
||||
return !(vn1->getAddr()==vn2->getAddr());
|
||||
if (vn1->isInput()&&vn2->isInput()) return false; // Might be the same
|
||||
if (vn1->isFree()||vn2->isFree()) return false; // Might be the same
|
||||
return true;
|
||||
}
|
||||
op1 = vn1->getDef();
|
||||
op2 = vn2->getDef();
|
||||
if (op1->code() != op2->code()) return true;
|
||||
num = op1->numInput();
|
||||
if (num != op2->numInput()) return true;
|
||||
if (depth==0) return true; // Different as far as we can tell
|
||||
depth -= 1;
|
||||
for(i=0;i<num;++i)
|
||||
if (functionalDifference(op1->getIn(i),op2->getIn(i),depth))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Test if two operations with same opcode produce complementary boolean values
|
||||
///
|
||||
/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the
|
||||
/// inputs is constant.
|
||||
/// \param bin1op is the first p-code op to compare
|
||||
/// \param bin2op is the second p-code op to compare
|
||||
/// \return \b true if the two operations always produce complementary values
|
||||
bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
|
||||
{
|
||||
OpCode opcode = bin1op->code();
|
||||
if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) {
|
||||
// Basically we test for the scenario like: x < 9 8 < x
|
||||
int4 constslot = 0;
|
||||
if (bin1op->getIn(1)->isConstant())
|
||||
constslot = 1;
|
||||
if (!bin1op->getIn(constslot)->isConstant()) return false;
|
||||
if (!bin2op->getIn(1-constslot)->isConstant()) return false;
|
||||
if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false;
|
||||
uintb val1 = bin1op->getIn(constslot)->getOffset();
|
||||
uintb val2 = bin2op->getIn(1-constslot)->getOffset();
|
||||
if (constslot!=0) {
|
||||
uintb tmp = val2;
|
||||
val2 = val1;
|
||||
val1 = tmp;
|
||||
}
|
||||
if (val1 + 1 != val2) return false;
|
||||
if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned
|
||||
if (opcode==CPUI_INT_SLESS) { // Corner case for signed
|
||||
int4 sz = bin1op->getIn(constslot)->getSize();
|
||||
if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Do the given Varnodes hold the same value, possibly as constants
|
||||
///
|
||||
/// \param a is the first Varnode to compare
|
||||
/// \param b is the second Varnode
|
||||
/// \return \b true if the Varnodes (always) hold the same value
|
||||
bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a->isConstant() && b->isConstant())
|
||||
return (a->getOffset() == b->getOffset());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine if two boolean Varnodes hold related values
|
||||
///
|
||||
/// The values may be the \e same, or opposite of each other (\e complementary).
|
||||
/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode
|
||||
/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned.
|
||||
/// \param vn1 is the first boolean Varnode
|
||||
/// \param vn2 is the second boolean Varnode
|
||||
/// \param depth is the maximum depth to traverse in the evaluation
|
||||
/// \return the correlation class
|
||||
int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||
|
||||
{
|
||||
if (vn1 == vn2) return same;
|
||||
PcodeOp *op1,*op2;
|
||||
OpCode opc1,opc2;
|
||||
if (vn1->isWritten()) {
|
||||
op1 = vn1->getDef();
|
||||
opc1 = op1->code();
|
||||
if (opc1 == CPUI_BOOL_NEGATE) {
|
||||
int res = evaluate(op1->getIn(0),vn2,depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else {
|
||||
op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE
|
||||
opc1 = CPUI_MAX;
|
||||
}
|
||||
if (vn2->isWritten()) {
|
||||
op2 = vn2->getDef();
|
||||
opc2 = op2->code();
|
||||
if (opc2 == CPUI_BOOL_NEGATE) {
|
||||
int4 res = evaluate(vn1,op2->getIn(0),depth);
|
||||
if (res == same) // Flip same <-> complementary result
|
||||
res = complementary;
|
||||
else if (res == complementary)
|
||||
res = same;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
return uncorrelated;
|
||||
if (op1 == (PcodeOp *)0)
|
||||
return uncorrelated;
|
||||
if (!op1->isBoolOutput() || !op2->isBoolOutput())
|
||||
return uncorrelated;
|
||||
if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) {
|
||||
if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) {
|
||||
if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) {
|
||||
int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1);
|
||||
int4 pair2;
|
||||
if (pair1 == uncorrelated) {
|
||||
pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op)
|
||||
if (pair1 == uncorrelated)
|
||||
return uncorrelated;
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1);
|
||||
}
|
||||
else {
|
||||
pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1);
|
||||
}
|
||||
if (pair2 == uncorrelated)
|
||||
return uncorrelated;
|
||||
if (opc1 == opc2) {
|
||||
if (pair1 == same && pair2 == same)
|
||||
return same;
|
||||
else if (opc1 == CPUI_BOOL_XOR) {
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return same;
|
||||
return complementary;
|
||||
}
|
||||
}
|
||||
else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR
|
||||
if (pair1 == complementary && pair2 == complementary)
|
||||
return complementary; // De Morgan's Law
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Two boolean output ops, compare them directly
|
||||
if (opc1 == opc2) {
|
||||
bool sameOp = true;
|
||||
int numInputs = op1->numInput();
|
||||
for (int i = 0; i < numInputs; ++i){
|
||||
if (!varnodeSame(op1->getIn(i),op2->getIn(i))){
|
||||
sameOp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sameOp){
|
||||
return same;
|
||||
}
|
||||
if (sameOpComplement(op1,op2)) {
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
// Check if the binary ops are complements of one another
|
||||
int4 slot1 = 0;
|
||||
int4 slot2 = 0;
|
||||
bool reorder;
|
||||
if (opc1 != get_booleanflip(opc2,reorder))
|
||||
return uncorrelated;
|
||||
if (reorder) slot2 = 1;
|
||||
if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2)))
|
||||
return uncorrelated;
|
||||
if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2)))
|
||||
return uncorrelated;
|
||||
return complementary;
|
||||
}
|
||||
return uncorrelated;
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue