Automated Threat Modeling

Rsync CVE-2026-29518: Overview of a TOCTOU Race Condition Enabling Symlink Based Arbitrary File Write

CVE-2026-29518 is a high-severity TOCTOU race condition in rsync's daemon mode that allows symlink-based arbitrary file writes and privilege escalation. This post covers the patch and how to mitigate the flaw.

CVE Analysis

8 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-20

Rsync CVE-2026-29518: Overview of a TOCTOU Race Condition Enabling Symlink Based Arbitrary File Write
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A classic race condition has been in rsync's daemon mode for years, and it becomes exploitable under one non-default configuration. CVE-2026-29518 is a time-of-check to time-of-use (TOCTOU) flaw in rsync versions before 3.4.3. It allows a local attacker with write access to a module directory to escape the intended file boundary through symlink substitution, and from there to write and read arbitrary files on the host system.

The mitigation function, secure_relative_open(), has been in the codebase since rsync 3.4.0, where it was added for a different vulnerability (CVE-2024-12086). It was never wired into the specific code path that handles daemon operation without chroot confinement. The fix, shipped in rsync 3.4.3 on May 20, 2026, turns the function on where it was needed all along.

Technical Information

Root Cause: The TOCTOU Window

The vulnerability is classified under CWE-367 (Time of check Time of use Race Condition). It affects rsync's daemon mode when the daemon is configured with use chroot = no. That is not the default setting, but it is common in environments where the daemon process lacks the privileges to call chroot() or where administrators need access to paths outside a single directory tree.

In the vulnerable code path, the receiver performs two operations in sequence:

  1. Check: The receiver validates the incoming file path, confirming it resolves to a location within the intended module directory.
  2. Use: The receiver calls open() on that validated path to perform the actual file operation.

Between the two operations is a window in which the filesystem state can change. A local attacker with write access to the module directory can use this window to replace a parent directory component with a symbolic link pointing outside the module boundary. When the open() call executes, it follows the newly planted symlink, and the file operation lands on an attacker-controlled location outside the intended directory.

Attack Flow

Exploitation runs in this order:

  1. The attacker opens an authenticated connection to the rsync daemon. The connection must have compression enabled, which is the default for protocol version 30 and above when both peers advertise compression support.
  2. The attacker identifies a module configured with use chroot = no where they have write access.
  3. The attacker starts a file transfer that makes the receiver validate and then open a file path containing multiple directory components.
  4. In the window between the receiver's path validation and its open() system call, the attacker replaces one of the parent directory components with a symlink pointing to a sensitive location (for example, /etc/ or another privileged directory).
  5. The open() call follows the symlink, and the file write or read happens outside the module boundary.

The race can be exploited in two directions:

  • Writes: The attacker can create or overwrite arbitrary files on the system. If the daemon runs as root, the attacker can escalate privileges directly by modifying sensitive system files such as /etc/passwd, /etc/shadow, or cron configurations.
  • Reads: By redirecting basis file reads, the attacker can disclose the contents of privileged files through the daemon's response data.

Why Chroot Matters

The default rsync daemon configuration uses use chroot = yes, which makes the daemon call chroot() and confines the process to the module directory at the kernel level. In this configuration, even if an attacker wins the race and plants a symlink, the kernel prevents any path resolution from escaping the chroot jail. The vulnerability therefore requires the non-default use chroot = no setting.

Patch Information

The fix shipped in rsync 3.4.3 on May 20, 2026, through PR #895 in the RsyncProject/rsync repository. It is a combined pull request from maintainer Andrew Tridgell (tridge) that bundles fixes for six CVEs in 21 commits, changing 40 files with 3,100 additions and 178 deletions. It was merged the same day it was opened.

Core Fix: Commit ed649cda (Receiver Side)

This commit is the primary fix. It turns on the secure_relative_open() function in syscall.c for the daemon no-chroot code path. The function had existed since rsync 3.4.0 (added for CVE-2024-12086) but was not used in the exact scenario this vulnerability exploits. The commit sets use_secure_symlinks in clientserver.c's rsync_module() function and routes the receiver's basis file open in receiver.c through secure_relative_open().

The function closes the race window by walking each parent path component from a trusted directory file descriptor (dirfd), with kernel-enforced restrictions on following symlinks at each step:

  • Linux 5.6+: Uses openat2() with the RESOLVE_BENEATH flag, which tells the kernel to reject any path component that would escape the starting directory through symlinks or .. traversal.
  • FreeBSD 13+ and macOS 15+: Uses O_RESOLVE_BENEATH, the BSD equivalent.
  • Other platforms: Falls back to a per-component walk using O_NOFOLLOW on each directory in the path, refusing to follow symlinks manually.

Because the kernel enforces confinement at every step, the race window closes entirely. Even if an attacker swaps a directory for a symlink between the check and the open, the kernel rejects the traversal.

Sender-Side Fix: Commit dbfeb532

This commit fixes the same TOCTOU race on the sender side. The sender's change_pathname() used chdir() to descend into subdirectories, and that chdir() followed symlinks freely. An attacker could swap a directory for a symlink between the chdir() and the following file open(), and read privileged files through the daemon. The fix rebuilds the full relative path (F_PATHNAME + fname) and opens the file with secure_relative_open() from the trusted module_dir root. Because this approach is CWD-independent, the chdir race is entirely neutralized.

Additional Hardening

Several follow-on commits in the same PR hardened adjacent code paths against the same race class, tracked under the related CVE-2026-43619:

  • 9401f882 secures change_dir() itself against chdir escape.
  • ef7b6205 and 65bd4ce9 add do_chmod_at() and a full set of do_*_at() wrappers for chmod, lchown, utimes, rename, unlink, mkdir, symlink, mknod, link, rmdir, and lstat.
  • 42ecda33 hardens the source and destination opens in copy_file().

Regression Tests

The release includes these regression tests: chmod-symlink-race.test, chdir-symlink-race.test, bare-do-open-symlink-race.test, alt-dest-symlink-race.test, copy-dest-source-symlink.test, and sender-flist-symlink-leak.test. The tests skip on platforms that lack RESOLVE_BENEATH equivalents (Cygwin, Solaris, OpenBSD, NetBSD).

Every version up to and including rsync 3.4.2 is affected. Users should upgrade to 3.4.3 from the tagged release on GitHub or the tarballs at rsync.samba.org/ftp/rsync/src/.

Affected Systems and Versions

  • Affected: All rsync versions before 3.4.3
  • Fixed in: rsync 3.4.3
  • Vulnerable configuration: The rsync daemon must be running with use chroot = no in rsyncd.conf. The default setting of use chroot = yes is not vulnerable.
  • Additional prerequisite: The attacker must have write access to a module path and an authenticated daemon connection with compression enabled (default for protocol 30+).

CVE-2026-29518 requires the non-default chroot configuration, but the 3.4.3 release also patches vulnerabilities reachable under standard operating conditions. The full set of CVEs fixed in this release:

CVE IdentifierCVSS ScoreDescriptionConfiguration Requirement
CVE-2026-295187.3 (High)TOCTOU symlink race, local privilege escalationRequires use chroot = no
CVE-2026-436196.3 (Medium)Symlink races on path-based system callsRequires use chroot = no
CVE-2026-43617Not ProvidedSecurity flaw requiring non-default daemon configRequires daemon chroot =...
CVE-2026-43618Not ProvidedSecurity flaw reachable from normal pullStandard configuration
CVE-2026-43620Not ProvidedSecurity flaw reachable from normal pullStandard configuration
CVE-2026-45232Not ProvidedOff-by-one out-of-bounds stack writeRequires RSYNC_PROXY

Organizations should deploy the update on every server, not only on servers with chroot disabled.

Vendor Security History

RsyncProject has a documented history of security problems in daemons running with chroot disabled. In April 2004, the rsync 2.6.1 release notes listed a security fix for a daemon problem when chroot is not enabled. The maintainers explicitly warned users running a non-read-only rsync daemon with chroot disabled to upgrade, especially if the user privileges were anything above nobody. More than two decades later, CVE-2026-29518 shows that the same configuration is still a source of security bugs.

secure_relative_open(), the core of the fix, was introduced in rsync 3.4.0 for CVE-2024-12086 but was not turned on in the daemon no-chroot code path. Large codebases often have this gap between having a security mechanism available and using it on every relevant code path, which is why security audits need to cover every configuration permutation.

The 3.4.3 release fixes six CVEs at once and is a significant hardening effort for the project. Much of the work goes after the whole class of symlink-based race conditions rather than fixing them one at a time.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization