Skip to content

Commit 2679b57

Browse files
committed
Fixed danmar#1693 (false negative: std::vector, negative index)
1 parent 0cae823 commit 2679b57

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

lib/checkstl.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ static const struct CWE CWE628(628U); // Function Call with Incorrectly Specif
4444
static const struct CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime
4545
static const struct CWE CWE704(704U); // Incorrect Type Conversion or Cast
4646
static const struct CWE CWE762(762U); // Mismatched Memory Management Routines
47+
static const struct CWE CWE786(786U); // Access of Memory Location Before Start of Buffer
4748
static const struct CWE CWE788(788U); // Access of Memory Location After End of Buffer
4849
static const struct CWE CWE825(825U); // Expired Pointer Dereference
4950
static const struct CWE CWE834(834U); // Excessive Iteration
@@ -437,6 +438,42 @@ void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, con
437438
reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + "[" + num + "] is out of bounds.", CWE788, false);
438439
}
439440

441+
void CheckStl::negativeIndex()
442+
{
443+
// Negative index is out of bounds..
444+
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
445+
const std::size_t functions = symbolDatabase->functionScopes.size();
446+
for (std::size_t ii = 0; ii < functions; ++ii) {
447+
const Scope * scope = symbolDatabase->functionScopes[ii];
448+
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
449+
if (!Token::Match(tok, "%var% ["))
450+
continue;
451+
const Variable * const var = tok->variable();
452+
if (!var || tok == var->nameToken())
453+
continue;
454+
const Library::Container * const container = _settings->library.detectContainer(var->typeStartToken());
455+
if (!container || !container->arrayLike_indexOp)
456+
continue;
457+
const ValueFlow::Value *index = tok->next()->astOperand2()->getValueLE(-1, _settings);
458+
if (!index)
459+
continue;
460+
negativeIndexError(tok, *index);
461+
}
462+
}
463+
}
464+
465+
void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &index)
466+
{
467+
const ErrorPath errorPath = getErrorPath(tok, &index, "Negative array index");
468+
std::ostringstream errmsg;
469+
if (index.condition)
470+
errmsg << ValueFlow::eitherTheConditionIsRedundant(index.condition)
471+
<< ", otherwise there is negative array index " << index.intvalue << ".";
472+
else
473+
errmsg << "Array index " << index.intvalue << " is out of bounds.";
474+
reportError(errorPath, index.errorSeverity() ? Severity::error : Severity::warning, "negativeContainerIndex", errmsg.str(), CWE786, index.inconclusive);
475+
}
476+
440477
void CheckStl::erase()
441478
{
442479
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();

lib/checkstl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class CPPCHECKLIB CheckStl : public Check {
6161
CheckStl checkStl(tokenizer, settings, errorLogger);
6262

6363
checkStl.stlOutOfBounds();
64+
checkStl.negativeIndex();
6465
checkStl.iterators();
6566
checkStl.mismatchingContainers();
6667
checkStl.erase();
@@ -86,6 +87,11 @@ class CPPCHECKLIB CheckStl : public Check {
8687
*/
8788
void stlOutOfBounds();
8889

90+
/**
91+
* negative index for array like containers
92+
*/
93+
void negativeIndex();
94+
8995
/**
9096
* Finds errors like this:
9197
* for (it = foo.begin(); it != bar.end(); ++it)
@@ -171,6 +177,7 @@ class CPPCHECKLIB CheckStl : public Check {
171177
void string_c_strParam(const Token *tok, unsigned int number);
172178

173179
void stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at);
180+
void negativeIndexError(const Token *tok, const ValueFlow::Value &index);
174181
void invalidIteratorError(const Token *tok, const std::string &iteratorName);
175182
void iteratorsError(const Token *tok, const std::string &container1, const std::string &container2);
176183
void mismatchingContainersError(const Token *tok);
@@ -203,6 +210,7 @@ class CPPCHECKLIB CheckStl : public Check {
203210
c.mismatchingContainersError(nullptr);
204211
c.dereferenceErasedError(nullptr, nullptr, "iter");
205212
c.stlOutOfBoundsError(nullptr, "i", "foo", false);
213+
c.negativeIndexError(nullptr, ValueFlow::Value(-1));
206214
c.invalidIteratorError(nullptr, "push_back|push_front|insert", "iterator");
207215
c.invalidPointerError(nullptr, "push_back", "pointer");
208216
c.stlBoundariesError(nullptr);

test/teststl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class TestStl : public TestFixture {
6060

6161
TEST_CASE(STLSize);
6262
TEST_CASE(STLSizeNoErr);
63+
TEST_CASE(negativeIndex);
6364
TEST_CASE(erase1);
6465
TEST_CASE(erase2);
6566
TEST_CASE(erase3);
@@ -720,7 +721,12 @@ class TestStl : public TestFixture {
720721
}
721722
}
722723

723-
724+
void negativeIndex() {
725+
check("void f(const std::vector<int> &v) {\n"
726+
" v[-11] = 123;\n"
727+
"}");
728+
ASSERT_EQUALS("[test.cpp:2]: (error) Array index -11 is out of bounds.\n", errout.str());
729+
}
724730

725731

726732

0 commit comments

Comments
 (0)