Track reading aliases during valueflow forward

This commit is contained in:
Paul 2020-07-19 23:25:35 -05:00
parent 4f83b3618c
commit e2a81a382f
6 changed files with 123 additions and 17 deletions

View File

@ -1429,9 +1429,16 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings *settings,
if (!tok)
return false;
const Token *tok2 = tok;
while (Token::simpleMatch(tok2->astParent(), "*") || (Token::simpleMatch(tok2->astParent(), ".") && !Token::simpleMatch(tok2->astParent()->astParent(), "(")) ||
(Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1()))
int derefs = 0;
while (Token::simpleMatch(tok2->astParent(), "*") ||
(Token::simpleMatch(tok2->astParent(), ".") && !Token::simpleMatch(tok2->astParent()->astParent(), "(")) ||
(Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1())) {
if (Token::simpleMatch(tok2->astParent(), "*") || tok2->astParent()->originalName() == "->")
derefs++;
if (derefs > indirect)
break;
tok2 = tok2->astParent();
}
while (Token::simpleMatch(tok2->astParent(), "?") || (Token::simpleMatch(tok2->astParent(), ":") && Token::simpleMatch(tok2->astParent()->astParent(), "?")))
tok2 = tok2->astParent();
@ -1513,6 +1520,11 @@ bool isVariableChanged(const Token *start, const Token *end, const nonneg int va
return findVariableChanged(start, end, 0, varid, globalvar, settings, cpp, depth) != nullptr;
}
bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth)
{
return findVariableChanged(start, end, indirect, varid, globalvar, settings, cpp, depth) != nullptr;
}
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth)
{
if (!precedes(start, end))

View File

@ -182,6 +182,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
/** Is variable changed in block of code? */
bool isVariableChanged(const Token *start, const Token *end, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth = 20);

View File

@ -274,7 +274,9 @@ void CheckAutoVariables::autoVariables()
errorInvalidDeallocation(tok, nullptr);
else if (tok && tok->variable() && tok->variable()->isPointer()) {
for (const ValueFlow::Value &v : tok->values()) {
if (v.isTokValue() && isArrayVar(v.tokvalue)) {
if (!(v.isTokValue()))
continue;
if (isArrayVar(v.tokvalue)) {
errorInvalidDeallocation(tok, &v);
break;
}

View File

@ -424,7 +424,10 @@ static std::list<std::pair<const Token *, MathLib::bigint>> getUnsafeFunction(co
tok2 = tok2->linkAt(1);
if (Token::findmatch(tok2->link(), "return|throw", tok2))
return ret;
if (isVariableChanged(tok2->link(), tok2, argvar->declarationId(), false, settings, tokenizer->isCPP()))
int indirect = 0;
if(argvar->valueType())
indirect = argvar->valueType()->pointer;
if (isVariableChanged(tok2->link(), tok2, indirect, argvar->declarationId(), false, settings, tokenizer->isCPP()))
return ret;
}
if (Token::Match(tok2, "%oror%|&&|?")) {

View File

@ -1256,7 +1256,9 @@ static void valueFlowPointerAliasDeref(TokenList *tokenlist)
if (!var->isConst() && isVariableChanged(lifeTok->next(), tok, lifeTok->varId(), !var->isLocal(), tokenlist->getSettings(), tokenlist->isCPP()))
continue;
for (const ValueFlow::Value& v:lifeTok->values()) {
if (v.isLifetimeValue())
// TODO: Move container size values to generic forward
// Forward uninit values since not all values can be forwarded directly
if (!(v.isContainerSizeValue() || v.isUninitValue()))
continue;
ValueFlow::Value value = v;
value.errorPath.insert(value.errorPath.begin(), errorPath.begin(), errorPath.end());
@ -2267,6 +2269,9 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
virtual ProgramState getProgramState() const = 0;
virtual const ValueType* getValueType(const Token*) const {
return nullptr;
}
virtual int getIndirect(const Token* tok) const {
const ValueFlow::Value* value = getValue(tok);
if (value)
@ -2314,8 +2319,12 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
virtual Action isAliasModified(const Token* tok) const {
int indirect = 0;
int baseIndirect = 0;
const ValueType* vt = getValueType(tok);
if (vt)
baseIndirect = vt->pointer;
if (tok->valueType())
indirect = tok->valueType()->pointer;
indirect = std::max<int>(0, tok->valueType()->pointer - baseIndirect);
if (isVariableChanged(tok, indirect, getSettings(), isCPP()))
return Action::Invalid;
return Action::None;
@ -2350,6 +2359,25 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
}
// Check for modifications by function calls
return isModified(tok);
} else if (tok->isUnaryOp("*")) {
const Token* lifeTok = nullptr;
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
if (!v.isLocalLifetimeValue())
continue;
if (lifeTok)
return Action::None;
lifeTok = v.tokvalue;
}
if (lifeTok && match(lifeTok)) {
Action a = Action::Read;
if (isModified(tok).isModified())
a = Action::Invalid;
if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok))
a |= Action::Read;
return a;
}
return Action::None;
} else if (isAlias(tok)) {
return isAliasModified(tok);
} else if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) {
@ -2425,6 +2453,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
std::unordered_map<nonneg int, const Variable*> varids;
std::unordered_map<nonneg int, const Variable*> aliases;
ValueFlow::Value value;
SingleValueFlowForwardAnalyzer()
@ -2439,6 +2468,10 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
return varids;
}
const std::unordered_map<nonneg int, const Variable*>& getAliasedVars() const {
return aliases;
}
virtual const ValueFlow::Value* getValue(const Token*) const OVERRIDE {
return &value;
}
@ -2457,13 +2490,15 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
virtual bool isAlias(const Token* tok) const OVERRIDE {
if (value.isLifetimeValue())
return false;
for (const auto& p:getVars()) {
nonneg int varid = p.first;
const Variable* var = p.second;
if (tok->varId() == varid)
return true;
if (isAliasOf(var, tok, varid, value))
return true;
for(const auto& m:{getVars(), getAliasedVars()}) {
for (const auto& p:m) {
nonneg int varid = p.first;
const Variable* var = p.second;
if (tok->varId() == varid)
return true;
if (isAliasOf(var, tok, varid, value))
return true;
}
}
return false;
}
@ -2529,9 +2564,18 @@ struct VariableForwardAnalyzer : SingleValueFlowForwardAnalyzer {
: SingleValueFlowForwardAnalyzer(), var(nullptr)
{}
VariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, const TokenList* t)
VariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
: SingleValueFlowForwardAnalyzer(val, t), var(v) {
varids[var->declarationId()] = var;
for(const Variable* av:paliases) {
if (!av)
continue;
aliases[av->declarationId()] = av;
}
}
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
return var->valueType();
}
virtual bool match(const Token* tok) const OVERRIDE {
@ -2545,6 +2589,20 @@ struct VariableForwardAnalyzer : SingleValueFlowForwardAnalyzer {
}
};
static void valueFlowForwardVariable(Token* const startToken,
const Token* const endToken,
const Variable* const var,
std::list<ValueFlow::Value> values,
std::vector<const Variable*> aliases,
TokenList* const tokenlist,
const Settings* const settings)
{
for (ValueFlow::Value& v : values) {
VariableForwardAnalyzer a(var, v, aliases, tokenlist);
valueFlowGenericForward(startToken, endToken, a, settings);
}
}
static bool valueFlowForwardVariable(Token* const startToken,
const Token* const endToken,
const Variable* const var,
@ -2556,10 +2614,25 @@ static bool valueFlowForwardVariable(Token* const startToken,
ErrorLogger* const,
const Settings* const settings)
{
for (ValueFlow::Value& v : values) {
VariableForwardAnalyzer a(var, v, tokenlist);
valueFlowGenericForward(startToken, endToken, a, settings);
std::vector<const Variable*> aliases;
for (const ValueFlow::Value& v : values) {
if (!v.tokvalue)
continue;
const Token* lifeTok = nullptr;
for(const ValueFlow::Value& lv:v.tokvalue->values()) {
if (!lv.isLocalLifetimeValue())
continue;
if (lifeTok) {
lifeTok = nullptr;
break;
}
lifeTok = lv.tokvalue;
}
if (lifeTok && lifeTok->variable()) {
aliases.push_back(lifeTok->variable());
}
}
valueFlowForwardVariable(startToken, endToken, var, std::move(values), aliases, tokenlist, settings);
return true;
}
@ -2578,6 +2651,10 @@ struct ExpressionForwardAnalyzer : SingleValueFlowForwardAnalyzer {
setupExprVarIds();
}
virtual const ValueType* getValueType(const Token*) const OVERRIDE {
return expr->valueType();
}
static bool nonLocal(const Variable* var, bool deref) {
return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern();
}

View File

@ -98,6 +98,7 @@ private:
TEST_CASE(nullpointer55); // #8144
TEST_CASE(nullpointer56); // #9701
TEST_CASE(nullpointer57); // #9751
TEST_CASE(nullpointer58); // #9807
TEST_CASE(nullpointer_addressOf); // address of
TEST_CASE(nullpointerSwitch); // #2626
TEST_CASE(nullpointer_cast); // #4692
@ -1840,6 +1841,16 @@ private:
ASSERT_EQUALS("", errout.str());
}
void nullpointer58() {
check("struct myStruct { char entry[0]; };\n"
"void f() {\n"
" struct myStruct* sPtr = NULL;\n"
" int sz = (!*(&sPtr) || ((*(&sPtr))->entry[0] > 15)) ?\n"
" sizeof((*(&sPtr))->entry[0]) : 123456789;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void nullpointer_addressOf() { // address of
check("void f() {\n"
" struct X *x = 0;\n"