openat2 (BZ 31664) vs FreeBSD-style O_RESOLVE_BENEATH

Aleksa Sarai cyphar@cyphar.com
Tue Nov 18 23:54:48 GMT 2025


On 2025-11-18, Paul Eggert <eggert@cs.ucla.edu> wrote:
> When I changed GNU tar to use openat2[1], I found that the only new feature
> that I used was its RESOLVE_BENEATH flag.

We needed RESOLVE_IN_ROOT for container runtimes, which was the main
use-case I had in mind. RESOLVE_BENEATH has also been quite useful, but
I think by far the most useful thing is RESOLVE_NO_XDEV -- before this,
it was not really possible to properly detect mount-point crossings in a
truly race-free way.

(The best you could do is look at the mount id in /proc/self/fdinfo/$n
but that has theoretical risks when dealing with container runtimes
where the attacker can modify the mount table. It also requires you to
do the path lookup in userspace because an attacker could misdirect the
open to a different file on the same mount with symlinks.)

> FreeBSD did this in a different way, by adding O_RESOLVE_BENEATH as a flag
> to plain openat, and by adding AT_RESOLVE_BENEATH as a flag for fstatat,
> faccessat, funlinkat, fchmodat, utimensat, linkat, and some other
> FreeBSD-specific syscalls.

For most of these, you can use AT_EMPTY_PATH (often with an O_PATH file
descriptor) to get the same result. This matches the more general trend
of *at(2) system calls being able to operate on file descriptors
directly and removing the need to add extra flags to every syscall.

For syscalls without AT_EMPTY_PATH, you can use /proc/self/fd/$n.

> In some ways the FreeBSD approach is better: it doesn't require a new
> syscall (it merely extends old ones), it improves on syscalls other than
> just openat, and it would have been a bit easier to modify GNU tar to use
> the FreeBSD approach than to use openat2. However, the FreeBSD approach does
> have some limitations, in that some syscalls (e.g., mkdirat, mknodat) that
> lack a flags argument don't have a AT_RESOLVE_BENEATH option. In the end, I
> modified GNU Tar, via Gnulib, to use FreeBSD's O_RESOLVE_BENEATH when
> available, to use openat2's RESOLVE_BENEATH when available, and to emulate
> openat2's RESOLVE_BENEATH on other platforms, and to use these new options
> only to open the parent directories of the files that I actually wanted to
> access (so that I didn't need to worry about fstatat etc. messing up).
> 
> One possibility to simplify portability for other apps would be to add
> O_RESOLVE_BENEATH support to glibc, for compatibility with FreeBSD. This
> should be easy to do by using openat2 internally. Supporting
> AT_RESOLVE_BENEATH would be harder, as it would require help from the Linux
> kernel; and anyway as mentioned above AT_RESOLVE_BENEATH doesn't solve the
> whole problem.
> 
> Even if glibc doesn't add O_RESOLVE_BENEATH, the glibc manual should mention
> the connection between openat2's RESOLVE_BENEATH and FreeBSD
> O_RESOLVE_BENEATH, in a portability section under openat and/or openat2. The
> Linux man page for openat2 does this, so you can look to it for wording
> ideas.
> 
> I don't know why the FreeBSD approach was rejected by the Linux kernel
> developers.

1. We couldn't add new O_* flags without risking that some old broken
   program set those bits (because openat does not error out if you pass
   garbage bits).

2. We planned to add several RESOLVE_* flags which would just take up
   more bits in the (32-bit) O_* flag space. There are some new
   RESOLVE_* flags I want to add soon but haven't had the time to work
   on.

3. Linus didn't want it in openat() -- his original preference was for a
   resolveat() which would only do openat(O_PATH) IIRC.

4. I had some plans around restricting re-opens which would require even
   more bits, hence open_how being extensible. Sadly the first few
   prototypes of this were NACKed or had serious issues, I am working on
   a new one but have been a bit busy this year...

5. Recently I've run into a use-case where you would need to specify a
   root file descriptor as well as a cwd file descriptor for the same
   lookup -- this would not be possible to add to existing syscalls but
   because openat2 is extensible we can easily add support for this.

-- 
Aleksa Sarai
Senior Software Engineer (Containers)
SUSE Linux GmbH
https://www.cyphar.com/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 265 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20251119/95c9486a/attachment.sig>


More information about the Libc-alpha mailing list