Fix 9135: Access of moved variable not detected in loop (#4215)

* Fix 9135: Access of moved variable not detected in loop

* Format

* Fix issue with pushing back on container

* Format

* Fix null pointer

* Remove yeild for now
This commit is contained in:
Paul Fultz II 2022-06-16 10:40:09 -05:00 committed by GitHub
parent ef33ff628f
commit de51ebbcf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 13 deletions

View File

@ -260,6 +260,25 @@ bool astIsContainerOwned(const Token* tok) {
return astIsContainer(tok) && !astIsContainerView(tok); return astIsContainer(tok) && !astIsContainerView(tok);
} }
static const Token* getContainerFunction(const Token* tok)
{
if (!tok || !tok->valueType() || !tok->valueType()->container)
return nullptr;
const Token* parent = tok->astParent();
if (Token::Match(parent, ". %name% (") && astIsLHS(tok)) {
return parent->next();
}
return nullptr;
}
Library::Container::Action astContainerAction(const Token* tok)
{
const Token* ftok = getContainerFunction(tok);
if (!ftok)
return Library::Container::Action::NO_ACTION;
return tok->valueType()->container->getAction(ftok->str());
}
bool astIsRangeBasedForDecl(const Token* tok) bool astIsRangeBasedForDecl(const Token* tok)
{ {
return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "("); return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "(");

View File

@ -139,6 +139,8 @@ bool astIsContainer(const Token *tok);
bool astIsContainerView(const Token* tok); bool astIsContainerView(const Token* tok);
bool astIsContainerOwned(const Token* tok); bool astIsContainerOwned(const Token* tok);
Library::Container::Action astContainerAction(const Token* tok);
/** Is given token a range-declaration in a range-based for loop */ /** Is given token a range-declaration in a range-based for loop */
bool astIsRangeBasedForDecl(const Token* tok); bool astIsRangeBasedForDecl(const Token* tok);

View File

@ -607,7 +607,8 @@ struct ForwardTraversal {
} else if (condTok->values().front().intvalue == inElse) { } else if (condTok->values().front().intvalue == inElse) {
return Break(); return Break();
} }
// Handle for loop // Handle loop
if (inLoop) {
Token* stepTok = getStepTokFromEnd(tok); Token* stepTok = getStepTokFromEnd(tok);
bool checkThen, checkElse; bool checkThen, checkElse;
std::tie(checkThen, checkElse) = evalCond(condTok); std::tie(checkThen, checkElse) = evalCond(condTok);
@ -616,6 +617,13 @@ struct ForwardTraversal {
return Break(); return Break();
if (updateRecursive(condTok) == Progress::Break) if (updateRecursive(condTok) == Progress::Break)
return Break(); return Break();
// Reevaluate condition
std::tie(checkThen, checkElse) = evalCond(condTok);
}
if (!checkElse) {
if (updateLoopExit(end, tok, condTok, nullptr, stepTok) == Progress::Break)
return Break();
}
} }
analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet); analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet);
if (Token::simpleMatch(tok, "} else {")) if (Token::simpleMatch(tok, "} else {"))
@ -852,7 +860,6 @@ struct ForwardTraversal {
return nullptr; return nullptr;
return getStepTok(end->link()); return getStepTok(end->link());
} }
}; };
Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings) Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings* settings)

View File

@ -2108,6 +2108,24 @@ static bool evalAssignment(Value& lhsValue, const std::string& assign, const Val
return !error; return !error;
} }
static ValueFlow::Value::MoveKind isMoveOrForward(const Token* tok)
{
if (!tok)
return ValueFlow::Value::MoveKind::NonMovedVariable;
const Token* parent = tok->astParent();
if (!Token::simpleMatch(parent, "("))
return ValueFlow::Value::MoveKind::NonMovedVariable;
const Token* ftok = parent->astOperand1();
if (!ftok)
return ValueFlow::Value::MoveKind::NonMovedVariable;
if (Token::simpleMatch(ftok->astOperand1(), "std :: move"))
return ValueFlow::Value::MoveKind::MovedVariable;
if (Token::simpleMatch(ftok->astOperand1(), "std :: forward"))
return ValueFlow::Value::MoveKind::ForwardedVariable;
// TODO: Check for cast
return ValueFlow::Value::MoveKind::NonMovedVariable;
}
template<class T> template<class T>
struct SingleRange { struct SingleRange {
T* x; T* x;
@ -2416,6 +2434,19 @@ struct ValueFlowAnalyzer : Analyzer {
virtual Action isModified(const Token* tok) const { virtual Action isModified(const Token* tok) const {
Action read = Action::Read; Action read = Action::Read;
const ValueFlow::Value* value = getValue(tok);
if (value) {
// Moving a moved value wont change the moved value
if (value->isMovedValue() && isMoveOrForward(tok) != ValueFlow::Value::MoveKind::NonMovedVariable)
return read;
// Inserting elements to container wont change the lifetime
if (astIsContainer(tok) && value->isLifetimeValue() &&
contains({Library::Container::Action::PUSH,
Library::Container::Action::INSERT,
Library::Container::Action::CHANGE_INTERNAL},
astContainerAction(tok)))
return read;
}
bool inconclusive = false; bool inconclusive = false;
if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive)) if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive))
return read | Action::Invalid; return read | Action::Invalid;
@ -2424,7 +2455,6 @@ struct ValueFlowAnalyzer : Analyzer {
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) { if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
if (Token::Match(tok->astParent(), "*|[|.|++|--")) if (Token::Match(tok->astParent(), "*|[|.|++|--"))
return read | Action::Invalid; return read | Action::Invalid;
const ValueFlow::Value* value = getValue(tok);
// Check if its assigned to the same value // Check if its assigned to the same value
if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) &&
astIsIntegral(tok->astParent()->astOperand2(), false)) { astIsIntegral(tok->astParent()->astOperand2(), false)) {

View File

@ -3943,7 +3943,9 @@ private:
" if (former_hover != pitem)\n" " if (former_hover != pitem)\n"
" dosth();\n" " dosth();\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n", errout.str()); ASSERT_EQUALS(
"[test.cpp:5] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n",
errout.str());
// #6575 // #6575
check("void trp_deliver_signal() {\n" check("void trp_deliver_signal() {\n"

View File

@ -867,7 +867,7 @@ private:
" i=4;\n" " i=4;\n"
" }\n" " }\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[5]' accessed at index 5, which is out of bounds.\n", errout.str());
check("void f()\n" check("void f()\n"
"{\n" "{\n"

View File

@ -254,6 +254,7 @@ private:
TEST_CASE(moveAndAddressOf); TEST_CASE(moveAndAddressOf);
TEST_CASE(partiallyMoved); TEST_CASE(partiallyMoved);
TEST_CASE(moveAndLambda); TEST_CASE(moveAndLambda);
TEST_CASE(moveInLoop);
TEST_CASE(forwardAndUsed); TEST_CASE(forwardAndUsed);
TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgNamesDifferent);
@ -9837,6 +9838,25 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void moveInLoop()
{
check("void g(std::string&& s);\n"
"void f() {\n"
" std::string p;\n"
" while(true)\n"
" g(std::move(p));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout.str());
check("std::list<int> g(std::list<int>&&);\n"
"void f(std::list<int>l) {\n"
" for(int i = 0; i < 10; ++i) {\n"
" for (auto &j : g(std::move(l))) { (void)j; }\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'l'.\n", errout.str());
}
void forwardAndUsed() { void forwardAndUsed() {
Settings keepTemplates; Settings keepTemplates;
keepTemplates.checkUnusedTemplates = true; keepTemplates.checkUnusedTemplates = true;