Skip to content

Commit 57f5b19

Browse files
authored
Fix 7812: False negative: return pointer of local variable (#3583)
* Fix 7812: False negative: return pointer of local variable * Format * Add test case for 3029 * Format
1 parent cea6497 commit 57f5b19

5 files changed

Lines changed: 76 additions & 16 deletions

File tree

lib/checkautovariables.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
653653
continue;
654654
if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) &&
655655
isInScope(tokvalue->variable()->nameToken(), scope)) ||
656-
isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
656+
isDeadTemporary(mTokenizer->isCPP(), tokvalue, nullptr, &mSettings->library)) {
657657
errorReturnDanglingLifetime(tok, &val);
658658
break;
659659
}

lib/symboldatabase.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
6868
createSymbolDatabaseSetScopePointers();
6969
createSymbolDatabaseSetVariablePointers();
7070
setValueTypeInTokenList(false);
71-
createSymbolDatabaseSetFunctionPointers(true);
7271
createSymbolDatabaseSetTypePointers();
7372
createSymbolDatabaseSetSmartPointerType();
73+
createSymbolDatabaseSetFunctionPointers(true);
74+
setValueTypeInTokenList(false);
7475
createSymbolDatabaseEnums();
7576
createSymbolDatabaseEscapeFunctions();
7677
createSymbolDatabaseIncompleteVars();
@@ -5273,8 +5274,10 @@ const Function* SymbolDatabase::findFunction(const Token *tok) const
52735274

52745275
// check for member function
52755276
else if (Token::Match(tok->tokAt(-2), "!!this .")) {
5276-
const Token *tok1 = tok->tokAt(-2);
5277-
if (Token::Match(tok1, "%var% .")) {
5277+
const Token* tok1 = tok->previous()->astOperand1();
5278+
if (tok1 && tok1->valueType() && tok1->valueType()->typeScope) {
5279+
return tok1->valueType()->typeScope->findFunction(tok, tok1->valueType()->constness == 1);
5280+
} else if (Token::Match(tok1, "%var% .")) {
52785281
const Variable *var = getVariableFromVarId(tok1->varId());
52795282
if (var && var->typeScope())
52805283
return var->typeScope()->findFunction(tok, var->valueType()->constness == 1);
@@ -5299,6 +5302,12 @@ const Function* SymbolDatabase::findFunction(const Token *tok) const
52995302
currScope = currScope->nestedIn;
53005303
}
53015304
}
5305+
// Check for contructor
5306+
if (Token::Match(tok, "%name% (|{")) {
5307+
ValueType vt = ValueType::parseDecl(tok, mSettings);
5308+
if (vt.typeScope)
5309+
return vt.typeScope->findFunction(tok, false);
5310+
}
53025311
return nullptr;
53035312
}
53045313

lib/valueflow.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3706,6 +3706,9 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
37063706
{
37073707
if (!Token::Match(tok, "%name% ("))
37083708
return;
3709+
Token* memtok = nullptr;
3710+
if (Token::Match(tok->astParent(), ". %name% (") && astIsRHS(tok))
3711+
memtok = tok->astParent()->astOperand1();
37093712
int returnContainer = settings->library.returnValueContainer(tok);
37103713
if (returnContainer >= 0) {
37113714
std::vector<const Token *> args = getArguments(tok);
@@ -3740,19 +3743,20 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
37403743
LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byVal(
37413744
tok->next(), tokenlist, errorLogger, settings);
37423745
}
3743-
} else if (Token::Match(tok->tokAt(-2), "%var% . push_back|push_front|insert|push|assign") &&
3744-
astIsContainer(tok->tokAt(-2))) {
3745-
Token *vartok = tok->tokAt(-2);
3746+
} else if (memtok && Token::Match(tok->astParent(), ". push_back|push_front|insert|push|assign") &&
3747+
astIsContainer(memtok)) {
37463748
std::vector<const Token *> args = getArguments(tok);
37473749
std::size_t n = args.size();
37483750
if (n > 1 && Token::typeStr(args[n - 2]) == Token::typeStr(args[n - 1]) &&
37493751
(((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) ||
37503752
(astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) {
3751-
LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byDerefCopy(
3752-
vartok, tokenlist, errorLogger, settings);
3753+
LifetimeStore{
3754+
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
3755+
.byDerefCopy(memtok, tokenlist, errorLogger, settings);
37533756
} else if (!args.empty() && isLifetimeBorrowed(args.back(), settings)) {
3754-
LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byVal(
3755-
vartok, tokenlist, errorLogger, settings);
3757+
LifetimeStore{
3758+
args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}
3759+
.byVal(memtok, tokenlist, errorLogger, settings);
37563760
}
37573761
} else if (tok->function()) {
37583762
const Function *f = tok->function();
@@ -3776,6 +3780,17 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
37763780
continue;
37773781
if (!v.tokvalue)
37783782
continue;
3783+
if (exprDependsOnThis(v.tokvalue) && memtok) {
3784+
LifetimeStore ls = LifetimeStore{memtok,
3785+
"Passed to member function '" + tok->expressionString() + "'.",
3786+
ValueFlow::Value::LifetimeKind::Object};
3787+
ls.inconclusive = inconclusive;
3788+
ls.forward = false;
3789+
ls.errorPath = v.errorPath;
3790+
ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + ".");
3791+
update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings);
3792+
continue;
3793+
}
37793794
const Variable *var = v.tokvalue->variable();
37803795
LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger);
37813796
if (!ls.argtok)

test/testautovariables.cpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class TestAutoVariables : public TestFixture {
151151
TEST_CASE(danglingLifetimeImplicitConversion);
152152
TEST_CASE(danglingTemporaryLifetime);
153153
TEST_CASE(danglingLifetimeBorrowedMembers);
154+
TEST_CASE(danglingLifetimeClassMemberFunctions);
154155
TEST_CASE(invalidLifetime);
155156
TEST_CASE(deadPointer);
156157
TEST_CASE(splitNamespaceAuto); // crash #10473
@@ -2246,10 +2247,8 @@ class TestAutoVariables : public TestFixture {
22462247
"auto f() {\n"
22472248
" return g().begin();\n"
22482249
"}");
2249-
TODO_ASSERT_EQUALS(
2250-
"[test.cpp:3] -> [test.cpp:3]: (error) Returning iterator that will be invalid when returning.\n",
2251-
"",
2252-
errout.str());
2250+
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Returning iterator that will be invalid when returning.\n",
2251+
errout.str());
22532252

22542253
check("std::vector<int> f();\n"
22552254
"auto f() {\n"
@@ -3291,6 +3290,41 @@ class TestAutoVariables : public TestFixture {
32913290
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n",
32923291
errout.str());
32933292
}
3293+
3294+
void danglingLifetimeClassMemberFunctions()
3295+
{
3296+
check("struct S {\n"
3297+
" S(int i) : i(i) {}\n"
3298+
" int i;\n"
3299+
" int* ptr() { return &i; }\n"
3300+
"};\n"
3301+
"int* fun(int i) { \n"
3302+
" return S(i).ptr();\n"
3303+
"}\n");
3304+
ASSERT_EQUALS(
3305+
"[test.cpp:4] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:7]: (error) Returning pointer that will be invalid when returning.\n",
3306+
errout.str());
3307+
3308+
check("struct Fred\n"
3309+
"{\n"
3310+
" int x[2];\n"
3311+
" Fred() {\n"
3312+
" x[0] = 0x41;\n"
3313+
" x[1] = 0x42;\n"
3314+
" }\n"
3315+
" const int *get_x() {\n"
3316+
" return x;\n"
3317+
" }\n"
3318+
"};\n"
3319+
"static const int *foo() {\n"
3320+
" Fred fred;\n"
3321+
" return fred.get_x();\n"
3322+
"}\n");
3323+
ASSERT_EQUALS(
3324+
"[test.cpp:9] -> [test.cpp:9] -> [test.cpp:14] -> [test.cpp:13] -> [test.cpp:14]: (error) Returning pointer to local variable 'fred' that will be invalid when returning.\n",
3325+
errout.str());
3326+
}
3327+
32943328
void invalidLifetime() {
32953329
check("void foo(int a) {\n"
32963330
" std::function<void()> f;\n"

test/testnullpointer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2088,7 +2088,9 @@ class TestNullPointer : public TestFixture {
20882088
" if (!y) {}\n"
20892089
" }\n"
20902090
"}\n");
2091-
ASSERT_EQUALS("", errout.str());
2091+
ASSERT_EQUALS(
2092+
"[test.cpp:13] -> [test.cpp:9]: (warning) Either the condition '!y' is redundant or there is possible null pointer dereference: x->g().\n",
2093+
errout.str());
20922094
}
20932095

20942096
void nullpointer65() {

0 commit comments

Comments
 (0)