ExprEngine: ConditionalValues, output symbolic expressions

This commit is contained in:
Daniel Marjamäki 2019-09-26 10:03:58 +02:00
parent c2a6053d7f
commit c5302d20a3
3 changed files with 105 additions and 91 deletions

View File

@ -66,12 +66,6 @@ namespace {
return; return;
if (!value) if (!value)
map[tok].push_back(tok->expressionString() + "=TODO_NO_VALUE"); map[tok].push_back(tok->expressionString() + "=TODO_NO_VALUE");
/*
else if (value->name[0] == '$')
map[tok].push_back(tok->expressionString() + "=(" + value->name + "," + value->getRange() + ")");
else
map[tok].push_back(tok->expressionString() + "=" + value->name);
*/
} }
void state(const Token *tok, const std::string &s) { void state(const Token *tok, const std::string &s) {
@ -224,7 +218,7 @@ namespace {
if (!value) if (!value)
s << "(null)"; s << "(null)";
else if (value->name[0] == '$') else if (value->name[0] == '$')
s << "(" << value->name << "," << value->getRange() << ")"; s << "(" << value->name << "," << value->getSymbolicExpression() << ")";
else else
s << value->name; s << value->name;
} }
@ -268,14 +262,14 @@ static ExprEngine::ValuePtr simplifyValue(ExprEngine::ValuePtr origValue)
ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value) ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value)
: Value(name) : Value(name, ExprEngine::ValueType::ArrayValue)
, size(size) , size(size)
{ {
assign(ExprEngine::ValuePtr(), value); assign(ExprEngine::ValuePtr(), value);
} }
ExprEngine::ArrayValue::ArrayValue(const std::string &name, const Variable *var) ExprEngine::ArrayValue::ArrayValue(const std::string &name, const Variable *var)
: Value(name) : Value(name, ExprEngine::ValueType::ArrayValue)
{ {
if (var) { if (var) {
int sz = 1; int sz = 1;
@ -315,8 +309,7 @@ static bool isEqual(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2)
{ {
if (!v1 || !v2) if (!v1 || !v2)
return !v1 && !v2; return !v1 && !v2;
// TODO: Maybe we need better logic here: return v1->name == v2->name;
return v1->getRange() == v2->getRange();
} }
static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2)
@ -330,18 +323,19 @@ static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2)
return false; return false;
} }
std::vector<ExprEngine::ValuePtr> ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) ExprEngine::ConditionalValue::Vector ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) const
{ {
std::vector<ExprEngine::ValuePtr> ret; ExprEngine::ConditionalValue::Vector ret;
for (const auto indexAndValue : data) { for (const auto indexAndValue : data) {
if (isEqual(index, indexAndValue.index)) if (isEqual(index, indexAndValue.index))
ret.clear(); ret.clear();
if (isNonOverlapping(index, indexAndValue.index)) if (isNonOverlapping(index, indexAndValue.index))
continue; continue;
if (!indexAndValue.index && indexAndValue.value->type() == ExprEngine::ValueType::StringLiteralValue) { // Array contains string literal data...
if (!indexAndValue.index && indexAndValue.value->type == ExprEngine::ValueType::StringLiteralValue) {
auto stringLiteral = std::dynamic_pointer_cast<ExprEngine::StringLiteralValue>(indexAndValue.value); auto stringLiteral = std::dynamic_pointer_cast<ExprEngine::StringLiteralValue>(indexAndValue.value);
if (!stringLiteral) { if (!stringLiteral) {
ret.push_back(std::make_shared<ExprEngine::IntRange>("", -128, 128)); ret.push_back(std::pair<ValuePtr,ValuePtr>(indexAndValue.index, std::make_shared<ExprEngine::IntRange>("", -128, 128)));
continue; continue;
} }
if (auto i = std::dynamic_pointer_cast<ExprEngine::IntRange>(index)) { if (auto i = std::dynamic_pointer_cast<ExprEngine::IntRange>(index)) {
@ -349,7 +343,7 @@ std::vector<ExprEngine::ValuePtr> ExprEngine::ArrayValue::read(ExprEngine::Value
int c = 0; int c = 0;
if (i->minValue < stringLiteral->size()) if (i->minValue < stringLiteral->size())
c = stringLiteral->string[i->minValue]; c = stringLiteral->string[i->minValue];
ret.push_back(std::make_shared<ExprEngine::IntRange>(std::to_string(c), c, c)); ret.push_back(std::pair<ValuePtr,ValuePtr>(indexAndValue.index, std::make_shared<ExprEngine::IntRange>(std::to_string(c), c, c)));
continue; continue;
} }
} }
@ -360,15 +354,48 @@ std::vector<ExprEngine::ValuePtr> ExprEngine::ArrayValue::read(ExprEngine::Value
else if (c > cmax) else if (c > cmax)
cmax = c; cmax = c;
} }
ret.push_back(std::make_shared<ExprEngine::IntRange>("", cmin, cmax)); ret.push_back(std::pair<ValuePtr,ValuePtr>(indexAndValue.index, std::make_shared<ExprEngine::IntRange>("", cmin, cmax)));
continue; continue;
} }
ret.push_back(indexAndValue.value); ret.push_back(std::pair<ValuePtr,ValuePtr>(indexAndValue.index, indexAndValue.value));
} }
if (ret.size() == 1)
ret[0].first = ExprEngine::ValuePtr();
else if (ret.size() == 2 && !ret[0].first) {
ret[0].first = std::make_shared<ExprEngine::BinOpResult>("!=", index, ret[1].first);
ret[1].first = std::make_shared<ExprEngine::BinOpResult>("==", index, ret[1].first);
} else {
// FIXME!!
ret.clear();
}
return ret; return ret;
} }
std::string ExprEngine::ArrayValue::getRange() const std::string ExprEngine::ConditionalValue::getSymbolicExpression() const
{
std::ostringstream ostr;
ostr << "{";
bool first = true;
for (auto condvalue : values) {
ValuePtr cond = condvalue.first;
ValuePtr value = condvalue.second;
if (!first)
ostr << ",";
first = false;
ostr << "{"
<< (cond ? cond->getSymbolicExpression() : std::string("(null)"))
<< ","
<< value->getSymbolicExpression()
<< "}";
}
ostr << "}";
return ostr.str();
}
std::string ExprEngine::ArrayValue::getSymbolicExpression() const
{ {
std::ostringstream ostr; std::ostringstream ostr;
ostr << "size=" << (size ? size->name : std::string("(null)")); ostr << "size=" << (size ? size->name : std::string("(null)"));
@ -385,7 +412,7 @@ std::string ExprEngine::PointerValue::getRange() const
{ {
std::string r; std::string r;
if (data) if (data)
r = "->" + data->getRange(); r = "->" + data->getSymbolicExpression();
if (null) if (null)
r += std::string(r.empty() ? "" : ",") + "null"; r += std::string(r.empty() ? "" : ",") + "null";
if (uninitData) if (uninitData)
@ -446,9 +473,9 @@ void ExprEngine::BinOpResult::getRange(ExprEngine::BinOpResult::IntOrFloatValue
} }
} }
std::string ExprEngine::IntegerTruncation::getRange() const std::string ExprEngine::IntegerTruncation::getSymbolicExpression() const
{ {
return sign + std::to_string(bits) + "(" + inputValue->getRange() + ")"; return sign + std::to_string(bits) + "(" + inputValue->getSymbolicExpression() + ")";
} }
bool ExprEngine::BinOpResult::isIntValueInRange(int value) const bool ExprEngine::BinOpResult::isIntValueInRange(int value) const
@ -500,6 +527,12 @@ ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluate(int t
BINARY_INT_OP(^) BINARY_INT_OP(^)
BINARY_INT_OP(<<) BINARY_INT_OP(<<)
BINARY_INT_OP(>>) BINARY_INT_OP(>>)
BINARY_INT_OP(==)
BINARY_INT_OP(!=)
BINARY_OP(>=)
BINARY_OP(>)
BINARY_OP(<=)
BINARY_OP(<)
if (binop == "%" && rhs.intValue != 0) { if (binop == "%" && rhs.intValue != 0) {
struct ExprEngine::BinOpResult::IntOrFloatValue result; struct ExprEngine::BinOpResult::IntOrFloatValue result;
@ -531,7 +564,7 @@ ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperan
result.setFloatValue(valueType ? floatRange->minValue : floatRange->maxValue); result.setFloatValue(valueType ? floatRange->minValue : floatRange->maxValue);
return result; return result;
} }
throw std::runtime_error("Internal error: Unhandled value:" + std::to_string((int)value->type())); throw std::runtime_error("Internal error: Unhandled value:" + std::to_string((int)value->type));
} }
// Todo: This is taken from ValueFlow and modified.. we should reuse it // Todo: This is taken from ValueFlow and modified.. we should reuse it
@ -670,7 +703,7 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data)
// Is it array initialization? // Is it array initialization?
const Token *arrayInit = lhsToken->astOperand1(); const Token *arrayInit = lhsToken->astOperand1();
if (arrayInit && arrayInit->variable() && arrayInit->variable()->nameToken() == arrayInit) { if (arrayInit && arrayInit->variable() && arrayInit->variable()->nameToken() == arrayInit) {
if (assignValue->type() == ExprEngine::ValueType::StringLiteralValue) if (assignValue->type == ExprEngine::ValueType::StringLiteralValue)
arrayValue->assign(ExprEngine::ValuePtr(), assignValue); arrayValue->assign(ExprEngine::ValuePtr(), assignValue);
} else { } else {
auto indexValue = executeExpression(lhsToken->astOperand2(), data); auto indexValue = executeExpression(lhsToken->astOperand2(), data);
@ -679,16 +712,16 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data)
} }
} else if (lhsToken->isUnaryOp("*")) { } else if (lhsToken->isUnaryOp("*")) {
auto pval = executeExpression(lhsToken->astOperand1(), data); auto pval = executeExpression(lhsToken->astOperand1(), data);
if (pval && pval->type() == ExprEngine::ValueType::AddressOfValue) { if (pval && pval->type == ExprEngine::ValueType::AddressOfValue) {
auto val = std::dynamic_pointer_cast<ExprEngine::AddressOfValue>(pval); auto val = std::dynamic_pointer_cast<ExprEngine::AddressOfValue>(pval);
if (val) if (val)
data.memory[val->varId] = assignValue; data.memory[val->varId] = assignValue;
} else if (pval && pval->type() == ExprEngine::ValueType::BinOpResult) { } else if (pval && pval->type == ExprEngine::ValueType::BinOpResult) {
auto b = std::dynamic_pointer_cast<ExprEngine::BinOpResult>(pval); auto b = std::dynamic_pointer_cast<ExprEngine::BinOpResult>(pval);
if (b && b->binop == "+") { if (b && b->binop == "+") {
std::shared_ptr<ExprEngine::ArrayValue> arr; std::shared_ptr<ExprEngine::ArrayValue> arr;
ExprEngine::ValuePtr offset; ExprEngine::ValuePtr offset;
if (b->op1->type() == ExprEngine::ValueType::ArrayValue) { if (b->op1->type == ExprEngine::ValueType::ArrayValue) {
arr = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(b->op1); arr = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(b->op1);
offset = b->op2; offset = b->op2;
} else { } else {
@ -743,10 +776,10 @@ static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data)
auto arrayValue = data.getArrayValue(tok->astOperand1()); auto arrayValue = data.getArrayValue(tok->astOperand1());
if (arrayValue) { if (arrayValue) {
auto indexValue = executeExpression(tok->astOperand2(), data); auto indexValue = executeExpression(tok->astOperand2(), data);
auto values = arrayValue->read(indexValue); auto conditionalValues = arrayValue->read(indexValue);
for (auto value: values) for (auto value: conditionalValues)
call(data.callbacks, tok, value); call(data.callbacks, tok, value.second);
return values[0]; // FIXME we need to split the programstate here return std::make_shared<ExprEngine::ConditionalValue>(data.getNewSymbolName(), conditionalValues);
} }
return ExprEngine::ValuePtr(); return ExprEngine::ValuePtr();
} }

View File

@ -58,6 +58,7 @@ namespace ExprEngine {
IntRange, IntRange,
FloatRange, FloatRange,
PointerValue, PointerValue,
ConditionalValue,
ArrayValue, ArrayValue,
StringLiteralValue, StringLiteralValue,
StructValue, StructValue,
@ -71,40 +72,34 @@ namespace ExprEngine {
class Value { class Value {
public: public:
Value(const std::string &name) : name(name) {} Value(const std::string &name, const ValueType type) : name(name), type(type) {}
virtual ~Value() {} virtual ~Value() {}
virtual ValueType type() const = 0; virtual std::string getRange() const {
virtual std::string getRange() const = 0; return name;
}
virtual std::string getSymbolicExpression() const {
return name;
}
virtual bool isIntValueInRange(int value) const { virtual bool isIntValueInRange(int value) const {
(void)value; (void)value;
return false; return false;
} }
const std::string name; const std::string name;
ValueType type;
}; };
class UninitValue: public Value { class UninitValue: public Value {
public: public:
UninitValue() : Value("?") {} UninitValue() : Value("?", ValueType::UninitValue) {}
ValueType type() const override {
return ValueType::UninitValue;
}
std::string getRange() const override {
return "?";
}
}; };
class IntRange : public Value { class IntRange : public Value {
public: public:
IntRange(const std::string &name, int128_t minValue, int128_t maxValue) IntRange(const std::string &name, int128_t minValue, int128_t maxValue)
: Value(name) : Value(name, ValueType::IntRange)
, minValue(minValue) , minValue(minValue)
, maxValue(maxValue) { , maxValue(maxValue) {
} }
~IntRange() OVERRIDE {}
ValueType type() const override {
return ValueType::IntRange;
}
std::string getRange() const override { std::string getRange() const override {
if (minValue == maxValue) if (minValue == maxValue)
return str(minValue); return str(minValue);
@ -121,15 +116,11 @@ namespace ExprEngine {
class FloatRange : public Value { class FloatRange : public Value {
public: public:
FloatRange(const std::string &name, long double minValue, long double maxValue) FloatRange(const std::string &name, long double minValue, long double maxValue)
: Value(name) : Value(name, ValueType::FloatRange)
, minValue(minValue) , minValue(minValue)
, maxValue(maxValue) { , maxValue(maxValue) {
} }
~FloatRange() OVERRIDE {}
ValueType type() const override {
return ValueType::FloatRange;
}
std::string getRange() const override { std::string getRange() const override {
return std::to_string(minValue) + ":" + std::to_string(maxValue); return std::to_string(minValue) + ":" + std::to_string(maxValue);
} }
@ -141,20 +132,28 @@ namespace ExprEngine {
class PointerValue: public Value { class PointerValue: public Value {
public: public:
PointerValue(const std::string &name, ValuePtr data, bool null, bool uninitData) PointerValue(const std::string &name, ValuePtr data, bool null, bool uninitData)
: Value(name) : Value(name, ValueType::PointerValue)
, data(data) , data(data)
, null(null) , null(null)
, uninitData(uninitData) { , uninitData(uninitData) {
} }
ValueType type() const override {
return ValueType::PointerValue;
}
std::string getRange() const override; std::string getRange() const override;
ValuePtr data; ValuePtr data;
bool null; bool null;
bool uninitData; bool uninitData;
}; };
class ConditionalValue : public Value {
public:
typedef std::vector<std::pair<ValuePtr,ValuePtr>> Vector;
ConditionalValue(const std::string &name, const Vector &values) : Value(name, ValueType::ConditionalValue), values(values) {}
std::string getSymbolicExpression() const override;
Vector values;
};
class ArrayValue: public Value { class ArrayValue: public Value {
public: public:
const int MAXSIZE = 0x100000; const int MAXSIZE = 0x100000;
@ -162,14 +161,11 @@ namespace ExprEngine {
ArrayValue(const std::string &name, ValuePtr size, ValuePtr value); ArrayValue(const std::string &name, ValuePtr size, ValuePtr value);
ArrayValue(const std::string &name, const Variable *var); ArrayValue(const std::string &name, const Variable *var);
ValueType type() const override { std::string getSymbolicExpression() const override;
return ValueType::ArrayValue;
}
std::string getRange() const override;
void assign(ValuePtr index, ValuePtr value); void assign(ValuePtr index, ValuePtr value);
void clear(); void clear();
std::vector<ExprEngine::ValuePtr> read(ValuePtr index); ConditionalValue::Vector read(ValuePtr index) const;
struct IndexAndValue { struct IndexAndValue {
ValuePtr index; ValuePtr index;
@ -181,14 +177,8 @@ namespace ExprEngine {
class StringLiteralValue: public Value { class StringLiteralValue: public Value {
public: public:
StringLiteralValue(const std::string &name, const std::string &s) StringLiteralValue(const std::string &name, const std::string &s) : Value(name, ValueType::StringLiteralValue), string(s) {}
: Value(name)
, string(s) {
}
ValueType type() const override {
return ValueType::StringLiteralValue;
}
std::string getRange() const override { std::string getRange() const override {
return "\"" + string + "\""; return "\"" + string + "\"";
} }
@ -201,13 +191,8 @@ namespace ExprEngine {
class StructValue: public Value { class StructValue: public Value {
public: public:
explicit StructValue(const std::string &name) : Value(name) {} explicit StructValue(const std::string &name) : Value(name, ValueType::StructValue) {}
ValueType type() const override {
return ValueType::StructValue;
}
std::string getRange() const override {
return name;
}
ValuePtr getValueOfMember(const std::string &name) const { ValuePtr getValueOfMember(const std::string &name) const {
auto it = member.find(name); auto it = member.find(name);
return (it == member.end()) ? ValuePtr() : it->second; return (it == member.end()) ? ValuePtr() : it->second;
@ -218,15 +203,12 @@ namespace ExprEngine {
class AddressOfValue: public Value { class AddressOfValue: public Value {
public: public:
AddressOfValue(const std::string &name, int varId) AddressOfValue(const std::string &name, int varId)
: Value(name) : Value(name, ValueType::AddressOfValue)
, varId(varId) , varId(varId)
{} {}
ValueType type() const override {
return ValueType::AddressOfValue;
}
std::string getRange() const override { std::string getRange() const override {
return "(&@" + std::to_string(varId); return "&@" + std::to_string(varId);
} }
int varId; int varId;
@ -235,7 +217,7 @@ namespace ExprEngine {
class BinOpResult : public Value { class BinOpResult : public Value {
public: public:
BinOpResult(const std::string &binop, ValuePtr op1, ValuePtr op2) BinOpResult(const std::string &binop, ValuePtr op1, ValuePtr op2)
: Value("(" + op1->name + ")" + binop + "(" + op2->name + ")") : Value("(" + op1->name + ")" + binop + "(" + op2->name + ")", ValueType::BinOpResult)
, binop(binop) , binop(binop)
, op1(op1) , op1(op1)
, op2(op2) { , op2(op2) {
@ -252,9 +234,6 @@ namespace ExprEngine {
mLeafs.insert(op2); mLeafs.insert(op2);
} }
ValueType type() const override {
return ValueType::BinOpResult;
}
std::string getRange() const override; std::string getRange() const override;
struct IntOrFloatValue { struct IntOrFloatValue {
@ -292,16 +271,13 @@ namespace ExprEngine {
class IntegerTruncation : public Value { class IntegerTruncation : public Value {
public: public:
IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign) IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign)
: Value(name) : Value(name, ValueType::IntegerTruncation)
, inputValue(inputValue) , inputValue(inputValue)
, bits(bits) , bits(bits)
, sign(sign) { , sign(sign) {
} }
ValueType type() const override { std::string getSymbolicExpression() const override;
return ValueType::IntegerTruncation;
}
std::string getRange() const override;
ExprEngine::ValuePtr inputValue; ExprEngine::ValuePtr inputValue;
int bits; int bits;

View File

@ -63,6 +63,7 @@ private:
TEST_CASE(localArray1); TEST_CASE(localArray1);
TEST_CASE(localArray2); TEST_CASE(localArray2);
TEST_CASE(localArray3);
TEST_CASE(localArrayInit1); TEST_CASE(localArrayInit1);
TEST_CASE(localArrayInit2); TEST_CASE(localArrayInit2);
TEST_CASE(localArrayUninit); TEST_CASE(localArrayUninit);
@ -96,7 +97,7 @@ private:
} }
void argPointer() { void argPointer() {
ASSERT_EQUALS("->0:255,null,->?", getRange("void f(unsigned char *p) { a = *p; }", "p")); ASSERT_EQUALS("->$1,null,->?", getRange("void f(unsigned char *p) { a = *p; }", "p"));
} }
void argSmartPointer() { void argSmartPointer() {
@ -113,7 +114,7 @@ private:
} }
void dynamicAllocation1() { void dynamicAllocation1() {
ASSERT_EQUALS("size=1,[:]=0", getRange("char *f() { char *p = calloc(1,1); return p; }", "p")); ASSERT_EQUALS("$2", getRange("char *f() { char *p = calloc(1,1); return p; }", "p"));
} }
void expr1() { void expr1() {
@ -203,6 +204,10 @@ private:
ASSERT_EQUALS("0:255", getRange("int f() { unsigned char arr[10] = \"\"; dostuff(arr); return arr[4]; }", "arr[4]")); ASSERT_EQUALS("0:255", getRange("int f() { unsigned char arr[10] = \"\"; dostuff(arr); return arr[4]; }", "arr[4]"));
} }
void localArray3() {
ASSERT_EQUALS("?,43", getRange("int f(unsigned char x) { int arr[10]; arr[4] = 43; int vx = arr[x]; }", "arr[x]"));
}
void localArrayInit1() { void localArrayInit1() {
ASSERT_EQUALS("0", getRange("inf f() { char arr[10] = \"\"; return arr[4]; }", "arr[4]")); ASSERT_EQUALS("0", getRange("inf f() { char arr[10] = \"\"; return arr[4]; }", "arr[4]"));
} }