]> git.ipfire.org Git - thirdparty/rsync.git/commit
syscall: use openat2(RESOLVE_BENEATH) on Linux for secure_relative_open
authorAndrew Tridgell <andrew@tridgell.net>
Wed, 29 Apr 2026 22:39:22 +0000 (08:39 +1000)
committerAndrew Tridgell <andrew@tridgell.net>
Wed, 29 Apr 2026 23:30:31 +0000 (09:30 +1000)
commit4fa7156ccdb2ad34b034d18fe2fd6cd79adef8a1
treefc0103714d71f0da2d5a133c9fc1a8a5b3ae03be
parentdcf364dac5970ffdd5279387c8a3fbe8d6cb8071
syscall: use openat2(RESOLVE_BENEATH) on Linux for secure_relative_open

The CVE fix in commit c35e283 made secure_relative_open() walk every
component of relpath with O_NOFOLLOW. That blocks every symlink in the
path, which is stricter than the threat model required: legitimate
directory symlinks within the destination tree (e.g. when using -K /
--copy-dirlinks) are also rejected, breaking delta transfers with
"failed verification -- update discarded".  See issue #715.

On Linux 5.6+, openat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS) gives
us exactly what we want: the kernel rejects any resolution that would
escape the starting directory (via "..", absolute paths, or symlinks
pointing outside dirfd) while still following symlinks that resolve
within it. /proc magic-links are blocked too.

Use openat2 first; fall back to the existing per-component O_NOFOLLOW
walk on ENOSYS (kernel < 5.6). The lexical "../" checks at the head
of the function are kept as defense in depth. The Linux gate is
plain #ifdef __linux__: the runtime ENOSYS fallback covers the only
case that actually matters (header present + old kernel), and any
Linux build environment without linux/openat2.h will fail with a
clear "no such file" error rather than silently disabling the
protection.

Verified manually that openat2(RESOLVE_BENEATH) blocks all four
escape patterns (absolute symlink, ../ symlink, lexical .., absolute
path) while allowing direct and within-tree symlinks. The new
testsuite/symlink-dirlink-basis.test (taken from PR #864 by Samuel
Henrique) exercises the issue #715 regression and passes; full
make check passes 47/47.

Test: testsuite/symlink-dirlink-basis.test (8 scenarios)
Fixes: https://github.com/RsyncProject/rsync/issues/715
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
syscall.c
testsuite/symlink-dirlink-basis.test [new file with mode: 0755]