From c1ff3419a63ac61004d13cbb46fc2efb2dc48f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 23 Sep 2019 20:27:13 +0200 Subject: [PATCH] ExprEngine: Value truncation --- lib/exprengine.cpp | 108 ++++++++++++++++++++++++++++------------ lib/exprengine.h | 32 +++++++++++- test/testexprengine.cpp | 5 ++ 3 files changed, 112 insertions(+), 33 deletions(-) diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index beb113a17..309ce79e0 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -335,6 +335,11 @@ void ExprEngine::BinOpResult::getRange(ExprEngine::BinOpResult::IntOrFloatValue } } +std::string ExprEngine::IntegerTruncation::getRange() const +{ + return sign + std::to_string(bits) + "(" + inputValue->getRange() + ")"; +} + bool ExprEngine::BinOpResult::isIntValueInRange(int value) const { IntOrFloatValue minValue, maxValue; @@ -419,31 +424,46 @@ ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperan } // Todo: This is taken from ValueFlow and modified.. we should reuse it +static int getIntBitsFromValueType(const ValueType *vt, const cppcheck::Platform &platform) +{ + if (!vt) + return 0; + + switch (vt->type) { + case ValueType::Type::BOOL: + return 1; + case ValueType::Type::CHAR: + return platform.char_bit; + case ValueType::Type::SHORT: + return platform.short_bit; + case ValueType::Type::INT: + return platform.int_bit; + case ValueType::Type::LONG: + return platform.long_bit; + case ValueType::Type::LONGLONG: + return platform.long_long_bit; + default: + return 0; + }; +} + static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform) { if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer) return ExprEngine::ValuePtr(); - int bits; + int bits = getIntBitsFromValueType(vt, platform); + if (bits == 1) { + return std::make_shared(name, 0, 1); + } else if (bits > 1) { + if (vt->sign == ValueType::Sign::UNSIGNED) { + return std::make_shared(name, 0, ((int128_t)1 << bits) - 1); + } else { + return std::make_shared(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1); + } + } + switch (vt->type) { - case ValueType::Type::BOOL: - bits = 1; - break; - case ValueType::Type::CHAR: - bits = platform.char_bit; - break; - case ValueType::Type::SHORT: - bits = platform.short_bit; - break; - case ValueType::Type::INT: - bits = platform.int_bit; - break; - case ValueType::Type::LONG: - bits = platform.long_bit; - break; - case ValueType::Type::LONGLONG: - bits = platform.long_long_bit; - break; case ValueType::Type::FLOAT: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); case ValueType::Type::DOUBLE: @@ -453,18 +473,6 @@ static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, default: return ExprEngine::ValuePtr(); }; - - if (bits == 1) { - return std::make_shared(name, 0, 1); - } else { - if (vt->sign == ValueType::Sign::UNSIGNED) { - return std::make_shared(name, 0, ((int128_t)1 << bits) - 1); - } else { - return std::make_shared(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1); - } - } - - return ExprEngine::ValuePtr(); } static void call(const std::vector &callbacks, const Token *tok, ExprEngine::ValuePtr value) @@ -485,6 +493,42 @@ static ExprEngine::ValuePtr executeReturn(const Token *tok, Data &data) return retval; } +static ExprEngine::ValuePtr truncateValue(ExprEngine::ValuePtr val, const ValueType *valueType, Data &data) +{ + if (valueType->pointer != 0) + return val; + if (!valueType->isIntegral()) + return val; // TODO + + int bits = getIntBitsFromValueType(valueType, *data.settings); + if (bits == 0) + // TODO + return val; + + if (auto range = std::dynamic_pointer_cast(val)) { + + if (range->minValue == range->maxValue) { + int128_t newValue = range->minValue; + newValue = newValue & (((int128_t)1 << bits) - 1); + // TODO: Sign extension + if (newValue == range->minValue) + return val; + return std::make_shared(ExprEngine::str(newValue), newValue, newValue); + } + if (auto typeRange = getValueRangeFromValueType("", valueType, *data.settings)) { + auto typeIntRange = std::dynamic_pointer_cast(typeRange); + if (typeIntRange) { + if (range->minValue >= typeIntRange->minValue && range->maxValue <= typeIntRange->maxValue) + return val; + } + } + + return std::make_shared(data.getNewSymbolName(), val, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); + } + // TODO + return val; +} + static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) { ExprEngine::ValuePtr rhsValue = executeExpression(tok->astOperand2(), data); @@ -503,7 +547,7 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) const Token *lhsToken = tok->astOperand1(); data.trackAssignment(lhsToken, rhsValue); if (lhsToken->varId() > 0) { - data.memory[lhsToken->varId()] = rhsValue; + data.memory[lhsToken->varId()] = truncateValue(rhsValue, lhsToken->valueType(), data); } else if (lhsToken->str() == "[") { auto arrayValue = data.getArrayValue(lhsToken->astOperand1()); if (arrayValue) { diff --git a/lib/exprengine.h b/lib/exprengine.h index 23830d897..d4fbfb8b4 100644 --- a/lib/exprengine.h +++ b/lib/exprengine.h @@ -52,7 +52,18 @@ namespace ExprEngine { std::string str(int128_t); // TODO we need to handle floats, containers, pointers, aliases and structs and stuff - enum class ValueType { UninitValue, IntRange, FloatRange, PointerValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult }; + enum class ValueType { + UninitValue, + IntRange, + FloatRange, + PointerValue, + ArrayValue, + StringLiteralValue, + StructValue, + AddressOfValue, + BinOpResult, + IntegerTruncation + }; class Value; typedef std::shared_ptr ValuePtr; @@ -274,6 +285,25 @@ namespace ExprEngine { std::set mLeafs; }; + class IntegerTruncation : public Value { + public: + IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign) + : Value(name) + , inputValue(inputValue) + , bits(bits) + , sign(sign) { + } + + ValueType type() const override { + return ValueType::IntegerTruncation; + } + std::string getRange() const override; + + ExprEngine::ValuePtr inputValue; + int bits; + char sign; + }; + typedef std::function Callback; /** Execute all functions */ diff --git a/test/testexprengine.cpp b/test/testexprengine.cpp index 02207c112..1d118eb70 100644 --- a/test/testexprengine.cpp +++ b/test/testexprengine.cpp @@ -42,6 +42,7 @@ private: TEST_CASE(expr4); TEST_CASE(expr5); TEST_CASE(exprAssign1); + TEST_CASE(exprAssign2); // Truncation TEST_CASE(floatValue1); TEST_CASE(floatValue2); @@ -131,6 +132,10 @@ private: ASSERT_EQUALS("1:256", getRange("void f(unsigned char a) { a += 1; }", "a+=1")); } + void exprAssign2() { + ASSERT_EQUALS("2", getRange("void f(unsigned char x) { x = 258; int a = x }", "a=x")); + } + void floatValue1() { ASSERT_EQUALS(std::to_string(std::numeric_limits::min()) + ":" + std::to_string(std::numeric_limits::max()), getRange("float f; void func() { f=f; }", "f=f")); }