[Bug 197778] Implement the AT_EMPTY_PATH race free Linux extension

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Wed Feb 18 02:07:57 UTC 2015


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197778

            Bug ID: 197778
           Summary: Implement the AT_EMPTY_PATH race free Linux extension
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs at FreeBSD.org
          Reporter: s_bugzilla at nedprod.com

Related: #197695

Due to only accepting paths, POSIX makes it hard to unlink and rename files
without creating race conditions. For example to unlink a file as safely as is
possible under POSIX:

1. Get one of the current paths of the open file descriptor using any facility
provided if #197695 were implement.

2. Open its containing directory.

3. Do a fstatat() on the containing directory for the leafname of the open file
descriptor, checking if the device ids and inodes match the ones for our file
descriptor.

4. If they match, do an unlinkat() to remove the leafname. NOTE THIS IS RACY as
another program could swap our leafname for another between the fstatat and the
unlinkat.

This raciness is disagreeable in this modern day and age. Moreover, Linux has
partially added an extension via the AT_EMPTY_PATH flag which enables the
XXXat() suite of functions work directly with the file descriptor. At the time
of writing (Feb 2015), Linux implements the AT_EMPTY_PATH extension for:

linkat(), fchownat(), fstatat(), name_to_handle_at().

The AT_EMPTY_PATH extension works as follows, so to create a new hard link for
some file descriptor:

linkat(fd, "", dirh, "name", AT_EMPTY_PATH)

Note the empty path, and the passing in of a file descriptor which may or may
not refer to a directory as the olddirh fd normally must.

There are some obviously useful missing functions which could do with the
AT_EMPTY_PATH extension:

unlinkat(), renameat().

I have filed a feature request for those with the Linux kernel at
https://bugzilla.kernel.org/show_bug.cgi?id=93441. Note that because Linux
allows the direct opening of symlinks via the flag O_PATH, on Linux you can use
linkat() to duplicate a symlink race free. As FreeBSD doesn't provide the
ability to directly open symlinks, I'd actually make the total list of
functions which this request asks for the AT_EMPTY_PATH extension upon to be:

linkat(), fchownat(), fstatat(), unlinkat(), renameat(), symlinkat(),
faccessat(), fchmodat().

Of these, renameat() and symlinkat() currently do not take a flags parameter.
You could varargs those, or else enable the above functions to treat a null
path fragment as equivalent to the dirh being the source filing system entry
being modified i.e. a null path fragment = AT_EMPTY_PATH semantics.

Or indeed additional f* variants of the non-at functions could be added which
consume file descriptors instead of paths e.g. the oft wished for flink() (more
discussion is at http://lwn.net/Articles/562488/). The point here is making it
possible to use the filing system without any possibility of race conditions,
something only Microsoft Windows currently permits.

Some may observe that all this is a potential security hole e.g. child
processes supposedly sandboxed could unlink or cause mischief with file
descriptors they inherit, or otherwise destroy data. A similar argument could
be fielded against #197695, except that every major OS except for FreeBSD has
provided fd to path reading for years and I am unaware of any security issues
which have emerged. For obvious reasons any of the above functions should fail
if their path based equivalent would fail from the privileges available to a
child process.

Niall

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list