aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2021-12-04 10:56:31 +0100
committerGitHub <noreply@github.com>2021-12-04 10:56:31 +0100
commitd76024b781361cc7b024e0bd240880cfaf1d091f (patch)
tree922f59feb43ec89e9f9c291e48011b2df738afb8 /src
parentc77512d5935be3dce6868f592552f70d7896b193 (diff)
parent45965f2ac677b8d70ee4fcd75545411597cfb3f3 (diff)
downloadPROJ-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.cpp34
-rw-r--r--src/iso19111/io.cpp107
-rw-r--r--src/param.cpp27
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;