Skip to content

Commit

Permalink
PROJStringParser::createFromPROJString(): avoid potential infinite re…
Browse files Browse the repository at this point in the history
…cursion (fixes #1574)

The exact circumstances are a bit difficult to explain, but they involve
using a non-default context, enabling proj_context_use_proj4_init_rules() on it,
using proj_create(ctxt, "+init=epsg:XXXX +type=crs"), whereas PROJ_LIB is
defined to a directory that has a 'epsg' file in it.
  • Loading branch information
rouault committed Sep 5, 2019
1 parent 06578ae commit 9c38bab
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 25 deletions.
72 changes: 48 additions & 24 deletions src/iso19111/io.cpp
Expand Up @@ -8638,6 +8638,21 @@ static const metadata::ExtentPtr &getExtent(const crs::CRS *crs) {

//! @endcond

namespace {
struct PJContextHolder {
PJ_CONTEXT *ctx_;
bool bFree_;

PJContextHolder(PJ_CONTEXT *ctx, bool bFree) : ctx_(ctx), bFree_(bFree) {}
~PJContextHolder() {
if (bFree_)
proj_context_destroy(ctx_);
}
PJContextHolder(const PJContextHolder &) = delete;
PJContextHolder &operator=(const PJContextHolder &) = delete;
};
} // namespace

// ---------------------------------------------------------------------------

/** \brief Instantiate a sub-class of BaseObject from a PROJ string.
Expand All @@ -8652,8 +8667,10 @@ PROJStringParser::createFromPROJString(const std::string &projString) {

// In some abnormal situations involving init=epsg:XXXX syntax, we could
// have infinite loop
if (d->ctx_ && d->ctx_->curStringInCreateFromPROJString == projString) {
throw ParsingException("invalid PROJ string");
if (d->ctx_ &&
d->ctx_->projStringParserCreateFromPROJStringRecursionCounter == 2) {
throw ParsingException(
"Infinite recursion in PROJStringParser::createFromPROJString()");
}

d->steps_.clear();
Expand Down Expand Up @@ -8701,27 +8718,38 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
const std::string &stepName = d->steps_[0].name;
if (ci_starts_with(stepName, "epsg:") ||
ci_starts_with(stepName, "IGNF:")) {

/* We create a new context so as to avoid messing up with the */
/* errorno of the main context, when trying to find the likely */
/* missing epsg file */
auto ctx = proj_context_create();
if (!ctx) {
throw ParsingException("out of memory");
}
PJContextHolder contextHolder(ctx, true);
if (d->ctx_) {
ctx->set_search_paths(d->ctx_->search_paths);
ctx->file_finder = d->ctx_->file_finder;
ctx->file_finder_legacy = d->ctx_->file_finder_legacy;
ctx->file_finder_user_data = d->ctx_->file_finder_user_data;
}

bool usePROJ4InitRules = d->usePROJ4InitRules_;
if (!usePROJ4InitRules) {
PJ_CONTEXT *ctx = proj_context_create();
if (ctx) {
usePROJ4InitRules = proj_context_get_use_proj4_init_rules(
ctx, FALSE) == TRUE;
proj_context_destroy(ctx);
}
usePROJ4InitRules =
proj_context_get_use_proj4_init_rules(ctx, FALSE) == TRUE;
}
if (!usePROJ4InitRules) {
throw ParsingException("init=epsg:/init=IGNF: syntax not "
"supported in non-PROJ4 emulation mode");
}

PJ_CONTEXT *ctx = proj_context_create();
char unused[256];
std::string initname(stepName);
initname.resize(initname.find(':'));
int file_found =
pj_find_file(ctx, initname.c_str(), unused, sizeof(unused));
proj_context_destroy(ctx);

if (!file_found) {
auto obj = createFromUserInput(stepName, d->dbContext_, true);
auto crs = dynamic_cast<CRS *>(obj.get());
Expand Down Expand Up @@ -8790,19 +8818,19 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
}
}

paralist *init = pj_mkparam(("init=" + d->steps_[0].name).c_str());
if (!init) {
auto ctx = d->ctx_ ? d->ctx_ : proj_context_create();
if (!ctx) {
throw ParsingException("out of memory");
}
PJ_CONTEXT *ctx = d->ctx_ ? d->ctx_ : proj_context_create();
if (!ctx) {
pj_dealloc(init);
PJContextHolder contextHolder(ctx, ctx != d->ctx_);

paralist *init = pj_mkparam(("init=" + d->steps_[0].name).c_str());
if (!init) {
throw ParsingException("out of memory");
}
ctx->projStringParserCreateFromPROJStringRecursionCounter++;
paralist *list = pj_expand_init(ctx, init);
if (ctx != d->ctx_) {
proj_context_destroy(ctx);
}
ctx->projStringParserCreateFromPROJStringRecursionCounter--;
if (!list) {
pj_dealloc(init);
throw ParsingException("cannot expand " + projString);
Expand Down Expand Up @@ -8933,18 +8961,14 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
proj_log_func(pj_context, &logger, Logger::log);
proj_context_use_proj4_init_rules(pj_context, d->usePROJ4InitRules_);
}
if (d->ctx_) {
d->ctx_->curStringInCreateFromPROJString = projString;
}
pj_context->projStringParserCreateFromPROJStringRecursionCounter++;
auto log_level = proj_log_level(pj_context, PJ_LOG_NONE);
auto pj = pj_create_internal(
pj_context, (projString.find("type=crs") != std::string::npos
? projString + " +disable_grid_presence_check"
: projString)
.c_str());
if (d->ctx_) {
d->ctx_->curStringInCreateFromPROJString.clear();
}
pj_context->projStringParserCreateFromPROJStringRecursionCounter--;
valid = pj != nullptr;
proj_log_level(pj_context, log_level);

Expand Down
2 changes: 1 addition & 1 deletion src/proj_internal.h
Expand Up @@ -706,7 +706,7 @@ struct projCtx_t {
const char* (*file_finder) (PJ_CONTEXT *, const char*, void* user_data) = nullptr;
void* file_finder_user_data = nullptr;

std::string curStringInCreateFromPROJString{};
int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString()

projCtx_t() = default;
projCtx_t(const projCtx_t&);
Expand Down

0 comments on commit 9c38bab

Please sign in to comment.