Skip to content

Commit

Permalink
Merge branch 'valyala:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
tylitianrui committed Jan 13, 2024
2 parents 30a5d4c + a04cd8c commit 3866e94
Show file tree
Hide file tree
Showing 19 changed files with 173 additions and 159 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'fasthttp'
dry-run: false
language: go
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'fasthttp'
fuzz-seconds: 300
dry-run: false
language: go
- name: Upload Crash
uses: actions/upload-artifact@v3
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tags
.vscode
.DS_Store
vendor/
testdata/fuzz
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ linters-settings:
"all",
"-ST1000", # at least one file in a package should have a package comment
]
gocritic:
enabled-checks:
- emptyStringTest

issues:
# Show all issues from a linter.
Expand Down
5 changes: 3 additions & 2 deletions bytesconv_table.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions bytesconv_table_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,34 @@ func main() {
return a
}()

tcharTable := func() [128]byte {
// Should match net/textproto's validHeaderFieldByte(c byte) bool
// Defined by RFC 9110 5.6.2
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
var a [128]byte
for _, v := range "!#$%&'*+-.^_`|~" {
a[v] = 1
}
for i := 'a'; i <= 'z'; i++ {
a[i] = 1
}
for i := 'A'; i <= 'Z'; i++ {
a[i] = 1
}
for i := '0'; i <= '9'; i++ {
a[i] = 1
}
return a
}()

w := bytes.NewBufferString(pre)
fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable)
fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable)
fmt.Fprintf(w, "const toUpperTable = %q\n", toUpperTable)
fmt.Fprintf(w, "const quotedArgShouldEscapeTable = %q\n", quotedArgShouldEscapeTable)
fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
fmt.Fprintf(w, "const tcharTable = %q\n", tcharTable)

if err := os.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
log.Fatal(err)
Expand Down
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,7 @@ func newClientTLSConfig(c *tls.Config, addr string) *tls.Config {
c = c.Clone()
}

if len(c.ServerName) == 0 {
if c.ServerName == "" {
serverName := tlsServerName(addr)
if serverName == "*" {
c.InsecureSkipVerify = true
Expand Down
2 changes: 1 addition & 1 deletion expvarhandler/expvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func ExpvarHandler(ctx *fasthttp.RequestCtx) {

func getExpvarRegexp(ctx *fasthttp.RequestCtx) (*regexp.Regexp, error) {
r := string(ctx.QueryArgs().Peek("r"))
if len(r) == 0 {
if r == "" {
return defaultRE, nil
}
rr, err := regexp.Compile(r)
Expand Down
13 changes: 10 additions & 3 deletions expvarhandler/expvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ import (
"encoding/json"
"expvar"
"strings"
"sync"
"testing"

"github.com/valyala/fasthttp"
)

var once sync.Once

func TestExpvarHandlerBasic(t *testing.T) {
t.Parallel()

expvar.Publish("customVar", expvar.Func(func() any {
return "foobar"
}))
// Publish panics if the same var is published more than once,
// which can happen if the test is run with -count
once.Do(func() {
expvar.Publish("customVar", expvar.Func(func() any {
return "foobar"
}))
})

var ctx fasthttp.RequestCtx

Expand Down
12 changes: 6 additions & 6 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func ServeFile(ctx *RequestCtx, path string) {
rootFSHandler = rootFS.NewRequestHandler()
})

if len(path) == 0 || !filepath.IsAbs(path) {
if path == "" || !filepath.IsAbs(path) {
// extend relative path to absolute path
hasTrailingSlash := len(path) > 0 && (path[len(path)-1] == '/' || path[len(path)-1] == '\\')

Expand Down Expand Up @@ -429,7 +429,7 @@ func (fs *FS) normalizeRoot(root string) string {
// fs.FS uses relative paths, that paths are slash-separated on all systems, even Windows.
if fs.FS == nil {
// Serve files from the current working directory if Root is empty or if Root is a relative path.
if (!fs.AllowEmptyRoot && len(root) == 0) || (len(root) > 0 && !filepath.IsAbs(root)) {
if (!fs.AllowEmptyRoot && root == "") || (root != "" && !filepath.IsAbs(root)) {
path, err := os.Getwd()
if err != nil {
path = "."
Expand All @@ -452,14 +452,14 @@ func (fs *FS) initRequestHandler() {
root := fs.normalizeRoot(fs.Root)

compressRoot := fs.CompressRoot
if len(compressRoot) == 0 {
if compressRoot == "" {
compressRoot = root
} else {
compressRoot = fs.normalizeRoot(compressRoot)
}

compressedFileSuffixes := fs.CompressedFileSuffixes
if len(compressedFileSuffixes["br"]) == 0 || len(compressedFileSuffixes["gzip"]) == 0 ||
if compressedFileSuffixes["br"] == "" || compressedFileSuffixes["gzip"] == "" ||
compressedFileSuffixes["br"] == compressedFileSuffixes["gzip"] {
// Copy global map
compressedFileSuffixes = make(map[string]string, len(FSCompressedFileSuffixes))
Expand Down Expand Up @@ -1474,7 +1474,7 @@ func (h *fsHandler) newCompressedFSFileCache(f fs.File, fileInfo fs.FileInfo, fi

ext := fileExtension(fileInfo.Name(), false, h.compressedFileSuffixes[fileEncoding])
contentType := mime.TypeByExtension(ext)
if len(contentType) == 0 {
if contentType == "" {
data, err := readFileHeader(f, false, fileEncoding)
if err != nil {
return nil, fmt.Errorf("cannot read header of the file %q: %w", fileInfo.Name(), err)
Expand Down Expand Up @@ -1573,7 +1573,7 @@ func (h *fsHandler) newFSFile(f fs.File, fileInfo fs.FileInfo, compressed bool,
// detect content-type
ext := fileExtension(fileInfo.Name(), compressed, h.compressedFileSuffixes[fileEncoding])
contentType := mime.TypeByExtension(ext)
if len(contentType) == 0 {
if contentType == "" {
data, err := readFileHeader(f, compressed, fileEncoding)
if err != nil {
return nil, fmt.Errorf("cannot read header of the file %q: %w", fileInfo.Name(), err)
Expand Down
93 changes: 93 additions & 0 deletions fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package fasthttp

import (
"bufio"
"bytes"
"testing"
)

func FuzzCookieParse(f *testing.F) {
inputs := []string{
`xxx=yyy`,
`xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b`,
" \n\t\"",
}
for _, input := range inputs {
f.Add([]byte(input))
}
c := AcquireCookie()
defer ReleaseCookie(c)
f.Fuzz(func(t *testing.T, cookie []byte) {
_ = c.ParseBytes(cookie)

w := bytes.Buffer{}
if _, err := c.WriteTo(&w); err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}

func FuzzVisitHeaderParams(f *testing.F) {
inputs := []string{
`application/json; v=1; foo=bar; q=0.938; param=param; param="big fox"; q=0.43`,
`*/*`,
`\\`,
`text/plain; foo="\\\"\'\\''\'"`,
}
for _, input := range inputs {
f.Add([]byte(input))
}
f.Fuzz(func(t *testing.T, header []byte) {
VisitHeaderParams(header, func(key, value []byte) bool {
if len(key) == 0 {
t.Errorf("Unexpected length zero parameter, failed input was: %s", header)
}
return true
})
})
}

func FuzzResponseReadLimitBody(f *testing.F) {
res := AcquireResponse()
defer ReleaseResponse(res)

f.Add([]byte("HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210"), 1024*1024)

f.Fuzz(func(t *testing.T, body []byte, max int) {
_ = res.ReadLimitBody(bufio.NewReader(bytes.NewReader(body)), max)
w := bytes.Buffer{}
_, _ = res.WriteTo(&w)
})
}

func FuzzRequestReadLimitBody(f *testing.F) {
req := AcquireRequest()
defer ReleaseRequest(req)

f.Add([]byte("POST /a HTTP/1.1\r\nHost: a.com\r\nTransfer-Encoding: chunked\r\nContent-Type: aa\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n"), 1024*1024)

f.Fuzz(func(t *testing.T, body []byte, max int) {
_ = req.ReadLimitBody(bufio.NewReader(bytes.NewReader(body)), max)
w := bytes.Buffer{}
_, _ = req.WriteTo(&w)
})
}

func FuzzURIUpdateBytes(f *testing.F) {
u := AcquireURI()
defer ReleaseURI(u)

f.Add([]byte(`http://foobar.com/aaa/bb?cc`))
f.Add([]byte(`//foobar.com/aaa/bb?cc`))
f.Add([]byte(`/aaa/bb?cc`))
f.Add([]byte(`xx?yy=abc`))

f.Fuzz(func(t *testing.T, uri []byte) {
u.UpdateBytes(uri)

w := bytes.Buffer{}
if _, err := u.WriteTo(&w); err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
}
25 changes: 0 additions & 25 deletions fuzzit/cookie/cookie_fuzz.go

This file was deleted.

26 changes: 0 additions & 26 deletions fuzzit/request/request_fuzz.go

This file was deleted.

26 changes: 0 additions & 26 deletions fuzzit/response/response_fuzz.go

This file was deleted.

23 changes: 0 additions & 23 deletions fuzzit/url/url_fuzz.go

This file was deleted.

0 comments on commit 3866e94

Please sign in to comment.