Boost logo

Boost :

Subject: [boost] Boost.Filesystem: basename function is not compatible with POSIX; potential for path-related security issues
From: Steve M. Robbins (steve_at_[hidden])
Date: 2010-01-16 20:45:15


I got the following report [1] for Boost.Filesystem from a Debian
user. Before entering into trac, I thought I'd ask whether this
deviation from POSIX is by design or is a bug.

P.S. The original report is based on Boost 1.40, but I
verified the same behaviour on Boost 1.41.


----- Forwarded message from Roger Leigh <rleigh_at_[hidden]> -----

Package: libboost-filesystem1.40.0
Version: 1.40.0-5
Severity: important

The basename function is not compatible with the POSIX function by the
same name:

Path POSIX Boost
test.real test.real test
/usr/bin/perl perl perl
/usr/lib lib lib
/usr/ usr
usr usr usr
/ / /
. .
.. .. .

The test program is attached. Just compile with
  g++ -o testbasename -lboost_filesystem

??? It is not stripping trailing backslashes.

??? "if ph.leaf() contains a dot ('.'), returns the substring of ph.leaf() starting from beginning and ending at the last dot (the dot is not included). Otherwise, returns ph.leaf()". This is wrong, shown by the paths returned for "." ("") and ".." (".") above.

The latter could lead to reading and writing using the wrong path,
which could have security issues if used in a secure context. This
might be justification for raising the severity of this bug.

Looking at the API reference, it looks like extension() and basename()
may be intended to be complementary and are for splitting a filename
into its main part and extension part, *not* the directory and filename
components of a path. This should probably be explicitly spelled out
due to the dangerous confusion which may result if used inappropriately.
In particular, "." and ".." definitely need special casing--these are
not extension separators and basename should return them intact;
extension() should return an empty string.

I noticed this when converting schroot to use the boost convenience
function instead of my own. For reference, this is my version:

sbuild::basename (std::string name,
                  char separator = '/')
  // Remove trailing separators
  std::string::size_type cur = name.length();
  while (cur > 0 && name[cur - 1] == separator)

  // Find last separator
  std::string::size_type pos = name.rfind(separator);

  std::string ret;
  if (pos == std::string::npos)
    ret = name; // No separators
  else if (pos == 0 && name.length() == 1 && name[0] == separator)
    ret = separator; // Only separators
    ret = name.substr(pos + 1); // Basename only

  // Remove any duplicate adjacent path separators
  return remove_duplicates(ret, separator);

A POSIX-compatible dirname() function would nicely complement a
POSIX-compatible basename() function as an addition to
boost::filesystem. It looks like these are orthogonal to the
existing functionality, however.


-- System Information:
Debian Release: squeeze/sid
  APT prefers unstable
  APT policy: (550, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.32-trunk-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages libboost-filesystem1.40.0 depends on:
ii libboost-system1.40.0 1.40.0-5 Operating system (e.g. diagnostics
ii libc6 2.10.2-5 Embedded GNU C Library: Shared lib
ii libgcc1 1:4.4.2-9 GCC support library
ii libstdc++6 4.4.2-9 The GNU Standard C++ Library v3

libboost-filesystem1.40.0 recommends no packages.

libboost-filesystem1.40.0 suggests no packages.

-- no debconf information

#include <libgen.h>

#include <boost/filesystem/convenience.hpp>

#include <cstring>
#include <iostream>
#include <iomanip>

main ()
const char *paths[] =
    "test.real", "/usr/bin/perl", "/usr/lib", "/usr/", "usr", "/", ".", "..", 0

 std::cout << std::setw(16) << std::left << "Path"
           << std::setw(16) << std::left << "POSIX"
           << std::setw(16) << std::left << "Boost" << '\n';
 for (const char **path = &paths[0];
      *path != 0;
     char *bpath = strdup(*path);
     std::cout << std::setw(16) << std::left << *path
               << std::setw(16) << std::left << basename(bpath)
               << std::setw(16) << std::left
               << boost::filesystem::basename(*path)
               << '\n';

 return 0;

pkg-boost-devel mailing list

----- End forwarded message -----

Boost list run by bdawes at, david.abrahams at, gregod at, cpdaniel at, john at