Skip to content

Commit 112363c

Browse files
authored
Fix 10590: container access out of bounds not found (#3560)
* Refactor container bounds check * Use symbolic values * Add test case * Format
1 parent 13f5b56 commit 112363c

4 files changed

Lines changed: 163 additions & 75 deletions

File tree

lib/checkstl.cpp

Lines changed: 125 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,43 @@ static const struct CWE CWE834(834U); // Excessive Iteration
6363

6464
static bool isElementAccessYield(const Library::Container::Yield& yield)
6565
{
66-
return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX;
66+
return contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield);
67+
}
68+
69+
static bool containerYieldsElement(const Library::Container* container, const Token* parent)
70+
{
71+
if (Token::Match(parent, ". %name% (")) {
72+
Library::Container::Yield yield = container->getYield(parent->strAt(1));
73+
if (isElementAccessYield(yield))
74+
return true;
75+
}
76+
return false;
77+
}
78+
79+
static const Token* getContainerIndex(const Library::Container* container, const Token* parent)
80+
{
81+
if (Token::Match(parent, ". %name% (")) {
82+
Library::Container::Yield yield = container->getYield(parent->strAt(1));
83+
if (isElementAccessYield(yield) && !Token::simpleMatch(parent->tokAt(2), "( )"))
84+
return parent->tokAt(2)->astOperand2();
85+
}
86+
if (!container->arrayLike_indexOp && !container->stdStringLike)
87+
return nullptr;
88+
if (Token::simpleMatch(parent, "["))
89+
return parent->astOperand2();
90+
return nullptr;
91+
}
92+
93+
static const Token* getContainerFromSize(const Library::Container* container, const Token* tok)
94+
{
95+
if (!tok)
96+
return nullptr;
97+
if (Token::Match(tok->tokAt(-2), ". %name% (")) {
98+
Library::Container::Yield yield = container->getYield(tok->strAt(-1));
99+
if (yield == Library::Container::Yield::SIZE)
100+
return tok->tokAt(-2)->astOperand1();
101+
}
102+
return nullptr;
67103
}
68104

69105
void CheckStl::outOfBounds()
@@ -74,6 +110,14 @@ void CheckStl::outOfBounds()
74110
if (!container)
75111
continue;
76112
const Token * parent = astParentSkipParens(tok);
113+
const Token* accessTok = parent;
114+
if (Token::simpleMatch(accessTok, ".") && Token::simpleMatch(accessTok->astParent(), "("))
115+
accessTok = accessTok->astParent();
116+
if (astIsIterator(accessTok) && Token::simpleMatch(accessTok->astParent(), "+"))
117+
accessTok = accessTok->astParent();
118+
const Token* indexTok = getContainerIndex(container, parent);
119+
if (indexTok == tok)
120+
continue;
77121
for (const ValueFlow::Value &value : tok->values()) {
78122
if (!value.isContainerSizeValue())
79123
continue;
@@ -83,70 +127,60 @@ void CheckStl::outOfBounds()
83127
continue;
84128
if (!value.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning))
85129
continue;
86-
if (Token::Match(parent, ". %name% (") && isElementAccessYield(container->getYield(parent->strAt(1)))) {
87-
if (value.intvalue == 0) {
88-
outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr);
89-
continue;
90-
}
91-
92-
const Token* indexTok = parent->tokAt(2)->astOperand2();
93-
if (!indexTok)
94-
continue;
95-
std::vector<ValueFlow::Value> indexValues =
96-
ValueFlow::isOutOfBounds(value, indexTok, mSettings->severity.isEnabled(Severity::warning));
97-
if (!indexValues.empty()) {
98-
outOfBoundsError(
99-
parent, tok->expressionString(), &value, indexTok->expressionString(), &indexValues.front());
100-
continue;
101-
}
102-
}
103-
if (Token::Match(tok, "%name% . %name% (") && container->getYield(tok->strAt(2)) == Library::Container::Yield::START_ITERATOR) {
104-
const Token *fparent = tok->tokAt(3)->astParent();
105-
const Token *other = nullptr;
106-
if (Token::simpleMatch(fparent, "+") && fparent->astOperand1() == tok->tokAt(3))
107-
other = fparent->astOperand2();
108-
else if (Token::simpleMatch(fparent, "+") && fparent->astOperand2() == tok->tokAt(3))
109-
other = fparent->astOperand1();
110-
if (other && other->hasKnownIntValue() && other->getKnownIntValue() > value.intvalue) {
111-
outOfBoundsError(fparent, tok->expressionString(), &value, other->expressionString(), &other->values().back());
112-
continue;
113-
} else if (other && !other->hasKnownIntValue() && value.isKnown() && value.intvalue==0) {
114-
outOfBoundsError(fparent, tok->expressionString(), &value, other->expressionString(), nullptr);
115-
continue;
116-
}
117-
}
118-
if (!container->arrayLike_indexOp && !container->stdStringLike)
119-
continue;
120-
if (value.intvalue == 0 && Token::Match(parent, "[") && tok == parent->astOperand1()) {
121-
outOfBoundsError(parent, tok->expressionString(), &value, "", nullptr);
130+
if (value.intvalue == 0 && (indexTok || containerYieldsElement(container, parent))) {
131+
std::string indexExpr;
132+
if (indexTok && !indexTok->hasKnownValue())
133+
indexExpr = indexTok->expressionString();
134+
outOfBoundsError(accessTok, tok->expressionString(), &value, indexExpr, nullptr);
122135
continue;
123136
}
124-
if (container->arrayLike_indexOp && Token::Match(parent, "[")) {
125-
const Token* indexTok = parent->astOperand2();
126-
if (!indexTok)
127-
continue;
137+
if (indexTok) {
128138
std::vector<ValueFlow::Value> indexValues =
129139
ValueFlow::isOutOfBounds(value, indexTok, mSettings->severity.isEnabled(Severity::warning));
130140
if (!indexValues.empty()) {
131141
outOfBoundsError(
132-
parent, tok->expressionString(), &value, indexTok->expressionString(), &indexValues.front());
142+
accessTok, tok->expressionString(), &value, indexTok->expressionString(), &indexValues.front());
133143
continue;
134144
}
135145
}
136146
}
147+
if (indexTok && !indexTok->hasKnownIntValue()) {
148+
const ValueFlow::Value* value =
149+
ValueFlow::findValue(indexTok->values(), mSettings, [&](const ValueFlow::Value& v) {
150+
if (!v.isSymbolicValue())
151+
return false;
152+
if (v.isImpossible())
153+
return false;
154+
if (v.intvalue < 0)
155+
return false;
156+
const Token* containerTok = getContainerFromSize(container, v.tokvalue);
157+
if (!containerTok)
158+
return false;
159+
return containerTok->exprId() == tok->exprId();
160+
});
161+
if (!value)
162+
continue;
163+
outOfBoundsError(accessTok, tok->expressionString(), nullptr, indexTok->expressionString(), value);
164+
}
137165
}
138166
}
139167
}
140168

141-
static std::string indexValueString(const ValueFlow::Value& indexValue)
169+
static std::string indexValueString(const ValueFlow::Value& indexValue, const std::string& containerName = "")
142170
{
143171
if (indexValue.isIteratorStartValue())
144172
return "at position " + MathLib::toString(indexValue.intvalue) + " from the beginning";
145173
if (indexValue.isIteratorEndValue())
146174
return "at position " + MathLib::toString(-indexValue.intvalue) + " from the end";
175+
std::string indexString = MathLib::toString(indexValue.intvalue);
176+
if (indexValue.isSymbolicValue()) {
177+
indexString = containerName + ".size()";
178+
if (indexValue.intvalue != 0)
179+
indexString += "+" + MathLib::toString(indexValue.intvalue);
180+
}
147181
if (indexValue.bound == ValueFlow::Value::Bound::Lower)
148-
return "greater or equal to " + MathLib::toString(indexValue.intvalue);
149-
return MathLib::toString(indexValue.intvalue);
182+
return "greater or equal to " + indexString;
183+
return indexString;
150184
}
151185

152186
void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue)
@@ -158,9 +192,14 @@ void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerNa
158192
const std::string expression = tok ? tok->expressionString() : (containerName+"[x]");
159193

160194
std::string errmsg;
161-
if (!containerSize)
162-
errmsg = "Out of bounds access in expression '" + expression + "'";
163-
else if (containerSize->intvalue == 0) {
195+
if (!containerSize) {
196+
if (indexValue && indexValue->condition)
197+
errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index +
198+
"' can have the value " + indexValueString(*indexValue, containerName) + ". Expression '" +
199+
expression + "' cause access out of bounds.";
200+
else
201+
errmsg = "Out of bounds access in expression '" + expression + "'";
202+
} else if (containerSize->intvalue == 0) {
164203
if (containerSize->condition)
165204
errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or expression '" + expression + "' cause access out of bounds.";
166205
else if (indexValue == nullptr && !index.empty())
@@ -1175,6 +1214,9 @@ void CheckStl::stlOutOfBounds()
11751214
continue;
11761215
}
11771216

1217+
if (containerToken->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))
1218+
continue;
1219+
11781220
// Is it a array like container?
11791221
const Library::Container* container = containerToken->valueType() ? containerToken->valueType()->container : nullptr;
11801222
if (!container)
@@ -2183,6 +2225,9 @@ void CheckStl::checkDereferenceInvalidIterator2()
21832225
continue;
21842226
}
21852227

2228+
if (Token::Match(tok, "%assign%"))
2229+
continue;
2230+
21862231
std::vector<ValueFlow::Value> contValues;
21872232
std::copy_if(tok->values().begin(), tok->values().end(), std::back_inserter(contValues), [&](const ValueFlow::Value& value) {
21882233
if (value.isImpossible())
@@ -2201,9 +2246,13 @@ void CheckStl::checkDereferenceInvalidIterator2()
22012246
continue;
22022247
if (!value.isIteratorValue())
22032248
continue;
2204-
const bool isInvalidIterator = (value.isIteratorEndValue() && value.intvalue >= 0) || (value.isIteratorStartValue() && value.intvalue < 0);
2249+
bool isInvalidIterator = false;
22052250
const ValueFlow::Value* cValue = nullptr;
2206-
if (!isInvalidIterator) {
2251+
if (value.isIteratorEndValue() && value.intvalue >= 0) {
2252+
isInvalidIterator = value.intvalue > 0;
2253+
} else if (value.isIteratorStartValue() && value.intvalue < 0) {
2254+
isInvalidIterator = true;
2255+
} else {
22072256
auto it = std::find_if(contValues.begin(), contValues.end(), [&](const ValueFlow::Value& c) {
22082257
if (value.isIteratorStartValue() && value.intvalue >= c.intvalue)
22092258
return true;
@@ -2214,17 +2263,41 @@ void CheckStl::checkDereferenceInvalidIterator2()
22142263
if (it == contValues.end())
22152264
continue;
22162265
cValue = &*it;
2266+
if (value.isIteratorStartValue() && value.intvalue > cValue->intvalue)
2267+
isInvalidIterator = true;
22172268
}
22182269
bool inconclusive = false;
22192270
bool unknown = false;
2220-
if (!CheckNullPointer::isPointerDeRef(tok, unknown, mSettings)) {
2271+
const Token* emptyAdvance = nullptr;
2272+
const Token* advanceIndex = nullptr;
2273+
if (cValue && cValue->intvalue == 0) {
2274+
if (Token::Match(tok->astParent(), "+|-") && astIsIntegral(tok->astSibling(), false)) {
2275+
if (tok->astSibling() && tok->astSibling()->hasKnownIntValue()) {
2276+
if (tok->astSibling()->values().front().intvalue == 0)
2277+
continue;
2278+
} else {
2279+
advanceIndex = tok->astSibling();
2280+
}
2281+
emptyAdvance = tok->astParent();
2282+
} else if (Token::Match(tok->astParent(), "++|--")) {
2283+
emptyAdvance = tok->astParent();
2284+
}
2285+
}
2286+
if (!CheckNullPointer::isPointerDeRef(tok, unknown, mSettings) && !isInvalidIterator && !emptyAdvance) {
22212287
if (!unknown)
22222288
continue;
22232289
inconclusive = true;
22242290
}
22252291
if (cValue) {
22262292
const ValueFlow::Value& lValue = getLifetimeObjValue(tok, true);
2227-
outOfBoundsError(tok, lValue.tokvalue->expressionString(), cValue, tok->expressionString(), &value);
2293+
if (emptyAdvance)
2294+
outOfBoundsError(emptyAdvance,
2295+
lValue.tokvalue->expressionString(),
2296+
cValue,
2297+
advanceIndex ? advanceIndex->expressionString() : "",
2298+
nullptr);
2299+
else
2300+
outOfBoundsError(tok, lValue.tokvalue->expressionString(), cValue, tok->expressionString(), &value);
22282301
} else {
22292302
dereferenceInvalidIteratorError(tok, &value, inconclusive);
22302303
}

lib/checkstl.h

100644100755
File mode changed.

lib/valueflow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2955,7 +2955,7 @@ std::vector<ValueFlow::Value> getLifetimeObjValues(const Token *tok, bool inconc
29552955
return false;
29562956
if (!inconclusive && v.isInconclusive())
29572957
return false;
2958-
if (!v.tokvalue->variable())
2958+
if (!v.tokvalue)
29592959
return false;
29602960
return true;
29612961
};

0 commit comments

Comments
 (0)