Case study · rsync

99 findings filed against rsync. Ten had been there since 1996.

Proof reduces the work between a software change and the evidence required to trust it. rsync's own requirements became checks in the 3.5.0 release, and the public board includes 28 findings later withdrawn.

01 · The problem

A flood of reports, many of them AI-generated.

While this audit ran, rsync’s maintainer described the load in public: Many of those reports are AI generated (not all though, there are some notable ones with very careful and high quality manual analysis). Checking one costs time either way.

Andrew Tridgell, rsync and outrage, 3 June 2026, as reported by LWN.

Nothing this audit filed had to be taken on trust.

Every requirement was written down, and every finding became a check that runs. Here is what the checks found:

A hosts-deny rule that failed open

An unresolvable hostname admitted the host it was meant to block. Now fails closed. HIGH · CVE-2026-70452 · fixed in 3.5.0

Control characters, injected into an administrator's terminal

Transferred filenames carried raw control bytes into the log file, ready to fire escape sequences when the log was read. Now escaped. CWE-117 log injection · fixed in 3.5.0

An uninitialized heap byte, handed to the remote shell

A counter and a writer disagreed on a quoted filename; the byte went out with --protect-args off. The counter now mirrors the writer. fixed in 3.5.0

Every item above carries its own entry in the release notes.

02 · The subject

A defect here is infrastructure.

rsync moves files inside most backup pipelines, cloud images and deployment scripts. Its core paths have been touched continuously since Andrew Tridgell's first release in 1996. It is widely shipped across Linux and Unix systems.

FIRST RELEASE 1996 · AUTHOR ANDREW TRIDGELL · WIDELY SHIPPED ON LINUX AND UNIX · C CODEBASE

03 · The findings

What the audit found.

The audit filed 99 findings. The 3.5.0 release shipped 33 security fixes from the combined focused audit, daemon-protocol fuzzing, Trail of Bits work, and other external research.

A sample · 6 of 99 findings

CVE-2026-70452

hosts deny failed open: an unresolvable hostname admitted the host it was meant to block

highfixed

CVE-2026-53799

Receiver ACL/xattr writes followed a symlink race, setting attacker-chosen ACLs on files outside the destination tree

mediumfixed

CWE-117

Log injection: raw control characters from transferred filenames reached the administrator's terminal

fixed

safe_arg()

Uninitialized heap byte leaked in a quoted filename, handed to the remote shell

fixed

hardening

--safe-links bypass through --backup hardlinks · cross-device confinement in the rename path · MSG_IO_ERROR exit-code masking

fixed

bugs

clean_fname() dot-dot normalization · .cvsignore clear-list abort · --chmod=a+s setgid bit · wildcard bracket case-folding

fixed

This is a sample of the 99. Each of the 33 security fixes in 3.5.0 has its own CVE advisory in the release notes.

04 · The method

Every requirement became an executable tripwire.

Proof formalized rsync's path-handling and daemon-protocol requirements, then turned each one into an executable check against the real code.

The tripwire contract · a temporary state

While the defect is live, the tripwire pins the broken behaviour and exits 0. When the fix lands, the tripwire exits 1, and it is rewritten as a regression test for the fixed behaviour.

While the bug is live the test asserts the broken output. Exit 0 means the defect still reproduces.

At the fix the assertion fails. Exit 1 is the signal that the defect is gone.

After the fix the same file is flipped to assert the correct output and stays in the suite as a regression test.

So no test in this suite claims a bug is correct. A pinned assertion is a detector with an expiry date, and the flip is recorded on the finding.

ASAN HARNESSES · MC/DC HARNESSES · PROPERTY TESTS · MANUAL REVIEW · MERGE REVIEW

MC/DC - modified condition/decision coverage: the strongest structural coverage measure short of formal proof.

05 · Continuous

The audit moved with the release.

rsync 3.5.0 took months to develop, and the audit ran through all of it. When the release shipped, the record was current.

REQUIREMENTS TRIPWIRES RUN UPSTREAM MOVES RECORD CORRECTED STILL TRUE? FIXES RETIRE
  1. Requirements
  2. Tripwires run
  3. Upstream moves
  4. Record corrected
  5. Still true?
  6. Fixes retire
Fig. 01 · rsync's own requirements became checks in its release process. The 3.5.0 release notes say every fix ships with a regression test that fails on the unfixed tree.

06 · The numbers

The board shrank when the evidence changed.

The audit disproved 28 of its own findings during the release cycle. Each one stays on the board with the evidence that overturned it, because a register that only ever grows has been edited.

99

findings filed by this audit

Every one carried a reproducer before it was filed.

28

findings the audit later withdrew

Taken back by the audit itself, and still published.

10

findings present since rsync's first release in 1996

Ten of the 99 sit in code paths that have been there since the first release.

RELEASE

33 security issues fixed · 39 CVE IDs across the cycle · shipped 13 Aug 2026

rsync 3.5.0 notes: a focused audit, a daemon-protocol fuzzing pass, and external researchers · each of the 33 fixes carries one CVE ID; the other six of the 39 were fixed mid-development, in the interim 3.4.3 security release

AUDIT

99 findings filed · 45 open · 21 fixed · 28 withdrawn · 5 reviewed

Board read 30 August 2026, and the audit is still running, so open and fixed still move. Reviewed means a person re-examined the finding and let it stand, and the release's 33 fixes also include fuzzing and other researchers.

SEVERITY

13 high · 41 medium · 43 low · 2 low-medium

our assessment, weighted by reachability and impact; a starting point for the maintainers' triage, and no CVSS determination

SUBMITTED

5 fixes authored by Proof · propagated across five maintenance branches

07 · Where the findings came from

Different methods found different things.

Four efforts hardened rsync 3.5.0, and the release notes credit all of them.

audit

This audit formalized rsync's path-handling and daemon-protocol requirements, then ran each one as a tripwire against every upstream change. It filed 99 findings.

fuzzing

A daemon-protocol fuzzing pass reached protocol states no written requirement had named. Some of the release's fixes came from there, and none of them are counted as ours.

partner

Trail of Bits worked this release alongside us through its “Patch the Planet” program. We found issues they did not. They found issues we did not.

researchers

Independent researchers reported through the upstream process on their own timetable, and their findings sit in the same release notes.

Each method has a blind spot the others cover. That is what a mature security practice looks like: several efforts, checking each other. The collaboration was good, and rsync is better off for it.

The release notes name the reporter behind each fix. The entries for this audit's findings name Leonid Bugaev, who validated every one of them before it was filed upstream.

08 · What's next

The audit is still running.

The work did not end with the release. We are building the standing audit for rsync: code review, triage, and a full re-run on every release candidate, as the jsonparser gate runs in CI.

Thirty years of code, kept honest on every change. Watch this page.

09 · The receipts

Open the receipts.

Do not take this page's word for any of it. The board, the withdrawals and the upstream fixes are all published where you can read them.

This is the Continuous Correctness Audit, run on your component: the same loop, the same receipts. Request demo →