Skip to content

Security: oferchen/rsync

SECURITY.md

Security Policy

Supported Versions

Version Supported
0.6.x ✅ (current: 0.6.2)
0.5.x ⚠️ critical fixes only
< 0.5

Reporting a Vulnerability

If you discover a security vulnerability in oc-rsync, please report it responsibly:

  1. Do not open a public GitHub issue for security vulnerabilities
  2. Email the maintainer directly at: skewers.irises.3b@icloud.com
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact assessment
    • Any suggested fixes (optional)

You can expect:

  • Initial acknowledgment within 48 hours
  • Regular updates on the fix progress
  • Credit in the security advisory (unless you prefer anonymity)

Security Design

oc-rsync leverages Rust's memory safety to eliminate entire vulnerability classes:

Memory Safety Guarantees

  • No buffer overflows: Rust's bounds checking prevents out-of-bounds memory access
  • No use-after-free: Rust's ownership system prevents dangling pointer access
  • No uninitialized memory: All memory must be initialized before use
  • No data races: Rust's type system prevents concurrent memory access bugs

Unsafe Code Policy

Crates that enforce #![deny(unsafe_code)] with no allow-listed exceptions:

  • daemon, cli, core, transfer, batch, filters, signature, matching, bandwidth, logging, logging-sink, branding, rsync_io, compress, apple-fs, flist, embedding, test-support - business logic, parsers, orchestration, and high-level I/O wrappers

Crates with #![deny(unsafe_code)] and targeted #[allow(unsafe_code)] for documented FFI/SIMD boundaries:

  • metadata - Ownership and privilege FFI (UID/GID lookup via getpwuid_r/getgrnam_r, setuid/setgid, setattrlist)
  • protocol - One isolated #[allow] in multiplex::helpers for performance-critical frame parsing
  • engine - Denies unsafe outside tests (#![cfg_attr(not(test), deny(unsafe_code))]) with targeted #[allow(unsafe_code)] on platform FFI (prefetch, buffer pool, CopyFileExW)
  • platform - Daemonization, signal handlers, environment isolation, and chroot syscalls
  • checksums - SIMD intrinsics for MD4/MD5 and rolling checksums (AVX2, AVX-512, SSE2, SSSE3, SSE4.1, NEON, WASM), with scalar fallbacks and parity tests
  • fast_io - Platform I/O syscalls (sendfile, io_uring, mmap, copy_file_range, IOCP, WSARecv/WSASend, setsockopt), with standard I/O fallbacks
  • windows-gnu-eh - Windows GNU exception handling shims (properly documented)

Long-term direction. Unsafe code is being consolidated into fast_io as the single crate permitted to wrap platform FFI directly; new unsafe code goes there and is exposed via safe public APIs. New #[allow(unsafe_code)] annotations in any other crate require explicit review.

Note: OS-level race conditions (TOCTOU) remain possible at filesystem boundaries; Rust's memory safety does not prevent them.

CVE Monitoring

Upstream rsync CVEs

oc-rsync monitors upstream rsync CVEs to verify continued non-applicability. Recent CVEs and our status:

CVE Upstream Issue oc-rsync Status Reason
CVE-2024-12084 Heap overflow in checksum parsing Not vulnerable Rust Vec handles dynamic sizing
CVE-2024-12085 Uninitialized stack buffer leak Not vulnerable Rust requires initialization
CVE-2024-12086 Server leaks client files Not vulnerable Strict path validation
CVE-2024-12087 Path traversal via --inc-recursive Not vulnerable Path sanitization
CVE-2024-12088 --safe-links bypass Mitigated Rust path handling
CVE-2024-12747 Symlink race condition Mitigated TOCTOU is OS-level

Upstream rsync 3.4.2 audits

In v0.6.2 the codebase was audited against every fix that landed in upstream rsync 3.4.2 (released 2026). The equivalent code paths were verified safe in oc-rsync:

  • Compressed-stream negative-token decoder bounds (#2225)
  • Xattr qsort element-count parity (#2226)
  • clean_fname() buffer-underflow parity (#2227)
  • Allocator zeroing pattern (calloc + realloc-expand) (#2228)
  • Y2038 safety in syscall paths (Int32x32To64 equivalent) (#2229)
  • ACL ID mapping for non-root users (#2230, closes #618)
  • FreeBSD many-xattrs handling parity (#2231)
  • "Directory has vanished" error path (#2232)
  • Removal of multiple leading slashes (#2233)
  • Daemon chrono::Local pre-init before chroot (#2234)
  • --open-noatime propagation through sender source-file opens (#2236)
  • AVX2 get_checksum1 mul_one uninitialised-regression audit (#2222)
  • MD4 get_checksum2 buf1 uninitialised-regression audit (#2223)
  • SIMD vs scalar self-test that cross-validates AVX2/SSE2/NEON paths at startup (#2224)

Monitoring Process

  1. Subscribe to rsync-announce: https://lists.samba.org/mailman/listinfo/rsync-announce
  2. Monitor NVD: https://nvd.nist.gov/vuln/search?query=rsync
  3. GitHub Security Advisories: Watch this repository for security advisories
  4. Scheduled CI watcher: tools/ci/check_upstream_release.sh runs weekly via GitHub Actions and opens a tracking issue when a new upstream rsync release ships, so new CVEs are surfaced automatically

When New CVEs Are Published

For each new upstream rsync CVE:

  1. Analyze the root cause (memory corruption, logic error, etc.)
  2. Check if oc-rsync has equivalent code paths
  3. Verify Rust's safety guarantees apply
  4. Document the analysis in this file
  5. If vulnerable, issue a security advisory and patch

Fuzzing

The repo ships cargo-fuzz targets for security-critical parsing and SIMD parity:

cd fuzz
cargo +nightly fuzz run fuzz_varint
cargo +nightly fuzz run fuzz_delta
cargo +nightly fuzz run fuzz_multiplex_frame
cargo +nightly fuzz run fuzz_legacy_greeting
cargo +nightly fuzz run simd_checksum_parity

simd_checksum_parity cross-validates the AVX2, SSE2, NEON, and scalar rolling/strong checksum paths against random inputs (see #2103). See fuzz/README.md for detailed fuzzing instructions.

Hardening Notes

These cover operationally relevant trade-offs in the current code base and how to mitigate them.

Buffer pool bounds checks

recycle_buffer(buf_id) in the io_uring path validates that buf_id falls within the registered buffer pool using debug_assert!. In release builds the assertion compiles out, so a corrupted or attacker-influenced buf_id reaching this code path would index out of range and either produce undefined behaviour inside the io_uring crate or — more likely — be caught by the kernel as an invalid SQE. A defense-in-depth fix to upgrade the check to a release-mode bound is tracked; until it lands, do not expose the io_uring path to untrusted protocol input.

io_uring buffer-group ID namespace

io_uring buffer-group IDs (bgid) live in a 16-bit namespace. The provided-buffer ring helpers in fast_io cap allocation at this bound, and exhaustion returns an error rather than wrapping. Long-running daemons that churn ring groups should monitor for the bounded error and recycle.

SSH double compression

If the SSH transport itself compresses the stream (Compression yes in ssh_config or a cipher with built-in compression), running oc-rsync -z will compress payloads twice. The amplification surface is small in practice but adds CPU and can mask compressor-specific bugs. Disable one layer; the canonical choice is to leave compression to rsync (-z / --compress) and disable it in SSH.

Daemon TLS

oc-rsync --daemon does not terminate TLS natively. To expose the daemon over an untrusted network, deploy it behind one of:

  • stunnel in front of rsync://-style daemon traffic
  • SSH tunnel (ssh -L to a localhost-bound daemon)
  • A reverse proxy that performs TLS termination (e.g., HAProxy in TCP mode)

Bind the daemon itself to 127.0.0.1 (or a private VPC interface) and route external clients exclusively through the TLS terminator.

See docs/deployment/daemon-tls.md for runnable recipes covering stunnel, ssh -L, and HAProxy TCP-mode configurations, hardened systemd unit excerpts, and nftables/iptables rules that deny external access to the daemon's loopback port.

Daemon module hardening

In addition to use chroot = yes, prefer:

  • numeric ids = yes so uid/gid mapping does not depend on the daemon's passwd/group
  • refuse options = delete * for read-only mirrors
  • hosts allow / hosts deny ACLs at the daemon layer (these run before authentication)
  • secrets file permissions of 0600, owned by the daemon user only

Security Best Practices for Users

Daemon Mode

When running oc-rsync --daemon:

  1. Use chroot: Configure use chroot = yes in rsyncd.conf
  2. Restrict modules: Only expose necessary paths
  3. Authentication: Use auth users and secrets file for access control
  4. Network security: Run behind a firewall, use SSH tunneling for remote access
  5. Read-only modules: Use read only = yes where possible

Client Mode

  1. Verify server identity: Use SSH for transport when possible
  2. Careful with --delete: Ensure you're syncing to the intended destination
  3. Review exclude patterns: Avoid accidentally transferring sensitive files

Acknowledgments

Security researchers who have contributed to oc-rsync's security:

  • (Your name could be here - report responsibly!)

There aren't any published security advisories