From 751e8cf76b9d2c792f595088337618af61c1ad04 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 10 Nov 2021 21:25:16 +0100 Subject: [PATCH] Do a bounds check before calling functions defined in cctype --- apps/niftest/niftest.cpp | 14 +++----------- apps/opencs/model/filter/parser.cpp | 21 +++++++++++++++++---- apps/opencs/model/world/idtable.cpp | 7 +++---- apps/openmw/mwinput/keyboardmanager.cpp | 2 +- components/files/escape.hpp | 2 +- components/interpreter/defines.cpp | 3 +-- components/lua/configuration.cpp | 15 ++++++++++----- components/misc/stringops.hpp | 15 +++++++-------- components/settings/parser.cpp | 2 +- 9 files changed, 44 insertions(+), 37 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index cb6205ef5c..c42df4a423 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -2,8 +2,8 @@ #include #include -#include +#include #include #include #include @@ -18,18 +18,10 @@ namespace bpo = boost::program_options; namespace bfs = boost::filesystem; ///See if the file has the named extension -bool hasExtension(std::string filename, std::string extensionToFind) +bool hasExtension(std::string filename, std::string extensionToFind) { std::string extension = filename.substr(filename.find_last_of('.')+1); - - //Convert strings to lower case for comparison - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower); - - if(extension == extensionToFind) - return true; - else - return false; + return Misc::StringUtils::ciEqual(extension, extensionToFind); } ///See if the file has the "nif" extension. diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index d363b4849d..b8f125e237 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -17,6 +17,19 @@ #include "textnode.hpp" #include "valuenode.hpp" +namespace +{ + bool isAlpha(char c) + { + return c >= 0 && c <= 255 && std::isalpha(c); + } + + bool isDigit(char c) + { + return c >= 0 && c <= 255 && std::isdigit(c); + } +} + namespace CSMFilter { struct Token @@ -103,7 +116,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken() { char c = mInput[mIndex]; - if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' || + if (isAlpha(c) || c==':' || c=='_' || (!string.empty() && isDigit(c)) || c=='"' || (!string.empty() && string[0]=='"')) string += c; else @@ -150,7 +163,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken() { char c = mInput[mIndex]; - if (std::isdigit (c)) + if (isDigit(c)) { string += c; hasDigit = true; @@ -225,10 +238,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken() case '!': ++mIndex; return Token (Token::Type_OneShot); } - if (c=='"' || c=='_' || std::isalpha (c) || c==':') + if (c=='"' || c=='_' || isAlpha(c) || c==':') return getStringToken(); - if (c=='-' || c=='.' || std::isdigit (c)) + if (c=='-' || c=='.' || isDigit(c)) return getNumberToken(); error(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 5b4a9b31bc..215f42133d 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "collectionbase.hpp" #include "columnbase.hpp" @@ -354,8 +355,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import for (int i = 0; i < idCollection()->getSize(); ++i) { auto& record = static_cast&>(idCollection()->getRecord(i)); - std::string texture = record.get().mTexture; - std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture); if (record.isModified()) reverseLookupMap.emplace(texture, idCollection()->getId(i)); } @@ -376,8 +376,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // Look for a pre-existing record auto& record = static_cast&>(idCollection()->getRecord(oldRow)); - std::string texture = record.get().mTexture; - std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture); auto searchIt = reverseLookupMap.find(texture); if (searchIt != reverseLookupMap.end()) { diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index b8019b12ba..c5a665e5c6 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -42,7 +42,7 @@ namespace MWInput bool consumed = SDL_IsTextInputActive() && // Little trick to check if key is printable (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && - (std::isprint(arg.keysym.sym) || + (arg.keysym.sym >= 0 && arg.keysym.sym <= 255 && std::isprint(arg.keysym.sym) || // Don't trust isprint for symbols outside the extended ASCII range (kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff))); if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState()) diff --git a/components/files/escape.hpp b/components/files/escape.hpp index d01bd8d980..310042072d 100644 --- a/components/files/escape.hpp +++ b/components/files/escape.hpp @@ -84,7 +84,7 @@ namespace Files { mNext.push(character); } - if (!mSeenNonWhitespace && !isspace(character)) + if (!mSeenNonWhitespace && !(character >= 0 && character <= 255 && isspace(character))) mSeenNonWhitespace = true; } int retval = mNext.front(); diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 48d2ceaa45..164e7b2126 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -172,8 +172,7 @@ namespace Interpreter{ for(unsigned int j = 0; j < globals.size(); j++){ if(globals[j].length() > temp.length()){ // Just in case there's a global with a huuuge name - temp = text.substr(i+1, globals[j].length()); - transform(temp.begin(), temp.end(), temp.begin(), ::tolower); + temp = Misc::StringUtils::lowerCase(text.substr(i+1, globals[j].length())); } found = check(temp, globals[j], &i, &start); diff --git a/components/lua/configuration.cpp b/components/lua/configuration.cpp index 4598ed2508..d059d1716f 100644 --- a/components/lua/configuration.cpp +++ b/components/lua/configuration.cpp @@ -30,6 +30,11 @@ namespace LuaUtil {"POTION", ESM::LuaScriptCfg::sPotion}, {"WEAPON", ESM::LuaScriptCfg::sWeapon}, }; + + bool isSpace(int c) + { + return c >= 0 && c <= 255 && std::isspace(c); + } } const std::vector ScriptsConfiguration::sEmpty; @@ -101,11 +106,11 @@ namespace LuaUtil if (!line.empty() && line.back() == '\r') line = line.substr(0, line.size() - 1); - while (!line.empty() && std::isspace(line[0])) + while (!line.empty() && isSpace(line[0])) line = line.substr(1); if (line.empty() || line[0] == '#') // Skip empty lines and comments continue; - while (!line.empty() && std::isspace(line.back())) + while (!line.empty() && isSpace(line.back())) line = line.substr(0, line.size() - 1); if (!Misc::StringUtils::ciEndsWith(line, ".lua")) @@ -118,7 +123,7 @@ namespace LuaUtil throw std::runtime_error(Misc::StringUtils::format("No flags found in: %s", std::string(line))); std::string_view flagsStr = line.substr(0, semicolonPos); std::string_view scriptPath = line.substr(semicolonPos + 1); - while (std::isspace(scriptPath[0])) + while (isSpace(scriptPath[0])) scriptPath = scriptPath.substr(1); // Parse flags @@ -126,10 +131,10 @@ namespace LuaUtil size_t flagsPos = 0; while (true) { - while (flagsPos < flagsStr.size() && (std::isspace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) + while (flagsPos < flagsStr.size() && (isSpace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) flagsPos++; size_t startPos = flagsPos; - while (flagsPos < flagsStr.size() && !std::isspace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') + while (flagsPos < flagsStr.size() && !isSpace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') flagsPos++; if (startPos == flagsPos) break; diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 12633db826..a0ec9f62dd 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -184,17 +184,16 @@ public: static inline void trim(std::string &s) { - // left trim - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) + const auto notSpace = [](int ch) { - return !std::isspace(ch); - })); + // TODO Do we care about multibyte whitespace? + return ch < 0 || ch > 255 || !std::isspace(ch); + }; + // left trim + s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace)); // right trim - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) - { - return !std::isspace(ch); - }).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), notSpace).base(), s.end()); } template diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index f2419dfdd6..f36f190ee2 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -311,7 +311,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con bool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str) { - while (i < str.size() && std::isspace(str[i], std::locale::classic())) + while (i < str.size() && str[i] >= 0 && str[i] <= 255 && std::isspace(str[i], std::locale::classic())) { ++i; }