Skip to content

Commit 0525042

Browse files
amwolffneild
authored andcommitted
net/http: treat MaxBytesReader's negative limits as equivalent to zero limit
Current MaxBytesReader behaviour differs from its documentation. It's not similar enough to io.LimitReader. It panics when limit (n) < -1 and returns [-1, <nil>] when limit (n) = -1. To fix that, we treat all negative limits as equivalent to 0. It would be possible to make MaxBytesReader analogically identical in behaviour to io.LimitReader, but that would require to stop maxBytesReader's Read from reading past the limit. Read always reads one more byte (if possible) for non-negative limits and returns a non-EOF error. This behaviour will now apply to all limits. Fixes #45101 Change-Id: I25d1877dbff1eb4b195c8741fe5e4a025d01ebc0 Reviewed-on: https://go-review.googlesource.com/c/go/+/303171 Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Trust: Damien Neil <[email protected]> Trust: Dmitri Shuralyov <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Go Bot <[email protected]>
1 parent 9b78c68 commit 0525042

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/net/http/request.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,9 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro
11241124
// MaxBytesReader prevents clients from accidentally or maliciously
11251125
// sending a large request and wasting server resources.
11261126
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
1127+
if n < 0 { // Treat negative limits as equivalent to 0.
1128+
n = 0
1129+
}
11271130
return &maxBytesReader{w: w, r: r, n: n}
11281131
}
11291132

src/net/http/request_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,92 @@ func TestMaxBytesReaderStickyError(t *testing.T) {
850850
}
851851
}
852852

853+
// Issue 45101: maxBytesReader's Read panicked when n < -1. This test
854+
// also ensures that Read treats negative limits as equivalent to 0.
855+
func TestMaxBytesReaderDifferentLimits(t *testing.T) {
856+
const testStr = "1234"
857+
tests := [...]struct {
858+
limit int64
859+
lenP int
860+
wantN int
861+
wantErr bool
862+
}{
863+
0: {
864+
limit: -123,
865+
lenP: 0,
866+
wantN: 0,
867+
wantErr: false, // Ensure we won't return an error when the limit is negative, but we don't need to read.
868+
},
869+
1: {
870+
limit: -100,
871+
lenP: 32 * 1024,
872+
wantN: 0,
873+
wantErr: true,
874+
},
875+
2: {
876+
limit: -2,
877+
lenP: 1,
878+
wantN: 0,
879+
wantErr: true,
880+
},
881+
3: {
882+
limit: -1,
883+
lenP: 2,
884+
wantN: 0,
885+
wantErr: true,
886+
},
887+
4: {
888+
limit: 0,
889+
lenP: 3,
890+
wantN: 0,
891+
wantErr: true,
892+
},
893+
5: {
894+
limit: 1,
895+
lenP: 4,
896+
wantN: 1,
897+
wantErr: true,
898+
},
899+
6: {
900+
limit: 2,
901+
lenP: 5,
902+
wantN: 2,
903+
wantErr: true,
904+
},
905+
7: {
906+
limit: 3,
907+
lenP: 2,
908+
wantN: 2,
909+
wantErr: false,
910+
},
911+
8: {
912+
limit: int64(len(testStr)),
913+
lenP: len(testStr),
914+
wantN: len(testStr),
915+
wantErr: false,
916+
},
917+
9: {
918+
limit: 100,
919+
lenP: 6,
920+
wantN: len(testStr),
921+
wantErr: false,
922+
},
923+
}
924+
for i, tt := range tests {
925+
rc := MaxBytesReader(nil, io.NopCloser(strings.NewReader(testStr)), tt.limit)
926+
927+
n, err := rc.Read(make([]byte, tt.lenP))
928+
929+
if n != tt.wantN {
930+
t.Errorf("%d. n: %d, want n: %d", i, n, tt.wantN)
931+
}
932+
933+
if (err != nil) != tt.wantErr {
934+
t.Errorf("%d. error: %v", i, err)
935+
}
936+
}
937+
}
938+
853939
func TestWithContextDeepCopiesURL(t *testing.T) {
854940
req, err := NewRequest("POST", "https://golang.org/", nil)
855941
if err != nil {

0 commit comments

Comments
 (0)