From b49997e69d7d058ea722d2d7ef8d4a231a514181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 27 Apr 2009 21:29:03 +0200 Subject: [PATCH] bail out instead of crash when parsing unicode code (#207) --- src/cppcheck.cpp | 72 ++++++++++++++++++++++----------------- src/preprocessor.cpp | 3 +- test/testpreprocessor.cpp | 11 ++++++ test/testsuite.cpp | 6 ++++ test/testsuite.h | 2 ++ 5 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/cppcheck.cpp b/src/cppcheck.cpp index e7fa96b95..0c0088f84 100644 --- a/src/cppcheck.cpp +++ b/src/cppcheck.cpp @@ -32,6 +32,7 @@ #include #include #include +#include //--------------------------------------------------------------------------- @@ -297,44 +298,53 @@ unsigned int CppCheck::check() if (_settings._errorsOnly == false) _errorLogger->reportOut(std::string("Checking ") + fname + std::string("...")); - Preprocessor preprocessor; - std::list configurations; - std::string filedata = ""; - if (_fileContents.size() > 0 && _fileContents.find(_filenames[c]) != _fileContents.end()) + try { - // File content was given as a string - std::istringstream iss(_fileContents[ _filenames[c] ]); - preprocessor.preprocess(iss, filedata, configurations, fname, _includePaths); - } - else - { - // Only file name was given, read the content from file - std::ifstream fin(fname.c_str()); - preprocessor.preprocess(fin, filedata, configurations, fname, _includePaths); - } + Preprocessor preprocessor; + std::list configurations; + std::string filedata = ""; - int checkCount = 0; - for (std::list::const_iterator it = configurations.begin(); it != configurations.end(); ++it) - { - // Check only 12 first configurations, after that bail out, unless --force - // was used. - if (!_settings._force && checkCount > 11) + if (_fileContents.size() > 0 && _fileContents.find(_filenames[c]) != _fileContents.end()) { - if (_settings._errorsOnly == false) - _errorLogger->reportOut(std::string("Bailing out from checking ") + fname + ": Too many configurations. Recheck this file with --force if you want to check them all."); - - break; + // File content was given as a string + std::istringstream iss(_fileContents[ _filenames[c] ]); + preprocessor.preprocess(iss, filedata, configurations, fname, _includePaths); + } + else + { + // Only file name was given, read the content from file + std::ifstream fin(fname.c_str()); + preprocessor.preprocess(fin, filedata, configurations, fname, _includePaths); } - cfg = *it; - std::string codeWithoutCfg = Preprocessor::getcode(filedata, *it, fname, _errorLogger); + int checkCount = 0; + for (std::list::const_iterator it = configurations.begin(); it != configurations.end(); ++it) + { + // Check only 12 first configurations, after that bail out, unless --force + // was used. + if (!_settings._force && checkCount > 11) + { + if (_settings._errorsOnly == false) + _errorLogger->reportOut(std::string("Bailing out from checking ") + fname + ": Too many configurations. Recheck this file with --force if you want to check them all."); - // If only errors are printed, print filename after the check - if (_settings._errorsOnly == false && it != configurations.begin()) - _errorLogger->reportOut(std::string("Checking ") + fname + ": " + cfg + std::string("...")); + break; + } - checkFile(codeWithoutCfg, _filenames[c].c_str()); - ++checkCount; + cfg = *it; + std::string codeWithoutCfg = Preprocessor::getcode(filedata, *it, fname, _errorLogger); + + // If only errors are printed, print filename after the check + if (_settings._errorsOnly == false && it != configurations.begin()) + _errorLogger->reportOut(std::string("Checking ") + fname + ": " + cfg + std::string("...")); + + checkFile(codeWithoutCfg, _filenames[c].c_str()); + ++checkCount; + } + } + catch (std::runtime_error &e) + { + // Exception was thrown when checking this file.. + _errorLogger->reportOut("Bailing out from checking " + fname + ": " + e.what()); } _errorLogger->reportStatus(c + 1, _filenames.size()); diff --git a/src/preprocessor.cpp b/src/preprocessor.cpp index 584eceebe..ada2914f9 100644 --- a/src/preprocessor.cpp +++ b/src/preprocessor.cpp @@ -23,6 +23,7 @@ #include "token.h" #include +#include #include #include #include @@ -71,7 +72,7 @@ std::string Preprocessor::read(std::istream &istr) for (char ch = readChar(istr); istr.good(); ch = readChar(istr)) { if (ch < 0) - continue; + throw std::runtime_error("The code contains characters that are unhandled"); if (ch == '\n') ++lineno; diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 5430c1374..df1d5855f 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -27,6 +27,8 @@ #include "../src/tokenize.h" #include #include +#include +#include extern std::ostringstream errout; @@ -115,6 +117,8 @@ private: TEST_CASE(pragma); TEST_CASE(endifsemicolon); TEST_CASE(missing_doublequote); + + TEST_CASE(unicode1); } @@ -850,6 +854,13 @@ private: ASSERT_EQUALS("[abc.h:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } } + + void unicode1() + { + const char filedata[] = {'a', (char)200, 0}; + std::istringstream istr(filedata); + ASSERT_THROW(Preprocessor::read(istr), std::runtime_error); + } }; REGISTER_TEST(TestPreprocessor) diff --git a/test/testsuite.cpp b/test/testsuite.cpp index 3c308b524..964b20b03 100644 --- a/test/testsuite.cpp +++ b/test/testsuite.cpp @@ -123,6 +123,12 @@ void TestFixture::assertEquals(const char *filename, int linenr, unsigned int ex assertEquals(filename, linenr, ostr1.str(), ostr2.str()); } +void TestFixture::assertThrowFail(const char *filename, int linenr) +{ + errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl + << "The expected exception was not thrown" << std::endl; +} + void TestFixture::printTests() { const std::list &tests = TestRegistry::theInstance().tests(); diff --git a/test/testsuite.h b/test/testsuite.h index 7bcf4a847..dd41a469a 100644 --- a/test/testsuite.h +++ b/test/testsuite.h @@ -41,6 +41,7 @@ protected: void assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual); void assertEquals(const char *filename, int linenr, unsigned int expected, unsigned int actual); + void assertThrowFail(const char *filename, int linenr); public: virtual void reportOut(const std::string &outmsg); @@ -58,6 +59,7 @@ public: #define TEST_CASE( NAME ) if ( runTest(#NAME) ) NAME (); #define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) +#define ASSERT_THROW( CMD, EXCEPTION ) try { CMD ; assertThrowFail(__FILE__, __LINE__); } catch (EXCEPTION &) { } catch (...) { assertThrowFail(__FILE__, __LINE__); } #define TODO_ASSERT_EQUALS( EXPECTED , ACTUAL ) if (EXPECTED==ACTUAL) assertEquals(__FILE__, __LINE__, "TODO assertion", "The assertion succeeded") #define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; }