diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2021-12-04 10:56:31 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-12-04 10:56:31 +0100 |
| commit | d76024b781361cc7b024e0bd240880cfaf1d091f (patch) | |
| tree | 922f59feb43ec89e9f9c291e48011b2df738afb8 /src | |
| parent | c77512d5935be3dce6868f592552f70d7896b193 (diff) | |
| parent | 45965f2ac677b8d70ee4fcd75545411597cfb3f3 (diff) | |
| download | PROJ-d76024b781361cc7b024e0bd240880cfaf1d091f.tar.gz PROJ-d76024b781361cc7b024e0bd240880cfaf1d091f.zip | |
Merge pull request #2968 from rouault/fix_ossfuzz_41462
Fix extremely long parsing time on hostile PROJ strings
Diffstat (limited to 'src')
| -rw-r--r-- | src/internal.cpp | 34 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 107 | ||||
| -rw-r--r-- | src/param.cpp | 27 |
3 files changed, 69 insertions, 99 deletions
diff --git a/src/internal.cpp b/src/internal.cpp index e934069f..b7648924 100644 --- a/src/internal.cpp +++ b/src/internal.cpp @@ -326,6 +326,31 @@ argument string, args, and count its number of elements. } +static void unquote_string(char* param_str) { + + size_t len = strlen(param_str); + // Remove leading and terminating spaces after equal sign + const char* equal = strstr(param_str, "=\""); + if( equal && equal - param_str + 1 >= 2 && param_str[len-1] == '"' ) { + size_t dst = equal + 1 - param_str; + size_t src = dst + 1; + for( ; param_str[src]; dst++, src++) + { + if( param_str[src] == '"' ) { + if( param_str[src+1] == '"' ) { + src++; + } else { + break; + } + } + param_str[dst] = param_str[src]; + } + param_str[dst] = '\0'; + } + +} + + /*****************************************************************************/ char **pj_trim_argv (size_t argc, char *args) { @@ -349,7 +374,6 @@ It is the duty of the caller to free this array. if (0==argc) return nullptr; - /* turn the input string into an array of strings */ char** argv = (char **) calloc (argc, sizeof (char *)); if (nullptr==argv) @@ -359,6 +383,7 @@ It is the duty of the caller to free this array. char* str = argv[j]; size_t nLen = strlen(str); i += nLen + 1; + unquote_string(str); } return argv; } @@ -370,7 +395,11 @@ std::string pj_double_quote_string_param_if_needed(const std::string& str) { if( str.find(' ') == std::string::npos ) { return str; } - return '"' + replaceAll(str, "\"", "\"\"") + '"'; + std::string ret; + ret += '"'; + ret += replaceAll(str, "\"", "\"\""); + ret += '"'; + return ret; } /*****************************************************************************/ @@ -383,7 +412,6 @@ Allocates, and returns, an array of char, large enough to hold a whitespace separated copy of the args in argv. It is the duty of the caller to free this array. ******************************************************************************/ - try { std::string s; diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index f8a4672a..fde56d66 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -8248,58 +8248,43 @@ static void PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, std::vector<Step::KeyValue> &globalParamValues, std::string &title) { - const char *c_str = projString.c_str(); std::vector<std::string> tokens; bool hasProj = false; bool hasInit = false; bool hasPipeline = false; - { - size_t i = 0; - while (true) { - for (; isspace(static_cast<unsigned char>(c_str[i])); i++) { - } - std::string token; - bool in_string = false; - for (; c_str[i]; i++) { - if (in_string) { - if (c_str[i] == '"' && c_str[i + 1] == '"') { - i++; - } else if (c_str[i] == '"') { - in_string = false; - continue; - } - } else if (c_str[i] == '=' && c_str[i + 1] == '"') { - in_string = true; - token += c_str[i]; - i++; - continue; - } else if (isspace(static_cast<unsigned char>(c_str[i]))) { - break; - } - token += c_str[i]; - } - if (in_string) { - throw ParsingException("Unbalanced double quote"); - } - if (token.empty()) { - break; - } - if (!hasPipeline && - (token == "proj=pipeline" || token == "+proj=pipeline")) { - hasPipeline = true; - } else if (!hasProj && (starts_with(token, "proj=") || - starts_with(token, "+proj="))) { - hasProj = true; - } else if (!hasInit && (starts_with(token, "init=") || - starts_with(token, "+init="))) { - hasInit = true; - } - tokens.emplace_back(token); + + std::string projStringModified(projString); + + // Special case for "+title=several words +foo=bar" + if (starts_with(projStringModified, "+title=") && + projStringModified.size() > 7 && projStringModified[7] != '"') { + const auto plusPos = projStringModified.find(" +", 1); + const auto spacePos = projStringModified.find(' '); + if (plusPos != std::string::npos && spacePos != std::string::npos && + spacePos < plusPos) { + std::string tmp("+title="); + tmp += pj_double_quote_string_param_if_needed( + projStringModified.substr(7, plusPos - 7)); + tmp += projStringModified.substr(plusPos); + projStringModified = std::move(tmp); } } - bool prevWasTitle = false; + size_t argc = pj_trim_argc(&projStringModified[0]); + char **argv = pj_trim_argv(argc, &projStringModified[0]); + for (size_t i = 0; i < argc; i++) { + std::string token(argv[i]); + if (!hasPipeline && token == "proj=pipeline") { + hasPipeline = true; + } else if (!hasProj && starts_with(token, "proj=")) { + hasProj = true; + } else if (!hasInit && starts_with(token, "init=")) { + hasInit = true; + } + tokens.emplace_back(token); + } + free(argv); if (!hasPipeline) { if (hasProj || hasInit) { @@ -8307,16 +8292,8 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, } for (auto &word : tokens) { - if (word[0] == '+') { - word = word.substr(1); - } else if (prevWasTitle && word.find('=') == std::string::npos) { - title += " "; - title += word; - continue; - } - - prevWasTitle = false; - if (starts_with(word, "proj=") && !hasInit) { + if (starts_with(word, "proj=") && !hasInit && + steps.back().name.empty()) { assert(hasProj); auto stepName = word.substr(strlen("proj=")); steps.back().name = stepName; @@ -8331,7 +8308,6 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, } } else if (starts_with(word, "title=")) { title = word.substr(strlen("title=")); - prevWasTitle = true; } else if (word != "step") { const auto pos = word.find('='); auto key = word.substr(0, pos); @@ -8352,15 +8328,6 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, bool inPipeline = false; bool invGlobal = false; for (auto &word : tokens) { - if (word[0] == '+') { - word = word.substr(1); - } else if (prevWasTitle && word.find('=') == std::string::npos) { - title += " "; - title += word; - continue; - } - - prevWasTitle = false; if (word == "proj=pipeline") { if (inPipeline) { throw ParsingException("nested pipeline not supported"); @@ -8388,7 +8355,6 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps, steps.back().isInit = true; } else if (!inPipeline && starts_with(word, "title=")) { title = word.substr(strlen("title=")); - prevWasTitle = true; } else { const auto pos = word.find('='); auto key = word.substr(0, pos); @@ -10518,7 +10484,8 @@ PROJStringParser::createFromPROJString(const std::string &projString) { std::string expanded; if (!d->title_.empty()) { expanded = "title="; - expanded += d->title_; + expanded += + pj_double_quote_string_param_if_needed(d->title_); } for (const auto &pair : d->steps_[0].paramValues) { if (!expanded.empty()) @@ -10527,7 +10494,8 @@ PROJStringParser::createFromPROJString(const std::string &projString) { expanded += pair.key; if (!pair.value.empty()) { expanded += '='; - expanded += pair.value; + expanded += pj_double_quote_string_param_if_needed( + pair.value); } } expanded += ' '; @@ -10557,7 +10525,8 @@ PROJStringParser::createFromPROJString(const std::string &projString) { } std::string expanded; if (!d->title_.empty()) { - expanded = "title=" + d->title_; + expanded = + "title=" + pj_double_quote_string_param_if_needed(d->title_); } bool first = true; bool has_init_term = false; @@ -10583,7 +10552,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) { expanded += pair.key; if (!pair.value.empty()) { expanded += '='; - expanded += pair.value; + expanded += pj_double_quote_string_param_if_needed(pair.value); } } diff --git a/src/param.cpp b/src/param.cpp index 21afc57f..0a9a66d8 100644 --- a/src/param.cpp +++ b/src/param.cpp @@ -9,31 +9,6 @@ #include "proj.h" #include "proj_internal.h" -static void unquote_string(char* param_str) { - - size_t len = strlen(param_str); - // Remove leading and terminating spaces after equal sign - const char* equal = strstr(param_str, "=\""); - if( equal && equal - param_str + 1 > 2 && param_str[len-1] == '"' ) { - size_t dst = equal + 1 - param_str; - size_t src = dst + 1; - for( ; param_str[src]; dst++, src++) - { - if( param_str[src] == '"' ) { - if( param_str[src+1] == '"' ) { - src++; - } else { - break; - } - } - param_str[dst] = param_str[src]; - } - param_str[dst] = '\0'; - } - -} - - /* create parameter list entry */ paralist *pj_mkparam(const char *str) { paralist *newitem; @@ -44,7 +19,6 @@ paralist *pj_mkparam(const char *str) { if (*str == '+') ++str; (void)strcpy(newitem->param, str); - unquote_string(newitem->param); } return newitem; } @@ -86,7 +60,6 @@ paralist *pj_mkparam_ws (const char *str, const char **next_str) { if (nullptr==newitem) return nullptr; memcpy(newitem->param, str, len); - unquote_string(newitem->param); newitem->used = 0; newitem->next = nullptr; |
