commit 8128173a00c234ccf34e258115747fa0e3b4457a Author: Jonathan Wakely Date: Wed Sep 23 02:00:57 2015 +0100 Limit number of symlinks that canonical() will resolve * src/filesystem/ops.cc (canonical): Simplify error handling and limit number of symlinks that can be resolved. diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index 5ff8120..7b261fb 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -116,6 +116,7 @@ fs::canonical(const path& p, const path& base, error_code& ec) { const path pa = absolute(p, base); path result; + #ifdef _GLIBCXX_USE_REALPATH char_ptr buf{ nullptr }; # if _XOPEN_VERSION < 700 @@ -137,18 +138,9 @@ fs::canonical(const path& p, const path& base, error_code& ec) } #endif - auto fail = [&ec, &result](int e) mutable { - if (!ec.value()) - ec.assign(e, std::generic_category()); - result.clear(); - }; - if (!exists(pa, ec)) - { - fail(ENOENT); - return result; - } - // else we can assume no unresolvable symlink loops + return result; + // else: we know there are (currently) no unresolvable symlink loops result = pa.root_path(); @@ -156,20 +148,19 @@ fs::canonical(const path& p, const path& base, error_code& ec) for (auto& f : pa.relative_path()) cmpts.push_back(f); - while (!cmpts.empty()) + int max_allowed_symlinks = 40; + + while (!cmpts.empty() && !ec) { path f = std::move(cmpts.front()); cmpts.pop_front(); - if (f.compare(".") == 0) + if (is_dot(f)) { - if (!is_directory(result, ec)) - { - fail(ENOTDIR); - break; - } + if (!is_directory(result, ec) && !ec) + ec.assign(ENOTDIR, std::generic_category()); } - else if (f.compare("..") == 0) + else if (is_dotdot(f)) { auto parent = result.parent_path(); if (parent.empty()) @@ -184,27 +175,30 @@ fs::canonical(const path& p, const path& base, error_code& ec) if (is_symlink(result, ec)) { path link = read_symlink(result, ec); - if (!ec.value()) + if (!ec) { - if (link.is_absolute()) - { - result = link.root_path(); - link = link.relative_path(); - } + if (--max_allowed_symlinks == 0) + ec.assign(ELOOP, std::generic_category()); else - result.remove_filename(); + { + if (link.is_absolute()) + { + result = link.root_path(); + link = link.relative_path(); + } + else + result.remove_filename(); - cmpts.insert(cmpts.begin(), link.begin(), link.end()); + cmpts.insert(cmpts.begin(), link.begin(), link.end()); + } } } - - if (ec.value() || !exists(result, ec)) - { - fail(ENOENT); - break; - } } } + + if (ec || !exists(result, ec)) + result.clear(); + return result; }