Skip to content

Commit 12e731a

Browse files
authored
Fix 10605: FP containerOutOfBounds with empty() check (#3572)
1 parent 33ad30f commit 12e731a

6 files changed

Lines changed: 134 additions & 18 deletions

File tree

lib/astutils.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ void visitAstNodesGeneric(T *ast, std::function<ChildrenToVisit(T *)> visitor)
5353

5454
if (c == ChildrenToVisit::done)
5555
break;
56-
if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2)
57-
tokens.push(tok->astOperand1());
5856
if (c == ChildrenToVisit::op2 || c == ChildrenToVisit::op1_and_op2)
5957
tokens.push(tok->astOperand2());
58+
if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2)
59+
tokens.push(tok->astOperand1());
6060
}
6161
}
6262

@@ -734,13 +734,27 @@ static bool isInLoopCondition(const Token * tok)
734734
/// If tok2 comes after tok1
735735
bool precedes(const Token * tok1, const Token * tok2)
736736
{
737+
if (tok1 == tok2)
738+
return false;
737739
if (!tok1)
738740
return false;
739741
if (!tok2)
740742
return true;
741743
return tok1->index() < tok2->index();
742744
}
743745

746+
/// If tok1 comes after tok2
747+
bool succedes(const Token* tok1, const Token* tok2)
748+
{
749+
if (tok1 == tok2)
750+
return false;
751+
if (!tok1)
752+
return false;
753+
if (!tok2)
754+
return true;
755+
return tok1->index() > tok2->index();
756+
}
757+
744758
bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive)
745759
{
746760
if (tok->varId() == varid)
@@ -1865,11 +1879,12 @@ bool isScopeBracket(const Token* tok)
18651879
return false;
18661880
}
18671881

1868-
const Token * getTokenArgumentFunction(const Token * tok, int& argn)
1882+
template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
1883+
T* getTokenArgumentFunctionImpl(T* tok, int& argn)
18691884
{
18701885
argn = -1;
18711886
{
1872-
const Token *parent = tok->astParent();
1887+
T* parent = tok->astParent();
18731888
if (parent && parent->isUnaryOp("&"))
18741889
parent = parent->astParent();
18751890
while (parent && parent->isCast())
@@ -1891,7 +1906,7 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn)
18911906
return nullptr;
18921907
}
18931908

1894-
const Token* argtok = tok;
1909+
T* argtok = tok;
18951910
while (argtok && argtok->astParent() && (!Token::Match(argtok->astParent(), ",|(|{") || argtok->astParent()->isCast())) {
18961911
argtok = argtok->astParent();
18971912
}
@@ -1935,6 +1950,14 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn)
19351950
return tok;
19361951
}
19371952

1953+
const Token* getTokenArgumentFunction(const Token* tok, int& argn) {
1954+
return getTokenArgumentFunctionImpl(tok, argn);
1955+
}
1956+
1957+
Token* getTokenArgumentFunction(Token* tok, int& argn) {
1958+
return getTokenArgumentFunctionImpl(tok, argn);
1959+
}
1960+
19381961
std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr)
19391962
{
19401963
std::vector<const Variable*> result;

lib/astutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ bool extractForLoopValues(const Token *forToken,
151151
long long * const lastValue);
152152

153153
bool precedes(const Token * tok1, const Token * tok2);
154+
bool succedes(const Token* tok1, const Token* tok2);
154155

155156
bool exprDependsOnThis(const Token* expr, bool onVar = true, nonneg int depth = 0);
156157

@@ -208,6 +209,7 @@ bool isReturnScope(const Token* const endToken,
208209

209210
/// Return the token to the function and the argument number
210211
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
212+
Token* getTokenArgumentFunction(Token* tok, int& argn);
211213

212214
std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr);
213215

lib/reverseanalyzer.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,44 @@ struct ReverseTraversal {
4040
return true;
4141
}
4242

43+
Token* getParentFunction(Token* tok)
44+
{
45+
if (!tok)
46+
return nullptr;
47+
if (!tok->astParent())
48+
return nullptr;
49+
int argn = -1;
50+
if (Token* ftok = getTokenArgumentFunction(tok, argn)) {
51+
while (!Token::Match(ftok, "(|{")) {
52+
if (!ftok)
53+
return nullptr;
54+
if (ftok->index() >= tok->index())
55+
return nullptr;
56+
if (ftok->link())
57+
ftok = ftok->link()->next();
58+
else
59+
ftok = ftok->next();
60+
}
61+
if (ftok == tok)
62+
return nullptr;
63+
return ftok;
64+
}
65+
return nullptr;
66+
}
67+
68+
Token* getTopFunction(Token* tok)
69+
{
70+
if (!tok)
71+
return nullptr;
72+
if (!tok->astParent())
73+
return tok;
74+
Token* parent = tok;
75+
Token* top = tok;
76+
while ((parent = getParentFunction(parent)))
77+
top = parent;
78+
return top;
79+
}
80+
4381
bool updateRecursive(Token* start) {
4482
bool continueB = true;
4583
visitAstNodes(start, [&](Token* tok) {
@@ -121,7 +159,7 @@ struct ReverseTraversal {
121159
if (start == end)
122160
return;
123161
std::size_t i = start->index();
124-
for (Token* tok = start->previous(); tok != end; tok = tok->previous()) {
162+
for (Token* tok = start->previous(); succedes(tok, end); tok = tok->previous()) {
125163
if (tok->index() >= i)
126164
throw InternalError(tok, "Cyclic reverse analysis.");
127165
i = tok->index();
@@ -198,6 +236,16 @@ struct ReverseTraversal {
198236
tok = previousBeforeAstLeftmostLeaf(assignTop)->next();
199237
continue;
200238
}
239+
if (tok->str() == ")" && !isUnevaluated(tok)) {
240+
if (Token* top = getTopFunction(tok->link())) {
241+
if (!updateRecursive(top))
242+
break;
243+
Token* next = previousBeforeAstLeftmostLeaf(top);
244+
if (next && precedes(next, tok))
245+
tok = next->next();
246+
}
247+
continue;
248+
}
201249
if (tok->str() == "}") {
202250
Token* condTok = getCondTokFromEnd(tok);
203251
if (!condTok)
@@ -283,6 +331,8 @@ struct ReverseTraversal {
283331
}
284332

285333
static Token* assignExpr(Token* tok) {
334+
if (Token::Match(tok, ")|}"))
335+
tok = tok->link();
286336
while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
287337
if (tok->astParent()->isAssignmentOp())
288338
return tok->astParent();

lib/valueflow.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,6 +2115,23 @@ struct ValueFlowAnalyzer : Analyzer {
21152115
return Action::None;
21162116
}
21172117

2118+
Action isGlobalModified(const Token* tok) const
2119+
{
2120+
if (tok->function()) {
2121+
if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings()->library))
2122+
return Action::Invalid;
2123+
} else if (getSettings()->library.getFunction(tok)) {
2124+
// Assume library function doesn't modify user-global variables
2125+
return Action::None;
2126+
// Function cast does not modify global variables
2127+
} else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) {
2128+
return Action::None;
2129+
} else if (Token::Match(tok, "%name% (")) {
2130+
return Action::Invalid;
2131+
}
2132+
return Action::None;
2133+
}
2134+
21182135
static const std::string& getAssign(const Token* tok, Direction d)
21192136
{
21202137
if (d == Direction::Forward)
@@ -2245,6 +2262,11 @@ struct ValueFlowAnalyzer : Analyzer {
22452262

22462263
Action analyzeMatch(const Token* tok, Direction d) const {
22472264
const Token* parent = tok->astParent();
2265+
if (d == Direction::Reverse && isGlobal() && !dependsOnThis() && Token::Match(parent, ". %name% (")) {
2266+
Action a = isGlobalModified(parent->next());
2267+
if (a != Action::None)
2268+
return a;
2269+
}
22482270
if ((astIsPointer(tok) || astIsSmartPointer(tok)) &&
22492271
(Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
22502272
return Action::Read;
@@ -2328,18 +2350,7 @@ struct ValueFlowAnalyzer : Analyzer {
23282350
// bailout: global non-const variables
23292351
if (isGlobal() && !dependsOnThis() && Token::Match(tok, "%name% (") &&
23302352
!Token::simpleMatch(tok->linkAt(1), ") {")) {
2331-
if (tok->function()) {
2332-
if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings()->library))
2333-
return Action::Invalid;
2334-
} else if (getSettings()->library.getFunction(tok)) {
2335-
// Assume library function doesn't modify user-global variables
2336-
return Action::None;
2337-
// Function cast does not modify global variables
2338-
} else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) {
2339-
return Action::None;
2340-
} else {
2341-
return Action::Invalid;
2342-
}
2353+
return isGlobalModified(tok);
23432354
}
23442355
return Action::None;
23452356
}
@@ -4998,6 +5009,8 @@ struct ConditionHandler {
49985009
for (Token *tok = const_cast<Token *>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
49995010
if (Token::Match(tok, "if|while|for ("))
50005011
continue;
5012+
if (Token::Match(tok, ":|;|,"))
5013+
continue;
50015014

50025015
const Token* top = tok->astTop();
50035016
if (!top)

test/testnullpointer.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class TestNullPointer : public TestFixture {
125125
TEST_CASE(nullpointer83); // #9870
126126
TEST_CASE(nullpointer84); // #9873
127127
TEST_CASE(nullpointer85); // #10210
128+
TEST_CASE(nullpointer86);
128129
TEST_CASE(nullpointer_addressOf); // address of
129130
TEST_CASE(nullpointerSwitch); // #2626
130131
TEST_CASE(nullpointer_cast); // #4692
@@ -2547,6 +2548,25 @@ class TestNullPointer : public TestFixture {
25472548
errout.str());
25482549
}
25492550

2551+
void nullpointer86()
2552+
{
2553+
check("struct A {\n"
2554+
" A* a() const;\n"
2555+
" int b() const;\n"
2556+
"};\n"
2557+
"A* f(A* t) {\n"
2558+
" if (t->b() == 0) {\n"
2559+
" return t;\n"
2560+
" }\n"
2561+
" return t->a();\n"
2562+
"}\n"
2563+
"void g(A* t) {\n"
2564+
" t = f(t->a());\n"
2565+
" if (!t->a()) {}\n"
2566+
"}\n");
2567+
ASSERT_EQUALS("", errout.str());
2568+
}
2569+
25502570
void nullpointer_addressOf() { // address of
25512571
check("void f() {\n"
25522572
" struct X *x = 0;\n"

test/teststl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,14 @@ class TestStl : public TestFixture {
646646
"}\n");
647647
ASSERT_EQUALS("", errout.str());
648648

649+
checkNormal("void f(std::vector<int>& v, int i) {\n"
650+
" if (i > -1) {\n"
651+
" v.erase(v.begin() + i);\n"
652+
" if (v.empty()) {}\n"
653+
" }\n"
654+
"}\n");
655+
ASSERT_EQUALS("", errout.str());
656+
649657
checkNormal("void g(const char *, ...) { exit(1); }\n" // #10025
650658
"void f(const char c[]) {\n"
651659
" std::vector<int> v = get();\n"

0 commit comments

Comments
 (0)