match: re-check .len == l before substituting want_i in hash_search

The check_want_i adjacent-match optimisation substitutes i = want_i after
verifying sum1 and sum2, but -- unlike the main chain loop and the aligned_i
path -- did not re-check s->sums[want_i].len == l. A hostile peer acting as the
generator against a daemon-as-sender can set s2length=0 (read_sum_head only
enforces >= 0, so the sum2 memcmp is a no-op) and craft count=2, blength=len+1,
remainder=len with both sum1 set to the file's rolling checksum. The chain head
(.len=remainder=len) passes the l==.len gate; want_i=0 (.len=blength=len+1) is
then substituted unchecked. After matched(), offset advances to len, k =
MIN(blength, len-offset) = 0, map_ptr(...,0) returns NULL, and the rolling-
checksum trim dereferences map[0] -- a remote unauthenticated SEGV of the
per-connection daemon child against any module serving a non-empty regular file.

Add the missing length re-check, matching the chain loop and aligned_i path.

(cherry picked from commit aad82d77aae098c8d8529f69614a0ffcbfbbb5d3)
(cherry picked from commit 8c1d0d3820)
This commit is contained in:
Andrew Tridgell committed 2026-06-15 17:16:06 +10:00
1 parent eeef7529c0
commit 032eed6181
1 file changed
+1
+1
View File
@@ -293,6 +293,7 @@ static void hash_search(int f,struct sum_struct *s,
&& (!updating_basis_file || s->sums[want_i].offset >= offset
|| s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
&& sum == s->sums[want_i].sum1
&& l == s->sums[want_i].len
&& memcmp(sum2, sum2_at(s, want_i), s->s2length) == 0) {
/* we've found an adjacent match - the RLL coder
* will be happy */