From 178216f10f2e05fd74bf865f2d0725fce4f907cd Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 24 Jan 2025 08:54:15 +0100 Subject: [PATCH] Fix: check sum header fields for validity The rsync vulnerability CVE-2024-12084 also affected gokrazy/rsync (before this commit): An attacker could send an invalid sum header and thereby trigger a panic (but no heap buffer overflow!) like: panic: runtime error: slice bounds out of range [:512] with length 16 goroutine 277 [running]: github.com/gokrazy/rsync/rsyncd.(*sendTransfer).receiveSums(0xc0000d7b68) /home/michael/go/src/github.com/gokrazy/rsync/rsyncd/sender.go:136 +0x339 After this commit, the code no longer panics but sends an error: gokr-rsync [sender]: invalid checksum length 512 --- types.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/types.go b/types.go index 5601697..f89c4d7 100644 --- a/types.go +++ b/types.go @@ -1,6 +1,10 @@ package rsync -import "github.com/gokrazy/rsync/internal/rsyncwire" +import ( + "fmt" + + "github.com/gokrazy/rsync/internal/rsyncwire" +) // rsync/rsync.h:struct sum_buf type SumBuf struct { @@ -32,26 +36,43 @@ type SumHead struct { } func (sh *SumHead) ReadFrom(c *rsyncwire.Conn) error { + // TODO(protocol>=30): update maxBlockLen + const maxBlockLen = 1 << 29 // see rsync.h:OLD_MAX_BLOCK_SIZE + var err error sh.ChecksumCount, err = c.ReadInt32() if err != nil { return err } + if sh.ChecksumCount < 0 { + return fmt.Errorf("invalid checksum count %d", sh.ChecksumCount) + } sh.BlockLength, err = c.ReadInt32() if err != nil { return err } + if sh.BlockLength < 0 || sh.BlockLength > maxBlockLen { + return fmt.Errorf("invalid block length %d", sh.BlockLength) + } sh.ChecksumLength, err = c.ReadInt32() if err != nil { return err } + // TODO(protocol>=27): update max sh.ChecksumLength check + if sh.ChecksumLength < 0 || sh.ChecksumLength > 16 { + return fmt.Errorf("invalid checksum length %d", sh.ChecksumLength) + } sh.RemainderLength, err = c.ReadInt32() if err != nil { return err } + if sh.RemainderLength < 0 || sh.RemainderLength > sh.BlockLength { + return fmt.Errorf("invalid remainder length %d", sh.RemainderLength) + } + return nil }