diff --git a/lib/checkother.cpp b/lib/checkother.cpp index dfc256967..4773a2a56 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2663,6 +2663,57 @@ void CheckOther::negativeBitwiseShiftError(const Token *tok) reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour"); } +//--------------------------------------------------------------------------- +// Checking for shift by too many bits +//--------------------------------------------------------------------------- + +void CheckOther::checkTooBigBitwiseShift() +{ + // unknown sizeof(int) => can't run this checker + if (_settings->platformType == Settings::Unspecified) + return; + + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + const std::size_t functions = symbolDatabase->functionScopes.size(); + for (std::size_t i = 0; i < functions; ++i) { + const Scope * scope = symbolDatabase->functionScopes[i]; + for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { + if (tok->str() != "<<" && tok->str() != ">>") + continue; + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + // get number of bits of lhs + const Variable *var = tok->astOperand1()->variable(); + if (!var) + continue; + int lhsbits = 0; + for (const Token *type = var->typeStartToken(); type; type = type->next()) { + if (Token::Match(type,"char|short|int") && !type->isLong()) { + lhsbits = _settings->sizeof_int * 8; + break; + } + if (type == var->typeEndToken()) + break; + } + if (lhsbits == 0) + continue; + + // Get biggest rhs value. preferably a value which doesn't have 'condition'. + const ValueFlow::Value *value = tok->astOperand2()->getValueGE(lhsbits, _settings); + if (value) + tooBigBitwiseShiftError(tok, lhsbits, value->intvalue); + } + } +} + +void CheckOther::tooBigBitwiseShiftError(const Token *tok, int lhsbits, MathLib::bigint rhsbits) +{ + std::ostringstream errmsg; + errmsg << "Shifting " << lhsbits << "-bit value by " << rhsbits << " bits is undefined behaviour"; + reportError(tok, Severity::error, "shiftTooManyBits", errmsg.str()); +} //--------------------------------------------------------------------------- // Check for incompletely filled buffers. diff --git a/lib/checkother.h b/lib/checkother.h index 452dd0151..df676ea2a 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -102,6 +102,7 @@ public: checkOther.checkDoubleFree(); checkOther.checkRedundantCopy(); checkOther.checkNegativeBitwiseShift(); + checkOther.checkTooBigBitwiseShift(); checkOther.checkSuspiciousEqualityComparison(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); } @@ -215,9 +216,12 @@ public: /** @brief %Check for code creating redundant copies */ void checkRedundantCopy(); - /** @brief %Check for bitwise operation with negative right operand */ + /** @brief %Check for bitwise shift with negative right operand */ void checkNegativeBitwiseShift(); + /** @brief %Check for bitwise shift with too big right operand */ + void checkTooBigBitwiseShift(); + /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ void checkIncompleteArrayFill(); @@ -286,6 +290,7 @@ private: void SuspiciousSemicolonError(const Token *tok); void doubleCloseDirError(const Token *tok, const std::string &varname); void negativeBitwiseShiftError(const Token *tok); + void tooBigBitwiseShiftError(const Token *tok, int lhsbits, MathLib::bigint rhsbits); void redundantCopyError(const Token *tok, const std::string &varname); void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); void varFuncNullUBError(const Token *tok); @@ -304,6 +309,7 @@ private: c.doubleFreeError(0, "varname"); c.invalidPointerCastError(0, "float", "double", false); c.negativeBitwiseShiftError(0); + c.tooBigBitwiseShiftError(0, 32, 64); c.checkPipeParameterSizeError(0, "varname", "dimension"); //performance @@ -366,6 +372,7 @@ private: "* provide wrong dimensioned array to pipe() system command (--std=posix)\n" "* cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" "* invalid input values for functions\n" + "* bitwise shift by too many bits\n" // warning "* either division by zero or useless condition\n" diff --git a/test/testother.cpp b/test/testother.cpp index affd54eee..724488412 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -148,6 +148,7 @@ private: TEST_CASE(checkRedundantCopy); TEST_CASE(checkNegativeShift); + TEST_CASE(checkTooBigShift); TEST_CASE(incompleteArrayFill); @@ -5384,6 +5385,21 @@ private: ASSERT_EQUALS("", errout.str()); } + void checkTooBigShift() { + Settings settings; + settings.platform(Settings::Unix32); + + check("int foo(int x) {\n" + " return x << 32;\n" + "}","test.cpp",false,false,false,true,&settings); + ASSERT_EQUALS("[test.cpp:2]: (error) Shifting 32-bit value by 32 bits is undefined behaviour\n", errout.str()); + + check("int foo(int x) {\n" + " return x << 2;\n" + "}","test.cpp",false,false,false,true,&settings); + ASSERT_EQUALS("", errout.str()); + } + void incompleteArrayFill() { check("void f() {\n" " int a[5];\n"