diff --git a/lib/bughuntingchecks.cpp b/lib/bughuntingchecks.cpp index 85e3eec24..1619df626 100644 --- a/lib/bughuntingchecks.cpp +++ b/lib/bughuntingchecks.cpp @@ -24,6 +24,9 @@ #include "token.h" #include +static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer +static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer + static float getKnownFloatValue(const Token *tok, float def) { @@ -34,6 +37,42 @@ static float getKnownFloatValue(const Token *tok, float def) return def; } +static void arrayIndex(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) +{ + if (!Token::simpleMatch(tok->astParent(), "[")) + return; + const Token *buf = tok->astParent()->astOperand1(); + if (!buf || !buf->variable() || !buf->variable()->isArray()) + // TODO + return; + const Token *index = tok->astParent()->astOperand2(); + if (tok != index) + // TODO + return; + if (buf->variable()->dimensions().size() == 1 && buf->variable()->dimensions()[0].known) { + const MathLib::bigint bufSize = buf->variable()->dimensions()[0].num; + if (value.isGreaterThan(dataBase, bufSize - 1)) { + const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue); + dataBase->reportError(tok, + Severity::SeverityType::error, + "bughuntingArrayIndexOutOfBounds", + "Array index out of bounds, cannot determine that " + index->expressionString() + " is less than " + std::to_string(bufSize), + CWE_BUFFER_OVERRUN, + false, + bailout); + } + } + if (value.isLessThan(dataBase, 0)) { + const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue); + dataBase->reportError(tok, + Severity::SeverityType::error, + "bughuntingArrayIndexNegative", + "Array index out of bounds, cannot determine that " + index->expressionString() + " is not negative", + CWE_BUFFER_UNDERRUN, + false, + bailout); + } +} static void bufferOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { @@ -565,6 +604,7 @@ static void checkAssignment(const Token *tok, const ExprEngine::Value &value, Ex void addBughuntingChecks(std::vector *callbacks) { + callbacks->push_back(arrayIndex); callbacks->push_back(bufferOverflow); callbacks->push_back(divByZero); callbacks->push_back(checkFunctionCall); diff --git a/test/testbughuntingchecks.cpp b/test/testbughuntingchecks.cpp index 5229224b4..877e7b679 100644 --- a/test/testbughuntingchecks.cpp +++ b/test/testbughuntingchecks.cpp @@ -36,6 +36,7 @@ private: settings.inconclusive = true; LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(checkAssignment); + TEST_CASE(arrayIndexOutOfBounds1); TEST_CASE(uninit); TEST_CASE(uninit_array); TEST_CASE(uninit_function_par); @@ -67,6 +68,16 @@ private: ASSERT_EQUALS("[test.cpp:2]: (error) There is assignment, cannot determine that value is greater or equal with 0\n", errout.str()); } + void arrayIndexOutOfBounds1() { + check("void foo(int x) {\n" + " int p[8];" + " p[x] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Array index out of bounds, cannot determine that x is less than 8\n" + "[test.cpp:2]: (error) Array index out of bounds, cannot determine that x is not negative\n", + errout.str()); + } + void uninit() { check("void foo() { int x; x = x + 1; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str());