Opened 5 years ago

Last modified 5 years ago

#13028 new Bugs

boost::filesystem::canonical(const path& p, system::error_code& ec) throws exception

Reported by: Zach Wasserman <zachwass2000@…> Owned by: Beman Dawes
Milestone: To Be Determined Component: filesystem
Version: Boost 1.65.0 Severity: Problem
Keywords: Cc:

Description

Per the docs, this function should report filesystem errors through the error_code. In the case of permission issues, this contract is violated.

This is best explained with the test case:

#include <boost/detail/lightweight_test_report.hpp>
#include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int test_main(int, char*[])
{
  // Ensure that we do not have read permissions on pwd
  fs::path tmp_path = fs::temp_directory_path();
  fs::path unique_path = fs::unique_path();
  fs::path dir_path = tmp_path / unique_path;
  fs::create_directory(dir_path);
  fs::current_path(dir_path);
  fs::permissions(dir_path, fs::no_perms);

  // Try to get a canonical path with the error_code API. This should return an
  // error through the error_code, but instead throws an exception (because
  // canonical(const path& p, system::error_code& ec) calls current_path()
  // without error_code)
  boost::system::error_code e;
  fs::canonical("foo", e);
  BOOST_TEST(e.value() != 0);

  return ::boost::report_errors();
}

Test output:

bin/bug
Clang version 8.0.0 (clang-800.0.38), __GXX_EXPERIMENTAL_CXX0X__ not defined
libc++ version 3700
Mac OS
Boost version 1.65.0
Command line: bin/bug

ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR  ERROR

****************************** std::exception *****************************
boost::filesystem::current_path: Permission denied
***************************************************************************

This unexpected exception causes a crash in osquery. See https://github.com/facebook/osquery/issues/3279

Change History (1)

comment:1 by anonymous, 5 years ago

I just ran into the same issue in one of our internal applications. There are actually 2 bugs causing the same problem. The first is that the canonical(const path& p, system::error_code& ec) calls current_path() without passing in the error_code. However, when that is fixed, then same problem shows up again if you pass in a relative path as base because detail::canonical() immediately calls absolute(), which in turn calls current_path().

It's easy to fix by simply calling the no except version of current_path() in both places (although, since absolute() doesn't take an error_code, it can get a bit wordy):

operations.hpp:

  inline
  path canonical(const path& p, system::error_code& ec)
  {
      path base = detail::current_path(&ec);
      if (ec)
          return path();
      return detail::canonical(p, base, &ec);
  }

operations.cpp

  BOOST_FILESYSTEM_DECL
  path canonical(const path& p, const path& base, system::error_code* ec)
  {
    path source(p);
    if (!p.is_absolute()) {
        path absBase(base);
        if (!absBase.is_absolute()) {
            path curr = current_path(ec);
            if (ec && *ec)
                return path();
            absBase = absolute(base, curr);
        }
        source = absolute(p, absBase);
    }
Note: See TracTickets for help on using tickets.