From b8969ed8dcb5cba0b037f3fe091a0933fc786a28 Mon Sep 17 00:00:00 2001 From: Erik Dubbelboer Date: Sat, 22 Feb 2025 16:33:57 +0900 Subject: [PATCH] Fix normalizeHeaderValue (#1963) The fuzzer found some cases where it would panic. The output of normalizeHeaderValue doesn't need to affect s.b and s.hLen because the length of the normalized header will never be bigger, so it can just be normalize in place without affecting the rest of the buffer. --- fuzz_test.go | 11 +++++++++++ header.go | 26 ++++---------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/fuzz_test.go b/fuzz_test.go index bf4aaaa8e8..ff4ce57bbf 100644 --- a/fuzz_test.go +++ b/fuzz_test.go @@ -3,6 +3,8 @@ package fasthttp import ( "bufio" "bytes" + "encoding/base64" + "encoding/binary" "net/url" "strings" "testing" @@ -43,6 +45,15 @@ func FuzzVisitHeaderParams(f *testing.F) { func FuzzResponseReadLimitBody(f *testing.F) { f.Add([]byte("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210"), 1024) + f.Add([]byte(" 0\nTrAnsfer-EnCoding:0\n\n0\r\n1:0\n 00\n 000\n\n"), 24922) + f.Add([]byte(" 0\n0:\n 0\n :\n"), 1048532) + + // Case found by OSS-Fuzz. + b, err := base64.StdEncoding.DecodeString("oeYAdyAyClRyYW5zZmVyLUVuY29kaW5nOmlka7AKCjANCiA6MAogOgogOgogPgAAAAAAAAAgICAhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiA6CiA6CiAgOgogOgogYDogCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogOgogOgogIDoKIDoKIGA6IAoKIDoKBSAgOgogOgogOgogOgogIDoKIDoKIGA6IAAgIAA6CiA6CiA6CjoKIDoKIDoWCiAyIOgKIDogugogOjAKIDoKIDoKBSAgOgogOgogOgogOgogIDoKIDoKIGA6IAAgIAAAAAAAAABaYQ==") + if err != nil { + panic(err) + } + f.Add(b[:len(b)-2], int(binary.LittleEndian.Uint16(b[len(b)-2:]))) f.Fuzz(func(t *testing.T, body []byte, maxBodySize int) { if len(body) > 1024*1024 || maxBodySize > 1024*1024 { diff --git a/header.go b/header.go index 57e5cb8431..bd5e3c1236 100644 --- a/header.go +++ b/header.go @@ -3331,7 +3331,6 @@ func (s *headerScanner) next() bool { s.err = errNeedMore return false } - isMultiLineValue := false for { if n+1 >= len(s.b) { break @@ -3351,14 +3350,12 @@ func (s *headerScanner) next() bool { s.nextNewLine = d - c - 1 break } - isMultiLineValue = true n = e } if n >= len(s.b) { s.err = errNeedMore return false } - oldB := s.b s.value = s.b[:n] s.hLen += n + 1 s.b = s.b[n+1:] @@ -3370,8 +3367,8 @@ func (s *headerScanner) next() bool { n-- } s.value = s.value[:n] - if isMultiLineValue { - s.value, s.b, s.hLen = normalizeHeaderValue(s.value, oldB, s.hLen) + if bytes.Contains(s.b, strCRLF) { + s.value = normalizeHeaderValue(s.value) } return true @@ -3445,7 +3442,7 @@ func getHeaderKeyBytes(bufK []byte, key string, disableNormalizing bool) []byte return bufK } -func normalizeHeaderValue(ov, ob []byte, headerLength int) (nv, nb []byte, nhl int) { +func normalizeHeaderValue(ov []byte) (nv []byte) { nv = ov length := len(ov) if length <= 0 { @@ -3480,23 +3477,8 @@ func normalizeHeaderValue(ov, ob []byte, headerLength int) (nv, nb []byte, nhl i write++ } - nv = nv[:write] - copy(ob[write:], ob[write+shrunk:]) + nv = nv[:length-shrunk] - // Check if we need to skip \r\n or just \n - skip := 0 - if ob[write] == rChar { - if ob[write+1] == nChar { - skip += 2 - } else { - skip++ - } - } else if ob[write] == nChar { - skip++ - } - - nb = ob[write+skip : len(ob)-shrunk] - nhl = headerLength - shrunk return }