Security: Cap per-connection inbound buffer to prevent memory DoS

The protocol length field is validated to 50 MiB, but a peer that sends
bytes which never assemble into a complete packet — for example an HTTP-mask
envelope without its terminating CRLF CRLF, which Parse() waits on
indefinitely — could grow the per-connection input buffer without bound.
handleConnection now closes the connection once unparsed buffered bytes
exceed MaxBufferedBytes (default 50 MiB + 64 KiB slack, covering the largest
legitimate packet plus its mask envelope and a read chunk).

Co-Authored-By: deepseek-v4-pro
This commit is contained in:
yuanyuanxianganddeepseek-v4-pro committed 2026-10-13 16:27:20 +02:00
1 parent 8e0f1283c6
commit 870a5832ea
2 files changed
+58

No files matched your search

+39
View File
@@ -1,6 +1,7 @@
package server
import (
"bytes"
"net"
"testing"
"time"
@@ -91,3 +92,41 @@ func TestPeerIP(t *testing.T) {
}
}
}
// A peer that sends bytes which never assemble into a complete packet (here an
// HTTP-mask envelope with no terminating CRLF CRLF) must be disconnected once
// the per-connection buffer cap is exceeded, rather than allowed to grow it
// without bound.
func TestHandleConnectionBufferCap(t *testing.T) {
cfg := DefaultConfig()
cfg.MaxBufferedBytes = 1024 // small cap for a fast, in-memory test
cfg.ReadTimeout = time.Second
srv := New(cfg)
srv.running.Store(true) // bypass acceptLoop; exercise handleConnection directly
client, serverConn := net.Pipe()
defer client.Close()
done := make(chan struct{})
go func() {
srv.handleConnection(serverConn)
close(done)
}()
// "POST " prefix with no terminator, plus filler to exceed the cap.
payload := append([]byte("POST /x HTTP/1.1\r\n"), bytes.Repeat([]byte{'A'}, 4096)...)
writeErr := make(chan error, 1)
go func() {
_, err := client.Write(payload)
writeErr <- err
}()
select {
case <-done:
// handleConnection returned because the cap was exceeded and it closed
// the connection.
case <-time.After(2 * time.Second):
t.Fatal("connection was not closed after exceeding the buffer cap")
}
}