mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-15 14:51:20 -04:00
match: bound the hash_search() chain walk (issue #217)
hash_search() walks the entire hash-table chain for the current rolling checksum at every byte offset of the source file. Disk and VM images contain large runs of identical blocks, so a single weak checksum (get_checksum1) can collide thousands of times and pile every one of those blocks onto one chain. When the sender then rolls across a region whose weak checksum keeps landing on that chain without ever producing a strong-checksum match, it re-walks the whole chain for every byte, giving O(file_size * chain_length) behaviour. The result is rsync sitting at 100% CPU for hours with no apparent progress -- the long-standing "rsync hangs on large files" reports. Cap the number of same-weak-checksum candidates examined per offset at MAX_CHAIN_LEN. Once the cap is hit we treat the offset as a non-match and roll forward a byte; any block skipped this way is simply sent as literal data, so the transferred result is always correct -- only the transfer size is marginally affected. This is purely a sender-side search limit: it changes no checksum, emitted byte, or protocol field, so a capped sender interoperates with an unmodified receiver and vice versa. On a synthetic 40000-block basis sharing one weak checksum, syncing a 60KB source dropped from ~18.4s to ~0.7s; the unbounded cost grows with the square of the file size. testsuite/hashsearch-chain_test.py reproduces the pathology with a tiny basis of weak-checksum-colliding decoy blocks and asserts, via the existing false_alarms counter (--debug=deltasum1), that the per-hash-hit chain walk stays bounded. The assertion is exact and machine-independent rather than timing-based.
This commit is contained in:
500 Internal Server Error
Gitea Version: 1.28.0+dev-477-g8b6ad49a5f