GP-5934 Fix handling of integer minimum in SBORROW, SLESS rules, add

SCARRY rule.
This commit is contained in:
caheckman 2025-08-18 23:21:42 +00:00
parent c7b125b1c7
commit 8217593e87
17 changed files with 898 additions and 670 deletions

View file

@ -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