Skip to content

GHSA-m9vj-637x-v6pq

CVE Information

do_rename_at()/do_link_at() confine a relative destination beneath the tree, but bailed to the unconfined path-based do_rename()/do_link() whenever EITHER path was absolute. An absolute source -- an absolute --temp-dir temp file, or an absolute --link-dest basis -- therefore disabled confinement of the relative destination, so finish_transfer()'s tmp->final rename (and a hard-link create) followed a destination parent component an attacker flipped from a directory to a symlink mid-transfer, landing the transferred file OUTSIDE the destination tree (arbitrary write / local privilege escalation).

The same wrappers had a second confinement gap of the same shape: a path with no slash (a bare top-level name) made the WHOLE rename/link fall through to the unconfined do_rename()/do_link(), so a mixed-parent operation (one slashed path, one bare path) escaped confinement on the slashed side too. Both gaps are closed by the same fix and are covered by this item.

Fix: confine each side independently -- an absolute path uses AT_FDCWD with the full path, a relative/slashed path resolves through secure_relative_open(), and a bare top-level name is confined against AT_FDCWD rather than bailing to the path-based call; a genuinely absolute destination remains an operator-named, trusted case.

Tests: rename-fullpath-symlink-race, rename-mixed-parent-symlink-race, rename-mixed-parent-transfer, rename-mixed-parent-escape-poc.

Additional variant (same class -- a peer-influenced basis path escapes receiver confinement to write outside the destination): a malicious pull server selects an in-place partial-dir basis (FNAMECMP_PARTIAL_DIR) whose relative path carries peer-derived components. Treating that whole path as operator-trusted let a server-planted, receiver-owned symlink redirect the partial-basis open -- and the in-place update's output -- to a client-local file outside the destination tree (overwrite). Fix: resolve a relative peer-selected partial basis through secure_relative_open(); a daemon rejects an unconfined partial basis while a pull client falls back to a safe no-basis update, and in-place output is gated on a validated basis fd (inplace_partial and fd1 != -1). Tests: malicious-server-partial-basis-symlink-overwrite, operator-path-partial-dir-daemon. Credit (variant): Filipe Casal of Trail of Bits in collaboration with OpenAI.

Credit: Omar Elsayed (seks99x); Filipe Casal of Trail of Bits in collaboration with OpenAI -- partial-dir basis variant.

Note: same do_rename_at()/do_link_at() wrappers added for the 3.4.3 symlink-race fix (CVE-2026-43619), but a distinct trigger (absolute --temp-dir / --link-dest, and the mixed/bare-path case) the 3.4.3 fix did not confine -- assigned a separate ID.

Affected: rsync 3.4.3 and earlier; fixed in 3.5.0. (Precise introduced-in ranges being finalised.)