ExprEngine: Fix the checking for integer overflows

This commit is contained in:
Daniel Marjamäki 2019-10-08 20:13:25 +02:00
parent 47e9504083
commit 3e50150dbf
2 changed files with 97 additions and 24 deletions

View File

@ -30,6 +30,14 @@
#include <z3++.h> #include <z3++.h>
#endif #endif
namespace {
struct VerifyException {
VerifyException(const Token *tok, const std::string &what) : tok(tok), what(what) {}
const Token *tok;
const std::string what;
};
}
std::string ExprEngine::str(int128_t value) std::string ExprEngine::str(int128_t value)
{ {
std::ostringstream ostr; std::ostringstream ostr;
@ -373,7 +381,7 @@ ExprEngine::ConditionalValue::Vector ExprEngine::ArrayValue::read(ExprEngine::Va
if (!index) if (!index)
return ret; return 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;
@ -591,7 +599,7 @@ struct ExprData {
}; };
#endif #endif
bool ExprEngine::IntRange::isIntValueInRange(DataBase *dataBase, int value) const bool ExprEngine::IntRange::isEqual(DataBase *dataBase, int value) const
{ {
if (value < minValue || value > maxValue) if (value < minValue || value > maxValue)
return false; return false;
@ -620,7 +628,7 @@ bool ExprEngine::IntRange::isIntValueInRange(DataBase *dataBase, int value) cons
#endif #endif
} }
bool ExprEngine::BinOpResult::isIntValueInRange(ExprEngine::DataBase *dataBase, int value) const bool ExprEngine::BinOpResult::isEqual(ExprEngine::DataBase *dataBase, int value) const
{ {
#ifdef USE_Z3 #ifdef USE_Z3
ExprData exprData; ExprData exprData;
@ -638,6 +646,42 @@ bool ExprEngine::BinOpResult::isIntValueInRange(ExprEngine::DataBase *dataBase,
#endif #endif
} }
bool ExprEngine::BinOpResult::isGreaterThan(ExprEngine::DataBase *dataBase, int value) const
{
#ifdef USE_Z3
ExprData exprData;
z3::solver solver(exprData.context);
z3::expr e = exprData.getExpr(this);
exprData.addAssertions(solver);
for (auto constraint : dynamic_cast<const Data *>(dataBase)->constraints)
solver.add(exprData.getConstraintExpr(constraint));
solver.add(e > value);
return solver.check() == z3::sat;
#else
(void)dataBase;
(void)value;
return false;
#endif
}
bool ExprEngine::BinOpResult::isLessThan(ExprEngine::DataBase *dataBase, int value) const
{
#ifdef USE_Z3
ExprData exprData;
z3::solver solver(exprData.context);
z3::expr e = exprData.getExpr(this);
exprData.addAssertions(solver);
for (auto constraint : dynamic_cast<const Data *>(dataBase)->constraints)
solver.add(exprData.getConstraintExpr(constraint));
solver.add(e < value);
return solver.check() == z3::sat;
#else
(void)dataBase;
(void)value;
return false;
#endif
}
std::string ExprEngine::BinOpResult::getExpr(ExprEngine::DataBase *dataBase) const std::string ExprEngine::BinOpResult::getExpr(ExprEngine::DataBase *dataBase) const
{ {
#ifdef USE_Z3 #ifdef USE_Z3
@ -849,7 +893,7 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data)
} }
if (!tok->valueType() && tok->astParent()) if (!tok->valueType() && tok->astParent())
throw std::runtime_error("Expression '" + tok->expressionString() + "' has unknown type!"); throw VerifyException(tok, "Expression '" + tok->expressionString() + "' has unknown type!");
auto val = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); auto val = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings);
call(data.callbacks, tok, val, &data); call(data.callbacks, tok, val, &data);
@ -1069,7 +1113,7 @@ static void execute(const Token *start, const Token *end, Data &data)
if (Token::simpleMatch(tok, "try")) if (Token::simpleMatch(tok, "try"))
// TODO this is a bailout // TODO this is a bailout
throw std::runtime_error("Unhandled:" + tok->str()); throw VerifyException(tok, "Unhandled:" + tok->str());
// Variable declaration.. // Variable declaration..
if (tok->variable() && tok->variable()->nameToken() == tok) { if (tok->variable() && tok->variable()->nameToken() == tok) {
@ -1179,6 +1223,10 @@ void ExprEngine::executeAllFunctions(const Tokenizer *tokenizer, const Settings
for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Scope *functionScope : symbolDatabase->functionScopes) {
try { try {
executeFunction(functionScope, tokenizer, settings, callbacks, trace); executeFunction(functionScope, tokenizer, settings, callbacks, trace);
} catch (const VerifyException &e) {
// FIXME.. there should not be exceptions
std::string functionName = functionScope->function->name();
std::cout << "Verify: Aborted analysis of function '" << functionName << "':" << e.tok->linenr() << ": " << e.what << std::endl;
} catch (const std::exception &e) { } catch (const std::exception &e) {
// FIXME.. there should not be exceptions // FIXME.. there should not be exceptions
std::string functionName = functionScope->function->name(); std::string functionName = functionScope->function->name();
@ -1284,7 +1332,7 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer,
std::function<void(const Token *, const ExprEngine::Value &, ExprEngine::DataBase *)> divByZero = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { std::function<void(const Token *, const ExprEngine::Value &, ExprEngine::DataBase *)> divByZero = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) {
if (!Token::Match(tok->astParent(), "[/%]")) if (!Token::Match(tok->astParent(), "[/%]"))
return; return;
if (tok->astParent()->astOperand2() == tok && value.isIntValueInRange(dataBase, 0)) { if (tok->astParent()->astOperand2() == tok && value.isEqual(dataBase, 0)) {
std::list<const Token*> callstack{tok->astParent()}; std::list<const Token*> 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::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); errorLogger->reportErr(errmsg);
@ -1300,31 +1348,42 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer,
if (!b) if (!b)
return; return;
ExprEngine::BinOpResult::IntOrFloatValue minValue,maxValue;
b->getRange(&minValue, &maxValue);
if (minValue.isFloat() || maxValue.isFloat())
return;
int bits = getIntBitsFromValueType(tok->valueType(), *settings); int bits = getIntBitsFromValueType(tok->valueType(), *settings);
if (bits == 0) if (bits == 0 || bits >= 60)
return; return;
std::string errorMessage;
if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) { if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) {
int128_t v = (int128_t)1 << (bits - 1); MathLib::bigint v = 1LL << (bits - 1);
if (minValue.intValue >= -v && maxValue.intValue < v) if (b->isGreaterThan(dataBase, v))
return; errorMessage = "greater than " + std::to_string(v);
if (b->isLessThan(dataBase, -v)) {
if (!errorMessage.empty())
errorMessage += " or ";
errorMessage += "less than " + std::to_string(v);
}
} else { } else {
int128_t v = (int128_t)1 << bits; MathLib::bigint maxValue = (1LL << bits) - 1;
if (minValue.intValue >= 0 && maxValue.intValue < v) if (b->isGreaterThan(dataBase, maxValue))
return; errorMessage = "greater than " + std::to_string(maxValue);
if (b->isLessThan(dataBase, 0)) {
if (!errorMessage.empty())
errorMessage += " or ";
errorMessage += "less than 0";
}
} }
std::string note; if (errorMessage.empty())
return;
errorMessage = "There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + ").";
if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED) if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED)
note = " Note that unsigned integer overflow is defined and will wrap around."; errorMessage += " Note that unsigned integer overflow is defined and will wrap around.";
std::list<const Token*> callstack{tok}; std::list<const Token*> callstack{tok};
ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationIntegerOverflow", "Integer overflow, " + tok->valueType()->str() + " result." + note, false); ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationIntegerOverflow", errorMessage, false);
errorLogger->reportErr(errmsg); errorLogger->reportErr(errmsg);
}; };
#endif #endif

View File

@ -87,11 +87,22 @@ namespace ExprEngine {
virtual std::string getSymbolicExpression() const { virtual std::string getSymbolicExpression() const {
return name; return name;
} }
virtual bool isIntValueInRange(DataBase *dataBase, int value) const { virtual bool isEqual(DataBase *dataBase, int value) const {
(void)dataBase; (void)dataBase;
(void)value; (void)value;
return false; return false;
} }
virtual bool isGreaterThan(DataBase *dataBase, int value) const {
(void)dataBase;
(void)value;
return false;
}
virtual bool isLessThan(DataBase *dataBase, int value) const {
(void)dataBase;
(void)value;
return false;
}
const std::string name; const std::string name;
ValueType type; ValueType type;
}; };
@ -113,7 +124,7 @@ namespace ExprEngine {
return str(minValue); return str(minValue);
return str(minValue) + ":" + str(maxValue); return str(minValue) + ":" + str(maxValue);
} }
bool isIntValueInRange(DataBase *dataBase, int value) const override; bool isEqual(DataBase *dataBase, int value) const override;
int128_t minValue; int128_t minValue;
int128_t maxValue; int128_t maxValue;
@ -231,7 +242,10 @@ namespace ExprEngine {
, op2(op2) { , op2(op2) {
} }
bool isIntValueInRange(DataBase *dataBase, int value) const; bool isEqual(DataBase *dataBase, int value) const override;
bool isGreaterThan(DataBase *dataBase, int value) const override;
virtual bool isLessThan(DataBase *dataBase, int value) const override;
std::string getExpr(DataBase *dataBase) const; std::string getExpr(DataBase *dataBase) const;
std::string binop; std::string binop;