/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "exprengine.h" #include "astutils.h" #include "settings.h" #include "symboldatabase.h" #include "tokenize.h" #include #include #include std::string ExprEngine::str(int128_t value) { std::ostringstream ostr; #ifdef __GNUC__ if (value == (int)value) { ostr << (int) value; return ostr.str(); } if (value < 0) { ostr << "-"; value = -value; } uint64_t high = value >> 64; uint64_t low = value; if (high > 0) ostr << "h" << std::hex << high << "l"; ostr << std::hex << low; #else ostr << value; #endif return ostr.str(); } static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform); namespace { class TrackExecution { public: TrackExecution() : mDataIndex(0) {} std::map> map; int getNewDataIndex() { return mDataIndex++; } void symbolRange(const Token *tok, ExprEngine::ValuePtr value) { if (!tok || !value) return; const std::string &symbolicExpression = value->getSymbolicExpression(); if (symbolicExpression[0] != '$') return; if (mSymbols.find(symbolicExpression) != mSymbols.end()) return; mSymbols.insert(symbolicExpression); map[tok].push_back(symbolicExpression + "=" + value->getRange()); } void state(const Token *tok, const std::string &s) { map[tok].push_back(s); } void print(std::ostream &out) { std::set> locations; for (auto it : map) { locations.insert(std::pair(it.first->linenr(), it.first->column())); } for (const std::pair &loc : locations) { int lineNumber = loc.first; int column = loc.second; for (auto &it : map) { const Token *tok = it.first; if (lineNumber != tok->linenr()) continue; if (column != tok->column()) continue; const std::vector &dumps = it.second; for (const std::string &dump : dumps) out << lineNumber << ":" << column << ": " << dump << "\n"; } } } private: int mDataIndex; std::set mSymbols; }; class Data : public ExprEngine::DataBase { public: Data(int *symbolValueIndex, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, TrackExecution *trackExecution) : DataBase(settings) , symbolValueIndex(symbolValueIndex) , tokenizer(tokenizer) , callbacks(callbacks) , mTrackExecution(trackExecution) , mDataIndex(trackExecution->getNewDataIndex()) {} typedef std::map> Memory; Memory memory; int * const symbolValueIndex; const Tokenizer * const tokenizer; const std::vector &callbacks; void assignValue(const Token *tok, unsigned int varId, ExprEngine::ValuePtr value) { mTrackExecution->symbolRange(tok, value); if (value) { if (auto arr = std::dynamic_pointer_cast(value)) { mTrackExecution->symbolRange(tok, arr->size); for (const auto &indexAndValue: arr->data) mTrackExecution->symbolRange(tok, indexAndValue.value); } else if (auto s = std::dynamic_pointer_cast(value)) { for (const auto &m: s->member) mTrackExecution->symbolRange(tok, m.second); } } memory[varId] = value; } void assignStructMember(const Token *tok, ExprEngine::StructValue *structVal, const std::string &memberName, ExprEngine::ValuePtr value) { mTrackExecution->symbolRange(tok, value); structVal->member[memberName] = value; } std::vector getData(const Token *cond, bool trueData) { std::vector ret; ret.push_back(Data(symbolValueIndex, tokenizer, settings, callbacks, mTrackExecution)); for (Memory::const_iterator mem = memory.cbegin(); mem != memory.cend(); ++mem) { for (Data &data : ret) data.memory[mem->first] = mem->second; if (cond->isComparisonOp() && cond->astOperand1()->varId() == mem->first && cond->astOperand2()->isNumber()) { const int128_t rhsValue = MathLib::toLongNumber(cond->astOperand2()->str()); if (auto intRange = std::dynamic_pointer_cast(mem->second)) { if (cond->str() == ">") { if (trueData) { if (intRange->maxValue <= rhsValue) return std::vector(); auto val = std::make_shared(getNewSymbolName(), rhsValue + 1, intRange->maxValue); ret[0].assignValue(cond, mem->first, val); } else { /* if (!trueData) */ if (intRange->maxValue <= rhsValue) return std::vector(); auto val = std::make_shared(getNewSymbolName(), intRange->minValue, rhsValue); ret[0].assignValue(cond, mem->first, val); } } } } else if (cond->varId() == mem->first) { if (auto intRange = std::dynamic_pointer_cast(mem->second)) { if (trueData) { if (intRange->minValue == 0 && intRange->maxValue == 0) return std::vector(); if (intRange->minValue < 0) { auto val = std::make_shared(getNewSymbolName(), intRange->minValue, -1); ret[0].assignValue(cond, mem->first, val); } if (intRange->maxValue > 0) { auto val = std::make_shared(getNewSymbolName(), 1, intRange->maxValue); if (intRange->minValue < 0) { // create additional intrange.. ret.push_back(Data(symbolValueIndex, tokenizer, settings, callbacks, mTrackExecution)); ret.back().memory = ret[0].memory; } ret[0].assignValue(cond, mem->first, val); } } else { /* if (!trueData) */ if (intRange->maxValue < 0 || intRange->minValue > 0) return std::vector(); auto val = std::make_shared(getNewSymbolName(), 0, 0); ret[0].assignValue(cond, mem->first, val); } } } } return ret; } std::string getNewSymbolName() override { return "$" + std::to_string(++(*symbolValueIndex)); } std::shared_ptr getArrayValue(const Token *tok) { const Memory::iterator it = memory.find(tok->varId()); if (it != memory.end()) return std::dynamic_pointer_cast(it->second); if (tok->varId() == 0) return std::shared_ptr(); auto val = std::make_shared(this, tok->variable()); assignValue(tok, tok->varId(), val); return val; } ExprEngine::ValuePtr getValue(unsigned int varId, const ValueType *valueType, const Token *tok) { const Memory::const_iterator it = memory.find(varId); if (it != memory.end()) return it->second; if (!valueType) return ExprEngine::ValuePtr(); ExprEngine::ValuePtr value = getValueRangeFromValueType(getNewSymbolName(), valueType, *settings); if (value) { assignValue(tok, varId, value); } return value; } void trackProgramState(const Token *tok) { if (memory.empty()) return; const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); std::ostringstream s; s << "{"; // << mDataIndex << ":"; for (auto mem : memory) { ExprEngine::ValuePtr value = mem.second; const Variable *var = symbolDatabase->getVariableFromVarId(mem.first); if (!var) continue; s << " " << var->name() << "="; if (!value) s << "(null)"; else if (value->name[0] == '$' && value->getSymbolicExpression() != value->name) s << "(" << value->name << "," << value->getSymbolicExpression() << ")"; else s << value->name; } s << "}"; mTrackExecution->state(tok, s.str()); } private: TrackExecution * const mTrackExecution; const int mDataIndex; }; } static ExprEngine::ValuePtr simplifyValue(ExprEngine::ValuePtr origValue) { auto b = std::dynamic_pointer_cast(origValue); if (!b) return origValue; if (!b->op1 || !b->op2) return origValue; auto intRange1 = std::dynamic_pointer_cast(b->op1); auto intRange2 = std::dynamic_pointer_cast(b->op1); if (intRange1 && intRange2 && intRange1->minValue == intRange1->maxValue && intRange2->minValue == intRange2->maxValue) { const std::string &binop = b->binop; int128_t v; if (binop == "+") v = intRange1->minValue + intRange2->minValue; else if (binop == "-") v = intRange1->minValue - intRange2->minValue; else if (binop == "*") v = intRange1->minValue * intRange2->minValue; else if (binop == "/" && intRange2->minValue != 0) v = intRange1->minValue / intRange2->minValue; else if (binop == "%" && intRange2->minValue != 0) v = intRange1->minValue % intRange2->minValue; else return origValue; return std::make_shared(ExprEngine::str(v), v, v); } return origValue; } static ExprEngine::ValuePtr translateUninitValueToRange(ExprEngine::ValuePtr value, const ::ValueType *valueType, Data &data) { if (!value) return value; if (value->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } if (auto conditionalValue = std::dynamic_pointer_cast(value)) { if (conditionalValue->values.size() == 1 && conditionalValue->values[0].second && conditionalValue->values[0].second->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } } return value; } static int128_t truncateInt(int128_t value, int bits, char sign) { value = value & (((int128_t)1 << bits) - 1); // Sign extension if (sign == 's' && value & (1ULL << (bits - 1))) value |= ~(((int128_t)1 << bits) - 1); return value; } ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value) : Value(name, ExprEngine::ValueType::ArrayValue) , size(size) { assign(ExprEngine::ValuePtr(), value); } ExprEngine::ArrayValue::ArrayValue(DataBase *data, const Variable *var) : Value(data->getNewSymbolName(), ExprEngine::ValueType::ArrayValue) { if (var) { int sz = 1; for (const auto &dim : var->dimensions()) { if (!dim.known) { sz = -1; break; } sz *= dim.num; } if (sz >= 1) size = std::make_shared(std::to_string(sz), sz, sz); } ValuePtr val; if (!var->isGlobal() && !var->isStatic()) val = std::make_shared(); else { if (var->valueType()) { ::ValueType vt(*var->valueType()); vt.pointer = 0; val = getValueRangeFromValueType(data->getNewSymbolName(), &vt, *data->settings); } } assign(ExprEngine::ValuePtr(), val); } void ExprEngine::ArrayValue::assign(ExprEngine::ValuePtr index, ExprEngine::ValuePtr value) { if (!index) data.clear(); if (value) { ExprEngine::ArrayValue::IndexAndValue indexAndValue = {index, value}; data.push_back(indexAndValue); } } void ExprEngine::ArrayValue::clear() { data.clear(); ExprEngine::ArrayValue::IndexAndValue indexAndValue = { ExprEngine::ValuePtr(), std::make_shared("0", 0, 0) }; data.push_back(indexAndValue); } static bool isEqual(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return !v1 && !v2; return v1->name == v2->name; } static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return false; // Don't know! auto intRange1 = std::dynamic_pointer_cast(v1); auto intRange2 = std::dynamic_pointer_cast(v2); if (intRange1 && intRange2 && (intRange1->minValue > intRange2->maxValue || intRange1->maxValue < intRange2->maxValue)) return true; return false; } ExprEngine::ConditionalValue::Vector ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) const { ExprEngine::ConditionalValue::Vector ret; for (const auto indexAndValue : data) { if (isEqual(index, indexAndValue.index)) ret.clear(); if (isNonOverlapping(index, indexAndValue.index)) continue; // Array contains string literal data... if (!indexAndValue.index && indexAndValue.value->type == ExprEngine::ValueType::StringLiteralValue) { auto stringLiteral = std::dynamic_pointer_cast(indexAndValue.value); if (!stringLiteral) { ret.push_back(std::pair(indexAndValue.index, std::make_shared("", -128, 128))); continue; } if (auto i = std::dynamic_pointer_cast(index)) { if (stringLiteral && i->minValue >= 0 && i->minValue == i->maxValue) { int c = 0; if (i->minValue < stringLiteral->size()) c = stringLiteral->string[i->minValue]; ret.push_back(std::pair(indexAndValue.index, std::make_shared(std::to_string(c), c, c))); continue; } } int cmin = 0, cmax = 0; for (char c : stringLiteral->string) { if (c < cmin) cmin = c; else if (c > cmax) cmax = c; } ret.push_back(std::pair(indexAndValue.index, std::make_shared("", cmin, cmax))); continue; } ret.push_back(std::pair(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("!=", index, ret[1].first); ret[1].first = std::make_shared("==", index, ret[1].first); } else { // FIXME!! ret.clear(); } return ret; } 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; ostr << "size=" << (size ? size->name : std::string("(null)")); for (const auto indexAndValue : data) { ostr << ",[" << (!indexAndValue.index ? std::string(":") : indexAndValue.index->name) << "]=" << indexAndValue.value->name; } return ostr.str(); } std::string ExprEngine::StructValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "{"; bool first = true; for (const auto& m: member) { const std::string &memberName = m.first; auto memberValue = m.second; if (!first) ostr << ","; first = false; ostr << memberName << "=" << (memberValue ? memberValue->getSymbolicExpression() : std::string("(null)")); } ostr << "}"; return ostr.str(); } std::string ExprEngine::PointerValue::getRange() const { std::string r; if (data) r = "->" + data->getSymbolicExpression(); if (null) r += std::string(r.empty() ? "" : ",") + "null"; if (uninitData) r += std::string(r.empty() ? "" : ",") + "->?"; return r; } std::string ExprEngine::BinOpResult::getRange() const { IntOrFloatValue minValue, maxValue; getRange(&minValue, &maxValue); const std::string s1 = minValue.isFloat() ? std::to_string(minValue.floatValue) : str(minValue.intValue); const std::string s2 = maxValue.isFloat() ? std::to_string(maxValue.floatValue) : str(maxValue.intValue); if (s1 == s2) return s1; return s1 + ":" + s2; } void ExprEngine::BinOpResult::getRange(ExprEngine::BinOpResult::IntOrFloatValue *minValue, ExprEngine::BinOpResult::IntOrFloatValue *maxValue) const { std::map valueBit; // Assign a bit number for each leaf int bit = 0; for (ValuePtr v : mLeafs) { if (auto intRange = std::dynamic_pointer_cast(v)) { if (intRange->minValue == intRange->maxValue) { valueBit[v] = 30; continue; } } valueBit[v] = bit++; } if (bit > 24) throw std::runtime_error("Internal error: bits"); for (int test = 0; test < (1 << bit); ++test) { auto result = evaluate(test, valueBit); if (test == 0) *minValue = *maxValue = result; else if (result.isFloat()) { if (result.floatValue < minValue->floatValue) *minValue = result; else if (result.floatValue > maxValue->floatValue) *maxValue = result; } else { if (result.intValue < minValue->intValue) *minValue = result; else if (result.intValue > maxValue->intValue) *maxValue = result; } } } std::string ExprEngine::IntegerTruncation::getSymbolicExpression() const { return sign + std::to_string(bits) + "(" + inputValue->getSymbolicExpression() + ")"; } bool ExprEngine::BinOpResult::isIntValueInRange(int value) const { IntOrFloatValue minValue, maxValue; getRange(&minValue, &maxValue); return value >= minValue.intValue && value <= maxValue.intValue; } #define BINARY_OP(OP) \ if (binop == #OP) { \ struct ExprEngine::BinOpResult::IntOrFloatValue result(lhs); \ if (lhs.isFloat()) \ { result.type = lhs.type; result.floatValue = lhs.floatValue OP (rhs.isFloat() ? rhs.floatValue : rhs.intValue); } \ else if (rhs.isFloat()) \ { result.type = rhs.type; result.floatValue = lhs.intValue OP rhs.floatValue; } \ else { result.type = lhs.type; result.intValue = lhs.intValue OP rhs.intValue; } \ return result; \ } #define BINARY_RELATIONAL_COMPARISON(OP) \ if (binop == #OP) { \ struct ExprEngine::BinOpResult::IntOrFloatValue result(lhs); \ if (lhs.isFloat()) \ { result.setIntValue(lhs.floatValue OP (rhs.isFloat() ? rhs.floatValue : rhs.intValue)); } \ else if (rhs.isFloat()) \ { result.setIntValue(lhs.intValue OP rhs.floatValue); } \ else { result.setIntValue(lhs.intValue OP rhs.intValue); } \ return result; \ } #define BINARY_EQ_COMPARISON(OP) \ if (binop == #OP && !lhs.isFloat() && !rhs.isFloat()) { \ struct ExprEngine::BinOpResult::IntOrFloatValue result; \ result.setIntValue(lhs.intValue OP rhs.intValue); \ return result; \ } #define BINARY_INT_OP(OP) \ if (binop == #OP) { \ struct ExprEngine::BinOpResult::IntOrFloatValue result; \ result.setIntValue(lhs.intValue OP rhs.intValue); \ return result; \ } #define BINARY_OP_DIV(OP) \ if (binop == #OP) { \ struct ExprEngine::BinOpResult::IntOrFloatValue result(lhs); \ if (lhs.isFloat()) \ { result.type = lhs.type; result.floatValue = lhs.floatValue OP (rhs.isFloat() ? rhs.floatValue : rhs.intValue); } \ else if (rhs.isFloat()) \ { result.type = rhs.type; result.floatValue = lhs.intValue OP rhs.floatValue; } \ else if (rhs.intValue != 0) { result.type = lhs.type; result.intValue = lhs.intValue OP rhs.intValue; } \ return result; \ } ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluate(int test, const std::map &valueBit) const { const ExprEngine::BinOpResult::IntOrFloatValue lhs = evaluateOperand(test, valueBit, op1); const ExprEngine::BinOpResult::IntOrFloatValue rhs = evaluateOperand(test, valueBit, op2); BINARY_OP(+) BINARY_OP(-) BINARY_OP(*) BINARY_OP_DIV(/) BINARY_INT_OP(&) BINARY_INT_OP(|) BINARY_INT_OP(^) BINARY_INT_OP(<<) BINARY_INT_OP(>>) BINARY_EQ_COMPARISON(==) BINARY_EQ_COMPARISON(!=) BINARY_RELATIONAL_COMPARISON(>=) BINARY_RELATIONAL_COMPARISON(>) BINARY_RELATIONAL_COMPARISON(<=) BINARY_RELATIONAL_COMPARISON(<) if (binop == "%" && rhs.intValue != 0) { struct ExprEngine::BinOpResult::IntOrFloatValue result; result.setIntValue(lhs.intValue % rhs.intValue); return result; } throw std::runtime_error("Internal error: Unhandled operator;" + binop); } ExprEngine::BinOpResult::IntOrFloatValue ExprEngine::BinOpResult::evaluateOperand(int test, const std::map &valueBit, ExprEngine::ValuePtr value) const { if (!value) throw std::runtime_error("Internal error: null value"); auto binOpResult = std::dynamic_pointer_cast(value); if (binOpResult) return binOpResult->evaluate(test, valueBit); auto it = valueBit.find(value); if (it == valueBit.end()) throw std::runtime_error("Internal error: valueBit not set properly"); bool valueType = test & (1 << it->second); if (auto intRange = std::dynamic_pointer_cast(value)) { ExprEngine::BinOpResult::IntOrFloatValue result; result.setIntValue(valueType ? intRange->minValue : intRange->maxValue); return result; } if (auto floatRange = std::dynamic_pointer_cast(value)) { ExprEngine::BinOpResult::IntOrFloatValue result; result.setFloatValue(valueType ? floatRange->minValue : floatRange->maxValue); return result; } if (auto integerTruncation = std::dynamic_pointer_cast(value)) { ExprEngine::BinOpResult::IntOrFloatValue result(evaluateOperand(test, valueBit, integerTruncation->inputValue)); if (result.isFloat()) result.setIntValue(truncateInt(result.floatValue, integerTruncation->bits, integerTruncation->sign)); else result.setIntValue(truncateInt(result.intValue, integerTruncation->bits, integerTruncation->sign)); return result; } 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 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 = 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::FLOAT: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); case ValueType::Type::DOUBLE: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); case ValueType::Type::LONGDOUBLE: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); default: return ExprEngine::ValuePtr(); }; } static void call(const std::vector &callbacks, const Token *tok, ExprEngine::ValuePtr value) { if (value) { for (ExprEngine::Callback f : callbacks) { f(tok, *value); } } } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data); static ExprEngine::ValuePtr executeReturn(const Token *tok, Data &data) { ExprEngine::ValuePtr retval = executeExpression(tok->astOperand1(), data); call(data.callbacks, tok, retval); return retval; } static ExprEngine::ValuePtr truncateValue(ExprEngine::ValuePtr val, const ValueType *valueType, Data &data) { if (!valueType) return val; 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 = truncateInt(range->minValue, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); 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); ExprEngine::ValuePtr assignValue; if (tok->str() == "=") assignValue = rhsValue; else { // "+=" => "+" std::string binop(tok->str()); binop = binop.substr(0, binop.size() - 1); ExprEngine::ValuePtr lhsValue = executeExpression(tok->astOperand1(), data); assignValue = simplifyValue(std::make_shared(binop, lhsValue, rhsValue)); } const Token *lhsToken = tok->astOperand1(); assignValue = truncateValue(assignValue, lhsToken->valueType(), data); call(data.callbacks, tok, assignValue); if (lhsToken->varId() > 0) { data.assignValue(lhsToken, lhsToken->varId(), assignValue); } else if (lhsToken->str() == "[") { auto arrayValue = data.getArrayValue(lhsToken->astOperand1()); if (arrayValue) { // Is it array initialization? const Token *arrayInit = lhsToken->astOperand1(); if (arrayInit && arrayInit->variable() && arrayInit->variable()->nameToken() == arrayInit) { if (assignValue->type == ExprEngine::ValueType::StringLiteralValue) arrayValue->assign(ExprEngine::ValuePtr(), assignValue); } else { auto indexValue = executeExpression(lhsToken->astOperand2(), data); arrayValue->assign(indexValue, assignValue); } } } else if (lhsToken->isUnaryOp("*")) { auto pval = executeExpression(lhsToken->astOperand1(), data); if (pval && pval->type == ExprEngine::ValueType::AddressOfValue) { auto val = std::dynamic_pointer_cast(pval); if (val) data.assignValue(lhsToken, val->varId, assignValue); } else if (pval && pval->type == ExprEngine::ValueType::BinOpResult) { auto b = std::dynamic_pointer_cast(pval); if (b && b->binop == "+") { std::shared_ptr arr; ExprEngine::ValuePtr offset; if (b->op1->type == ExprEngine::ValueType::ArrayValue) { arr = std::dynamic_pointer_cast(b->op1); offset = b->op2; } else { arr = std::dynamic_pointer_cast(b->op2); offset = b->op1; } if (arr && offset) { arr->assign(offset, assignValue); } } } } else if (Token::Match(lhsToken, ". %name%")) { auto structVal = executeExpression(lhsToken->astOperand1(), data); if (structVal && structVal->type == ExprEngine::ValueType::StructValue) data.assignStructMember(tok, &*std::static_pointer_cast(structVal), lhsToken->strAt(1), assignValue); } return assignValue; } static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) { std::vector argValues; for (const Token *argtok : getArguments(tok)) { auto val = executeExpression(argtok, data); argValues.push_back(val); if (!argtok->valueType() || (argtok->valueType()->constness & 1) == 1) continue; if (auto arrayValue = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; auto anyVal = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); arrayValue->assign(ExprEngine::ValuePtr(), anyVal); } else if (auto addressOf = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; if (vt.isIntegral() && argtok->valueType()->pointer == 1) data.assignValue(argtok, addressOf->varId, getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings)); } } if (!tok->valueType() && tok->astParent()) throw std::runtime_error("Expression '" + tok->expressionString() + "' has unknown type!"); auto val = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); call(data.callbacks, tok, val); return val; } static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data) { auto arrayValue = data.getArrayValue(tok->astOperand1()); if (arrayValue) { auto indexValue = executeExpression(tok->astOperand2(), data); auto conditionalValues = arrayValue->read(indexValue); for (auto value: conditionalValues) call(data.callbacks, tok, value.second); return std::make_shared(data.getNewSymbolName(), conditionalValues); } // TODO: Pointer value.. executeExpression(tok->astOperand1(), data); executeExpression(tok->astOperand2(), data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeCast(const Token *tok, Data &data) { const Token *expr = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); auto val = executeExpression(expr, data); if (expr->valueType() && expr->valueType()->type == ::ValueType::Type::VOID && expr->valueType()->pointer > 0) { ::ValueType vt(*tok->valueType()); vt.pointer = 0; auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); if (tok->valueType()->pointer == 0) return range; bool uninit = false, null = false; if (val && val->type == ExprEngine::ValueType::PointerValue) { null = std::static_pointer_cast(val)->null; uninit = std::static_pointer_cast(val)->uninitData; } return std::make_shared(data.getNewSymbolName(), range, null, uninit); } if (val) // TODO: Cast this.. return val; return getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); } static ExprEngine::ValuePtr executeDot(const Token *tok, Data &data) { if (!tok->astOperand1() || !tok->astOperand1()->varId()) return ExprEngine::ValuePtr(); std::shared_ptr structValue = std::dynamic_pointer_cast(data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); if (!structValue) { if (tok->originalName() == "->") { std::shared_ptr pointerValue = std::dynamic_pointer_cast(data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); if (pointerValue) { call(data.callbacks, tok->astOperand1(), pointerValue); structValue = std::dynamic_pointer_cast(pointerValue->data); } else { call(data.callbacks, tok->astOperand1(), data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); } } if (!structValue) return ExprEngine::ValuePtr(); } call(data.callbacks, tok->astOperand1(), structValue); return structValue->getValueOfMember(tok->astOperand2()->str()); } static ExprEngine::ValuePtr executeBinaryOp(const Token *tok, Data &data) { ExprEngine::ValuePtr v1 = executeExpression(tok->astOperand1(), data); ExprEngine::ValuePtr v2 = executeExpression(tok->astOperand2(), data); if (v1 && v2) { auto result = simplifyValue(std::make_shared(tok->str(), v1, v2)); call(data.callbacks, tok, result); return result; } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeAddressOf(const Token *tok, Data &data) { auto addr = std::make_shared(data.getNewSymbolName(), tok->astOperand1()->varId()); call(data.callbacks, tok, addr); return addr; } static ExprEngine::ValuePtr executeDeref(const Token *tok, Data &data) { ExprEngine::ValuePtr pval = executeExpression(tok->astOperand1(), data); if (pval) { auto addressOf = std::dynamic_pointer_cast(pval); if (addressOf) { auto val = data.getValue(addressOf->varId, tok->valueType(), tok); call(data.callbacks, tok, val); return val; } auto pointer = std::dynamic_pointer_cast(pval); if (pointer) { auto val = pointer->data; call(data.callbacks, tok, val); return val; } } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeVariable(const Token *tok, Data &data) { auto val = data.getValue(tok->varId(), tok->valueType(), tok); call(data.callbacks, tok, val); return val; } static ExprEngine::ValuePtr executeKnownMacro(const Token *tok, Data &data) { auto val = std::make_shared(data.getNewSymbolName(), tok->getKnownIntValue(), tok->getKnownIntValue()); call(data.callbacks, tok, val); return val; } static ExprEngine::ValuePtr executeNumber(const Token *tok) { if (tok->valueType()->isFloat()) { long double value = MathLib::toDoubleNumber(tok->str()); return std::make_shared(tok->str(), value, value); } int128_t value = MathLib::toLongNumber(tok->str()); return std::make_shared(tok->str(), value, value); } static ExprEngine::ValuePtr executeStringLiteral(const Token *tok, Data &data) { std::string s = tok->str(); return std::make_shared(data.getNewSymbolName(), s.substr(1, s.size()-2)); } static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data) { if (tok->str() == "return") return executeReturn(tok, data); if (tok->isAssignmentOp()) // TODO: Handle more operators return executeAssign(tok, data); if (tok->astOperand1() && tok->astOperand2() && tok->str() == "[") return executeArrayIndex(tok, data); if (tok->str() == "(") { if (!tok->isCast()) return executeFunctionCall(tok, data); return executeCast(tok, data); } if (tok->str() == ".") return executeDot(tok, data); if (tok->astOperand1() && tok->astOperand2()) return executeBinaryOp(tok, data); if (tok->isUnaryOp("&") && Token::Match(tok->astOperand1(), "%var%")) return executeAddressOf(tok, data); if (tok->isUnaryOp("*")) return executeDeref(tok, data); if (tok->varId()) return executeVariable(tok, data); if (tok->isName() && tok->hasKnownIntValue()) return executeKnownMacro(tok, data); if (tok->isNumber() || tok->tokType() == Token::Type::eChar) return executeNumber(tok); if (tok->tokType() == Token::Type::eString) return executeStringLiteral(tok, data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data) { return translateUninitValueToRange(executeExpression1(tok, data), tok->valueType(), data); } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data); static void execute(const Token *start, const Token *end, Data &data) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) data.trackProgramState(tok); if (tok->variable() && tok->variable()->nameToken() == tok) { if (Token::Match(tok, "%varid% ; %varid% =", tok->varId())) { // if variable is not used in assignment rhs then we do not need to create a "confusing" variable value.. bool foundInRhs = false; visitAstNodes(tok->tokAt(3)->astOperand2(), [&](const Token *rhs) { if (rhs->varId()==tok->varId()) { foundInRhs = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!foundInRhs) { tok = tok->tokAt(2); continue; } } if (tok->variable()->isArray()) { data.assignValue(tok, tok->varId(), std::make_shared(&data, tok->variable())); if (Token::Match(tok, "%name% [")) tok = tok->linkAt(1); } else if (Token::Match(tok, "%var% ;")) data.assignValue(tok, tok->varId(), createVariableValue(*tok->variable(), data)); } else if (!tok->astParent() && (tok->astOperand1() || tok->astOperand2())) executeExpression(tok, data); else if (Token::simpleMatch(tok, "if (")) { const Token *cond = tok->next()->astOperand2(); /*const ExprEngine::ValuePtr condValue =*/ executeExpression(cond,data); std::vector trueData = data.getData(cond, true); std::vector falseData = data.getData(cond, false); const Token *thenStart = tok->linkAt(1)->next(); const Token *thenEnd = thenStart->link(); for (Data &d : trueData) execute(thenStart->next(), end, d); if (Token::simpleMatch(thenEnd, "} else {")) { const Token *elseStart = thenEnd->tokAt(2); for (Data &d : falseData) execute(elseStart->next(), end, d); } else { for (Data &d : falseData) execute(thenEnd->next(), end, d); } return; } if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } } void ExprEngine::executeAllFunctions(const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace) { const SymbolDatabase *symbolDatabase = tokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { try { executeFunction(functionScope, tokenizer, settings, callbacks, trace); } catch (const std::exception &e) { // FIXME.. there should not be exceptions std::string functionName = functionScope->function->name(); std::cout << "Verify: Aborted analysis of function '" << functionName << "': " << e.what() << std::endl; } } } static ExprEngine::ValuePtr createStructVal(const Scope *structScope, bool uninitData, Data &data) { if (!structScope) return ExprEngine::ValuePtr(); std::shared_ptr structValue = std::make_shared(data.getNewSymbolName()); auto uninitValue = std::make_shared(); for (const Variable &member : structScope->varlist) { if (uninitData) { if (member.isPointer()) { structValue->member[member.name()] = uninitValue; continue; } if (member.valueType() && member.valueType()->type >= ::ValueType::Type::CHAR) { structValue->member[member.name()] = uninitValue; continue; } } ExprEngine::ValuePtr memberValue = createVariableValue(member, data); if (memberValue) structValue->member[member.name()] = memberValue; } return structValue; } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data) { if (!var.nameToken()) return ExprEngine::ValuePtr(); const ValueType *valueType = var.valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) valueType = var.nameToken()->valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) { // variable with unknown type if (var.isLocal() && var.isPointer() && !var.isArray()) return std::make_shared(); return ExprEngine::ValuePtr(); } if (valueType->pointer > 0) { if (var.isLocal()) return std::make_shared(); ValueType vt(*valueType); vt.pointer = 0; auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); return std::make_shared(data.getNewSymbolName(), range, true, true); } if (var.isArray()) return std::make_shared(&data, &var); if (valueType->isIntegral()) return getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (valueType->type == ValueType::Type::RECORD) return createStructVal(valueType->typeScope, var.isLocal() && !var.isStatic(), data); if (valueType->smartPointerType) { auto structValue = createStructVal(valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data); return std::make_shared(data.getNewSymbolName(), structValue, true, false); } if (valueType->container && valueType->container->stdStringLike) { auto size = std::make_shared(data.getNewSymbolName(), 0, ~0ULL); auto value = std::make_shared(data.getNewSymbolName(), -128, 127); return std::make_shared(data.getNewSymbolName(), size, value); } return ExprEngine::ValuePtr(); } void ExprEngine::executeFunction(const Scope *functionScope, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace) { if (!functionScope->bodyStart) return; const Function *function = functionScope->function; if (!function) return; if (functionScope->bodyStart->fileIndex() > 0) // TODO.. what about functions in headers? return; int symbolValueIndex = 0; TrackExecution trackExecution; Data data(&symbolValueIndex, tokenizer, settings, callbacks, &trackExecution); for (const Variable &arg : function->argumentList) data.assignValue(functionScope->bodyStart, arg.declarationId(), createVariableValue(arg, data)); execute(functionScope->bodyStart, functionScope->bodyEnd, data); if (settings->debugVerification) { // TODO generate better output!! trackExecution.print(trace); } } void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings) { std::function divByZero = [=](const Token *tok, const ExprEngine::Value &value) { if (!Token::Match(tok->astParent(), "[/%]")) return; if (tok->astParent()->astOperand2() == tok && value.isIntValueInRange(0)) { std::list callstack{tok->astParent()}; ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationDivByZero", "There is division, cannot determine that there can't be a division by zero.", CWE(369), false); errorLogger->reportErr(errmsg); } }; std::function nullPointerDereference = [=](const Token *tok, const ExprEngine::Value &value) { if (!tok->astParent()) return; // Is pointer dereferenced? bool deref = false; deref |= tok->astParent()->isUnaryOp("*"); deref |= Token::simpleMatch(tok->astParent(), "["); deref |= Token::simpleMatch(tok->astParent(), ".") && tok == tok->astParent()->astOperand1(); if (!deref) return; // Is this a null pointer value? try { if (auto pointerValue = dynamic_cast(&value)) { if (!pointerValue->null) return; } else if (!value.isIntValueInRange(0)) return; } catch (const std::exception &) { return; } std::list callstack{tok->astParent()}; ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationNullPointerDereference", "There is pointer dereference, cannot determine that the pointer can't be NULL.", CWE(476), false); errorLogger->reportErr(errmsg); }; std::function integerOverflow = [&](const Token *tok, const ExprEngine::Value &value) { if (!tok->isArithmeticalOp() || !tok->valueType() || !tok->valueType()->isIntegral() || tok->valueType()->pointer > 0) return; const ExprEngine::BinOpResult *b = dynamic_cast(&value); if (!b) return; ExprEngine::BinOpResult::IntOrFloatValue minValue,maxValue; b->getRange(&minValue, &maxValue); if (minValue.isFloat() || maxValue.isFloat()) return; int bits = getIntBitsFromValueType(tok->valueType(), *settings); if (bits == 0) return; if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) { int128_t v = (int128_t)1 << (bits - 1); if (minValue.intValue >= -v && maxValue.intValue < v) return; } else { int128_t v = (int128_t)1 << bits; if (minValue.intValue >= 0 && maxValue.intValue < v) return; } std::string note; if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED) note = " Note that unsigned integer overflow is defined and will wrap around."; std::list callstack{tok}; ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationIntegerOverflow", "Integer overflow, " + tok->valueType()->str() + " result." + note, false); errorLogger->reportErr(errmsg); }; std::vector callbacks; callbacks.push_back(divByZero); callbacks.push_back(nullPointerDereference); #ifdef VERIFY_INTEGEROVERFLOW callbacks.push_back(integerOverflow); #endif ExprEngine::executeAllFunctions(tokenizer, settings, callbacks, std::cout); }