exprengine: better checking for uninit variables
This commit is contained in:
parent
6739995e79
commit
33305ef4ec
|
@ -199,7 +199,7 @@ static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngi
|
||||||
return;
|
return;
|
||||||
if (tok->isImpossibleIntValue(0))
|
if (tok->isImpossibleIntValue(0))
|
||||||
return;
|
return;
|
||||||
if (value.isUninit() && value.type != ExprEngine::ValueType::BailoutValue)
|
if (value.isUninit(dataBase) && value.type != ExprEngine::ValueType::BailoutValue)
|
||||||
return;
|
return;
|
||||||
float f = getKnownFloatValue(tok, 0.0f);
|
float f = getKnownFloatValue(tok, 0.0f);
|
||||||
if (f > 0.0f || f < 0.0f)
|
if (f > 0.0f || f < 0.0f)
|
||||||
|
@ -315,7 +315,7 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
|
|
||||||
std::string uninitStructMember;
|
std::string uninitStructMember;
|
||||||
if (const auto* structValue = dynamic_cast<const ExprEngine::StructValue*>(&value)) {
|
if (const auto* structValue = dynamic_cast<const ExprEngine::StructValue*>(&value)) {
|
||||||
uninitStructMember = structValue->getUninitStructMember();
|
uninitStructMember = structValue->getUninitStructMember(dataBase);
|
||||||
|
|
||||||
// uninitialized struct member => is there data copy of struct..
|
// uninitialized struct member => is there data copy of struct..
|
||||||
if (!uninitStructMember.empty()) {
|
if (!uninitStructMember.empty()) {
|
||||||
|
@ -325,10 +325,10 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uninitData = false;
|
bool uninitData = false;
|
||||||
if (!value.isUninit() && uninitStructMember.empty()) {
|
if (!value.isUninit(dataBase) && uninitStructMember.empty()) {
|
||||||
if (Token::Match(tok->astParent(), "[(,]")) {
|
if (Token::Match(tok->astParent(), "[(,]")) {
|
||||||
if (const auto* arrayValue = dynamic_cast<const ExprEngine::ArrayValue*>(&value)) {
|
if (const auto* arrayValue = dynamic_cast<const ExprEngine::ArrayValue*>(&value)) {
|
||||||
uninitData = arrayValue->data.size() >= 1 && arrayValue->data[0].value->isUninit();
|
uninitData = arrayValue->data.size() >= 1 && arrayValue->data[0].value->isUninit(dataBase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +627,7 @@ static void checkFunctionCall(const Token *tok, const ExprEngine::Value &value,
|
||||||
const ExprEngine::ArrayValue &arrayValue = static_cast<const ExprEngine::ArrayValue &>(value);
|
const ExprEngine::ArrayValue &arrayValue = static_cast<const ExprEngine::ArrayValue &>(value);
|
||||||
auto index0 = std::make_shared<ExprEngine::IntRange>("0", 0, 0);
|
auto index0 = std::make_shared<ExprEngine::IntRange>("0", 0, 0);
|
||||||
for (const auto &v: arrayValue.read(index0)) {
|
for (const auto &v: arrayValue.read(index0)) {
|
||||||
if (v.second->isUninit()) {
|
if (v.second->isUninit(dataBase)) {
|
||||||
dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingUninitArg", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument is initialized.", CWE_USE_OF_UNINITIALIZED_VARIABLE, false);
|
dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingUninitArg", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument is initialized.", CWE_USE_OF_UNINITIALIZED_VARIABLE, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,8 @@
|
||||||
#define Z3_VERSION_INT GET_VERSION_INT(Z3_MAJOR_VERSION, Z3_MINOR_VERSION, Z3_BUILD_NUMBER)
|
#define Z3_VERSION_INT GET_VERSION_INT(Z3_MAJOR_VERSION, Z3_MINOR_VERSION, Z3_BUILD_NUMBER)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
constexpr auto MAX_BUFFER_SIZE = ~0UL;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct ExprEngineException {
|
struct ExprEngineException {
|
||||||
ExprEngineException(const Token *tok, const std::string &what) : tok(tok), what(what) {}
|
ExprEngineException(const Token *tok, const std::string &what) : tok(tok), what(what) {}
|
||||||
|
@ -201,9 +203,7 @@ static std::string str(ExprEngine::ValuePtr val)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream ret;
|
return val->name + "=" + std::string(typestr) + "(" + val->getRange() + ")";
|
||||||
ret << val->name << "=" << typestr << "(" << val->getRange() << ")";
|
|
||||||
return ret.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t extfind(const std::string &str, const std::string &what, size_t pos)
|
static size_t extfind(const std::string &str, const std::string &what, size_t pos)
|
||||||
|
@ -341,7 +341,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ifSplit(const Token *tok, unsigned int thenIndex, unsigned int elseIndex) {
|
void ifSplit(const Token *tok, unsigned int thenIndex, unsigned int elseIndex) {
|
||||||
mMap[tok].push_back(std::to_string(thenIndex) + ": Split. Then:" + std::to_string(thenIndex) + " Else:" + std::to_string(elseIndex));
|
mMap[tok].push_back("D" + std::to_string(thenIndex) + ": Split. Then:D" + std::to_string(thenIndex) + " Else:D" + std::to_string(elseIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -536,7 +536,7 @@ namespace {
|
||||||
return;
|
return;
|
||||||
const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase();
|
const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase();
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << mDataIndex << ":" << "memory:{";
|
s << "D" << mDataIndex << ":" << "memory:{";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto mem : memory) {
|
for (auto mem : memory) {
|
||||||
ExprEngine::ValuePtr value = mem.second;
|
ExprEngine::ValuePtr value = mem.second;
|
||||||
|
@ -1305,6 +1305,32 @@ public:
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool ExprEngine::UninitValue::isUninit(const DataBase *dataBase) const {
|
||||||
|
const Data *data = dynamic_cast<const Data *>(dataBase);
|
||||||
|
if (data->constraints.empty())
|
||||||
|
return true;
|
||||||
|
#ifdef USE_Z3
|
||||||
|
// Check the value against the constraints
|
||||||
|
ExprData exprData;
|
||||||
|
z3::solver solver(exprData.context);
|
||||||
|
try {
|
||||||
|
exprData.addConstraints(solver, data);
|
||||||
|
exprData.addAssertions(solver);
|
||||||
|
return solver.check() == z3::sat;
|
||||||
|
} catch (const z3::exception &exception) {
|
||||||
|
std::cerr << "z3: " << exception << std::endl;
|
||||||
|
return true; // Safe option is to return true
|
||||||
|
} catch (const ExprData::BailoutValueException &) {
|
||||||
|
return true; // Safe option is to return true
|
||||||
|
} catch (const ExprEngineException &) {
|
||||||
|
return true; // Safe option is to return true
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// The value may or may not be uninitialized
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool ExprEngine::IntRange::isEqual(const DataBase *dataBase, int value) const
|
bool ExprEngine::IntRange::isEqual(const DataBase *dataBase, int value) const
|
||||||
{
|
{
|
||||||
if (value < minValue || value > maxValue)
|
if (value < minValue || value > maxValue)
|
||||||
|
@ -2180,7 +2206,7 @@ static ExprEngine::ValuePtr executeCast(const Token *tok, Data &data)
|
||||||
uninitPointer = std::static_pointer_cast<ExprEngine::ArrayValue>(val)->uninitPointer;
|
uninitPointer = std::static_pointer_cast<ExprEngine::ArrayValue>(val)->uninitPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bufferSize = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), 1, ~0UL);
|
auto bufferSize = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), 1, MAX_BUFFER_SIZE);
|
||||||
return std::make_shared<ExprEngine::ArrayValue>(data.getNewSymbolName(), bufferSize, range, true, nullPointer, uninitPointer);
|
return std::make_shared<ExprEngine::ArrayValue>(data.getNewSymbolName(), bufferSize, range, true, nullPointer, uninitPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2241,6 +2267,11 @@ static void streamReadSetValue(const Token *tok, Data &data)
|
||||||
{
|
{
|
||||||
if (!tok || !tok->valueType())
|
if (!tok || !tok->valueType())
|
||||||
return;
|
return;
|
||||||
|
if (tok->varId() > 0 && tok->valueType()->pointer) {
|
||||||
|
const auto oldValue = data.getValue(tok->varId(), tok->valueType(), tok);
|
||||||
|
if (oldValue && (oldValue->isUninit(&data)))
|
||||||
|
call(data.callbacks, tok, oldValue, &data);
|
||||||
|
}
|
||||||
auto rangeValue = getValueRangeFromValueType(tok->valueType(), data);
|
auto rangeValue = getValueRangeFromValueType(tok->valueType(), data);
|
||||||
if (rangeValue)
|
if (rangeValue)
|
||||||
assignExprValue(tok, rangeValue, data);
|
assignExprValue(tok, rangeValue, data);
|
||||||
|
@ -2363,7 +2394,8 @@ static ExprEngine::ValuePtr executeVariable(const Token *tok, Data &data)
|
||||||
|
|
||||||
static ExprEngine::ValuePtr executeKnownMacro(const Token *tok, Data &data)
|
static ExprEngine::ValuePtr executeKnownMacro(const Token *tok, Data &data)
|
||||||
{
|
{
|
||||||
auto val = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), tok->getKnownIntValue(), tok->getKnownIntValue());
|
const auto intval = tok->getKnownIntValue();
|
||||||
|
auto val = std::make_shared<ExprEngine::IntRange>(std::to_string(intval), intval, intval);
|
||||||
call(data.callbacks, tok, val, &data);
|
call(data.callbacks, tok, val, &data);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -2508,6 +2540,9 @@ static std::string execute(const Token *start, const Token *end, Data &data)
|
||||||
if (Token::Match(prev, "[;{}] return|throw"))
|
if (Token::Match(prev, "[;{}] return|throw"))
|
||||||
return data.str();
|
return data.str();
|
||||||
}
|
}
|
||||||
|
while (Token::simpleMatch(tok, "} catch (") && Token::simpleMatch(tok->linkAt(2), ") {")) {
|
||||||
|
tok = tok->linkAt(2)->next()->link();
|
||||||
|
}
|
||||||
if (std::time(nullptr) > stopTime)
|
if (std::time(nullptr) > stopTime)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -2530,9 +2565,15 @@ static std::string execute(const Token *start, const Token *end, Data &data)
|
||||||
return data.str();
|
return data.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(tok, "try"))
|
if (Token::simpleMatch(tok, "try {") && Token::simpleMatch(tok->linkAt(1), "} catch (")) {
|
||||||
// TODO this is a bailout
|
const Token *catchTok = tok->linkAt(1);
|
||||||
throw ExprEngineException(tok, "Unhandled:" + tok->str());
|
while (Token::simpleMatch(catchTok, "} catch (")) {
|
||||||
|
Data catchData(data);
|
||||||
|
catchTok = catchTok->linkAt(2)->next();
|
||||||
|
execute(catchTok, end, catchData);
|
||||||
|
catchTok = catchTok->link();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Variable declaration..
|
// Variable declaration..
|
||||||
if (tok->variable() && tok->variable()->nameToken() == tok) {
|
if (tok->variable() && tok->variable()->nameToken() == tok) {
|
||||||
|
@ -2771,7 +2812,7 @@ static std::string execute(const Token *start, const Token *end, Data &data)
|
||||||
continue;
|
continue;
|
||||||
changedVariables.insert(varid);
|
changedVariables.insert(varid);
|
||||||
auto oldValue = bodyData.getValue(varid, nullptr, nullptr);
|
auto oldValue = bodyData.getValue(varid, nullptr, nullptr);
|
||||||
if (oldValue && oldValue->isUninit())
|
if (oldValue && oldValue->isUninit(&bodyData))
|
||||||
call(bodyData.callbacks, lhs, oldValue, &bodyData);
|
call(bodyData.callbacks, lhs, oldValue, &bodyData);
|
||||||
if (oldValue && oldValue->type == ExprEngine::ValueType::ArrayValue) {
|
if (oldValue && oldValue->type == ExprEngine::ValueType::ArrayValue) {
|
||||||
// Try to assign "any" value
|
// Try to assign "any" value
|
||||||
|
|
|
@ -116,7 +116,8 @@ namespace ExprEngine {
|
||||||
(void)value;
|
(void)value;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual bool isUninit() const {
|
virtual bool isUninit(const DataBase *dataBase) const {
|
||||||
|
(void)dataBase;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,9 +133,7 @@ namespace ExprEngine {
|
||||||
(void)value;
|
(void)value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool isUninit() const OVERRIDE {
|
bool isUninit(const DataBase *dataBase) const OVERRIDE;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IntRange : public Value {
|
class IntRange : public Value {
|
||||||
|
@ -245,9 +244,9 @@ namespace ExprEngine {
|
||||||
return (it == member.end()) ? ValuePtr() : it->second;
|
return (it == member.end()) ? ValuePtr() : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getUninitStructMember() const {
|
std::string getUninitStructMember(const DataBase *dataBase) const {
|
||||||
for (auto memberNameValue: member) {
|
for (auto memberNameValue: member) {
|
||||||
if (memberNameValue.second && memberNameValue.second->isUninit())
|
if (memberNameValue.second && memberNameValue.second->isUninit(dataBase))
|
||||||
return memberNameValue.first;
|
return memberNameValue.first;
|
||||||
}
|
}
|
||||||
return std::string();
|
return std::string();
|
||||||
|
@ -327,7 +326,8 @@ namespace ExprEngine {
|
||||||
bool isEqual(const DataBase * /*dataBase*/, int /*value*/) const OVERRIDE {
|
bool isEqual(const DataBase * /*dataBase*/, int /*value*/) const OVERRIDE {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool isUninit() const OVERRIDE {
|
bool isUninit(const DataBase *dataBase) const OVERRIDE {
|
||||||
|
(void)dataBase;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -359,14 +359,14 @@ private:
|
||||||
ASSERT_EQUALS("1:26: $4=ArrayValue([$3],[:]=$2)\n"
|
ASSERT_EQUALS("1:26: $4=ArrayValue([$3],[:]=$2)\n"
|
||||||
"1:26: $3=IntRange(0:2147483647)\n"
|
"1:26: $3=IntRange(0:2147483647)\n"
|
||||||
"1:26: $2=IntRange(-128:127)\n"
|
"1:26: $2=IntRange(-128:127)\n"
|
||||||
"1:27: 0:memory:{s=($4,[$3],[:]=$2)}\n",
|
"1:27: D0:memory:{s=($4,[$3],[:]=$2)}\n",
|
||||||
trackExecution("void foo() { std::string s; }", &settings));
|
trackExecution("void foo() { std::string s; }", &settings));
|
||||||
|
|
||||||
|
|
||||||
ASSERT_EQUALS("1:52: $4=ArrayValue([$3],[:]=$2)\n"
|
ASSERT_EQUALS("1:52: $4=ArrayValue([$3],[:]=$2)\n"
|
||||||
"1:52: $3=IntRange(0:2147483647)\n"
|
"1:52: $3=IntRange(0:2147483647)\n"
|
||||||
"1:52: $2=IntRange(-128:127)\n"
|
"1:52: $2=IntRange(-128:127)\n"
|
||||||
"1:66: 0:memory:{s=($4,[$3],[:]=$2)}\n",
|
"1:66: D0:memory:{s=($4,[$3],[:]=$2)}\n",
|
||||||
trackExecution("std::string getName(int); void foo() { std::string s = getName(1); }", &settings));
|
trackExecution("std::string getName(int); void foo() { std::string s = getName(1); }", &settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,7 +780,7 @@ private:
|
||||||
ASSERT_EQUALS("2:16: $2:0=IntRange(-2147483648:2147483647)\n"
|
ASSERT_EQUALS("2:16: $2:0=IntRange(-2147483648:2147483647)\n"
|
||||||
"2:20: $1=ArrayValue([10],[:]=$2)\n"
|
"2:20: $1=ArrayValue([10],[:]=$2)\n"
|
||||||
"2:20: $2=IntRange(-2147483648:2147483647)\n"
|
"2:20: $2=IntRange(-2147483648:2147483647)\n"
|
||||||
"2:26: 0:memory:{buf=($1,[10],[:]=$2) x=$2:0}\n",
|
"2:26: D0:memory:{buf=($1,[10],[:]=$2) x=$2:0}\n",
|
||||||
trackExecution(code));
|
trackExecution(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,10 +791,10 @@ private:
|
||||||
" return buf[0][1][2];\n"
|
" return buf[0][1][2];\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS("1:14: $1=IntRange(-2147483648:2147483647)\n"
|
ASSERT_EQUALS("1:14: $1=IntRange(-2147483648:2147483647)\n"
|
||||||
"1:14: 0:memory:{x=$1}\n"
|
"1:14: D0:memory:{x=$1}\n"
|
||||||
"2:7: $2=ArrayValue([3][4][5],[:]=?)\n"
|
"2:7: $2=ArrayValue([3][4][5],[:]=?)\n"
|
||||||
"2:19: 0:memory:{x=$1 buf=($2,[3][4][5],[:]=?)}\n"
|
"2:19: D0:memory:{x=$1 buf=($2,[3][4][5],[:]=?)}\n"
|
||||||
"3:20: 0:memory:{x=$1 buf=($2,[3][4][5],[:]=?,[((20)*($1))+(7)]=10)}\n",
|
"3:20: D0:memory:{x=$1 buf=($2,[3][4][5],[:]=?,[((20)*($1))+(7)]=10)}\n",
|
||||||
trackExecution(code));
|
trackExecution(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,9 +815,9 @@ private:
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS("1:28: $2=ArrayValue([$1],[:]=?,null)\n"
|
ASSERT_EQUALS("1:28: $2=ArrayValue([$1],[:]=?,null)\n"
|
||||||
"1:28: $1=IntRange(1:ffffffffffffffff)\n"
|
"1:28: $1=IntRange(1:ffffffffffffffff)\n"
|
||||||
"1:28: 0:memory:{x=($2,[$1],[:]=?)}\n"
|
"1:28: D0:memory:{x=($2,[$1],[:]=?)}\n"
|
||||||
"2:9: 0:memory:{x=($2,[$1],[:]=?,[0]=2)}\n"
|
"2:9: D0:memory:{x=($2,[$1],[:]=?,[0]=2)}\n"
|
||||||
"3:9: 0:memory:{x=($2,[$1],[:]=?,[0]=1)}\n",
|
"3:9: D0:memory:{x=($2,[$1],[:]=?,[0]=1)}\n",
|
||||||
trackExecution(code));
|
trackExecution(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,7 +903,7 @@ private:
|
||||||
ASSERT_EQUALS("1:36: $3=ArrayValue([$2],[:]=bailout,null)\n"
|
ASSERT_EQUALS("1:36: $3=ArrayValue([$2],[:]=bailout,null)\n"
|
||||||
"1:36: $2=IntRange(1:2147483647)\n"
|
"1:36: $2=IntRange(1:2147483647)\n"
|
||||||
"1:36: bailout=BailoutValue(bailout)\n"
|
"1:36: bailout=BailoutValue(bailout)\n"
|
||||||
"1:46: 0:memory:{p=($3,[$2],[:]=bailout)}\n",
|
"1:46: D0:memory:{p=($3,[$2],[:]=bailout)}\n",
|
||||||
trackExecution("char *foo(int); void bar() { char *p = foo(1); }"));
|
trackExecution("char *foo(int); void bar() { char *p = foo(1); }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue