diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 83f143676..fac3e9e46 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -2222,3 +2222,51 @@ void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::s "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will " "not be accessed if the index is out of limits."); } + +// ------------------------------------------------------------------------------------- +// Check the second and the third parameter of the POSIX function write and validate +// their values. +// The parameters have the following meaning: +// - 1.parameter: file descripter (not required for this check) +// - 2.parameter: is a null terminated character string of the content to write. +// - 3.parameter: the number of bytes to write. +// +// This check is triggered if the size of the string ( 2. parameter) is lower than +// the number of bytes provided at the 3. parameter. +// +// References: +// - http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_075.htm +// - http://codewiki.wikidot.com/c:system-calls:write +// ------------------------------------------------------------------------------------- +void CheckBufferOverrun::writeOutsideBufferSize() +{ + if (!_settings->standards.posix) + 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; tok && tok != scope->classEnd; tok = tok->next()) { + if (Token::Match(tok, "pwrite|write ( %any% , %str% , %num%")) { + const std::string functionName(tok->str()); + tok = tok->tokAt(4); // set tokenptr to %str% parameter + const std::size_t stringLength = Token::getStrLength(tok); + tok = tok->tokAt(2); // set tokenptr to %num% parameter + const MathLib::bigint writeLength = MathLib::toLongNumber(tok->str()); + if (static_cast(writeLength) > stringLength) + writeOutsideBufferSizeError(tok, stringLength, writeLength,functionName); + } + } + } +} + +void CheckBufferOverrun::writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string &strFunctionName) +{ + reportError(tok, Severity::error, "writeOutsideBufferSize", + "Writing '" +MathLib::longToString(writeLength-stringLength)+"' bytes outside buffer size.\n" + "The number of bytes to write ('" +MathLib::longToString(writeLength)+ "' bytes) are bigger than the source buffer ('" +MathLib::longToString(stringLength)+ "' bytes)." + " Please check the second and the third parameter of the function '"+strFunctionName+"'."); +} +// ------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------- diff --git a/lib/checkbufferoverrun.h b/lib/checkbufferoverrun.h index e6ea30bc5..3909a175d 100644 --- a/lib/checkbufferoverrun.h +++ b/lib/checkbufferoverrun.h @@ -67,6 +67,7 @@ public: /** ExecutionPath checking.. */ checkBufferOverrun.executionPaths(); + checkBufferOverrun.writeOutsideBufferSize(); } /** @brief %Check for buffer overruns */ @@ -78,6 +79,9 @@ public: /** @brief %Check for buffer overruns by inspecting execution paths */ void executionPaths(); + /** @brief %Check using POSIX write function and writing outside buffer size */ + void writeOutsideBufferSize(); + /** * @brief Get minimum length of format string result * @param input_string format string @@ -229,6 +233,7 @@ private: void possibleBufferOverrunError(const Token *tok, const std::string &src, const std::string &dst, bool cat); void possibleReadlinkBufferOverrunError(const Token *tok, const std::string &funcname, const std::string &varname); void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname); + void writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string& functionName); public: void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { @@ -249,6 +254,7 @@ public: c.possibleBufferOverrunError(0, "source", "destination", false); c.possibleReadlinkBufferOverrunError(0, "readlink", "buffer"); c.argumentSizeError(0, "function", "array"); + c.writeOutsideBufferSizeError(0,2,3,"write"); } private: diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index e13b2280f..699e95e4c 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -58,6 +58,7 @@ private: checkBufferOverrun.bufferOverrun(); checkBufferOverrun.negativeIndex(); checkBufferOverrun.arrayIndexThenCheck(); + checkBufferOverrun.writeOutsideBufferSize(); } void run() { @@ -252,6 +253,8 @@ private: TEST_CASE(bufferNotZeroTerminated); TEST_CASE(readlink); TEST_CASE(readlinkat); + + TEST_CASE(writeOutsideBufferSize) } @@ -4090,6 +4093,18 @@ private: "}"); ASSERT_EQUALS("", errout.str()); } + + void writeOutsideBufferSize() { + check("void f(void){\n" + "write(1, \"Dump string \\n\", 100);\n" + "}"); // ^ number of bytes too big + ASSERT_EQUALS("[test.cpp:2]: (error) Writing '87' bytes outside buffer size.\n", errout.str()); + + check("void f(void){\n" + "write(1, \"Dump string \\n\", 10);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestBufferOverrun)