GHSA-phxh-hjqv-39c9
CVE Information
When preserving metadata (
-A/--acls,-X/--xattrs, or fake-super ACL-as-xattr), the receiver applied the received ACL/xattrs to each entry by path, viaacl_set_file()/setxattr()(and the libacl walk re-resolves every component). A local user who controls a path component races the entry an unprivileged user just received -- a regular file, dir or FIFO -- into a symlink->outside AFTER the receiver created it but BEFORE the metadata apply, so the ACL set follows the flipped leaf (or a flipped parent) and writes an attacker-chosen ACL onto a victim inode OUTSIDE the destination tree. The attacker controls the ACL bytes (they are carried in the source entry), so this grants the attacker rwx on a root-owned file -- a local privilege escalation, not just a write. A raced FIFO could also block the apply (DoS).Fix: hold an
O_RDONLY|O_NOFOLLOW(O_NONBLOCK) fd pinning each entry's inode and apply all metadata to the pinned inode rather than re-resolving the path: xattrs via the f-variant calls on the held fd; ACLs vialib/acl.c's fd path (xacl_*_fd) -- orsetxattrat(AT_SYMLINK_NOFOLLOW)on the dirfd+leaf for a socket/device -- covering both the access and default ACL; fake-super ACL-as-xattr writes/deletes likewise go through the held fd. On Linux 6.13+ this uses the*xattratsyscalls (preferring a patched libacl's*_atbindings when present); on older Linux the/proc/self/fdcompat path pins the same inode. The packed ACL bytes are unchanged, so the result matches the oldacl_set_file()exactly.Tests: acl-symlink-race (RED on the unfixed path-based apply, GREEN pinned), acls-unpinnable (ACL still applies correctly on the path-based fallback).
Additional surfaces folded in (same class -- a confined receiver's xattr/ACL/metadata write resolved by pathname): the receiver's own xattr/ACL copy-out (the copy-xattrs case), and cross-tree operator-path metadata writes on
--backup-dir/ alt-dest / fake-super targets, likewise resolved by path, so a raced parent-component symlink could redirect the write outside the confined tree (a fake-super residual of this same issue). Fix: never path-resolve a confined receiver's xattr/ACL/metadata write -- route it through a held/pinnedO_NOFOLLOWfd like the metadata apply above. Added test: copy-xattrs-symlink-race.Note: the path-based fallback (and thus the unavoidable parent-symlink race) is reached only where neither primitive is available -- the BSDs, Solaris and macOS (no
*xattratsyscalls and no/proc/self/fdmagic symlinks), plus a Linux instance with no usable/proc. There the apply keeps the long-standing path-based calls to keep--aclsfunctional rather than silently skipping it -- an accepted, documented residual (SECURITY.md), refusable viarefuse options = acls.Affected: rsync 3.4.3 and earlier; fixed in 3.5.0. (Precise introduced-in ranges being finalised.)