Skip to content

Commit

Permalink
Improve bson2 fuzzing (#3988)
Browse files Browse the repository at this point in the history
Closes #3759.
  • Loading branch information
AlekSi authored Jan 19, 2024
1 parent 3eddac3 commit 2b45f90
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 34 deletions.
16 changes: 2 additions & 14 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -334,31 +334,19 @@ jobs:
path: fuzz-corpus
fetch-depth: 0 # for commit and push to work

- name: Start environment
run: bin/task env-up-detach
working-directory: source

- name: Run init
run: bin/task init
working-directory: source

- name: Setup corpus
run: bin/task fuzz-corpus
working-directory: source

- name: Wait for and setup environment
run: bin/task env-setup
working-directory: source

# precompile tests with unset GOMAXPROCS
- name: Init fuzzers
run: bin/task fuzz-init
working-directory: source

# TODO https://github.com/FerretDB/FerretDB/issues/238
# TODO https://github.com/FerretDB/FerretDB/issues/344
- name: Run collected fuzzing corpus
run: bin/task fuzz FUZZTIME=1s
- name: Fuzz
run: bin/task fuzz FUZZ_TIME=1m
working-directory: source
env:
GOMAXPROCS: 1 # otherwise, oom-killer kills fuzzer too often
Expand Down
198 changes: 192 additions & 6 deletions internal/bson2/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,152 @@ var (
)),
}

float64Doc = testCase{
name: "float64Doc",
raw: RawDocument{
0x10, 0x00, 0x00, 0x00,
0x01, 0x66, 0x00,
0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", float64(3.141592653589793),
)),
}

stringDoc = testCase{
name: "stringDoc",
raw: RawDocument{
0x0e, 0x00, 0x00, 0x00,
0x02, 0x66, 0x00,
0x02, 0x00, 0x00, 0x00,
0x76, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", "v",
)),
}

binaryDoc = testCase{
name: "binaryDoc",
raw: RawDocument{
0x0e, 0x00, 0x00, 0x00,
0x05, 0x66, 0x00,
0x01, 0x00, 0x00, 0x00,
0x80,
0x76,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", types.Binary{B: []byte("v"), Subtype: types.BinaryUser},
)),
}

objectIDDoc = testCase{
name: "objectIDDoc",
raw: RawDocument{
0x14, 0x00, 0x00, 0x00,
0x07, 0x66, 0x00,
0x62, 0x56, 0xc5, 0xba, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", types.ObjectID{0x62, 0x56, 0xc5, 0xba, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40},
)),
}

boolDoc = testCase{
name: "boolDoc",
raw: RawDocument{
0x09, 0x00, 0x00, 0x00,
0x08, 0x66, 0x00,
0x01,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", true,
)),
}

timeDoc = testCase{
name: "timeDoc",
raw: RawDocument{
0x10, 0x00, 0x00, 0x00,
0x09, 0x66, 0x00,
0x0b, 0xce, 0x82, 0x18, 0x8d, 0x01, 0x00, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", time.Date(2024, 1, 17, 17, 40, 42, 123000000, time.UTC),
)),
}

nullDoc = testCase{
name: "nullDoc",
raw: RawDocument{
0x08, 0x00, 0x00, 0x00,
0x0a, 0x66, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", types.Null,
)),
}

regexDoc = testCase{
name: "regexDoc",
raw: RawDocument{
0x0c, 0x00, 0x00, 0x00,
0x0b, 0x66, 0x00,
0x70, 0x00,
0x6f, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", types.Regex{Pattern: "p", Options: "o"},
)),
}

int32Doc = testCase{
name: "int32Doc",
raw: RawDocument{
0x0c, 0x00, 0x00, 0x00,
0x10, 0x66, 0x00,
0xa1, 0xb0, 0xb9, 0x12,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", int32(314159265),
)),
}

timestampDoc = testCase{
name: "timestampDoc",
raw: RawDocument{
0x10, 0x00, 0x00, 0x00,
0x11, 0x66, 0x00,
0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", types.Timestamp(42),
)),
}

int64Doc = testCase{
name: "int64Doc",
raw: RawDocument{
0x10, 0x00, 0x00, 0x00,
0x12, 0x66, 0x00,
0x21, 0x6d, 0x25, 0xa, 0x43, 0x29, 0xb, 0x00,
0x00,
},
doc: must.NotFail(types.NewDocument(
"f", int64(3141592653589793),
)),
}

eof = testCase{
name: "EOF",
raw: RawDocument{0x00},
Expand Down Expand Up @@ -282,6 +428,7 @@ var (

documentTestCases = []testCase{
handshake1, handshake2, handshake3, handshake4, all,
float64Doc, stringDoc, binaryDoc, objectIDDoc, boolDoc, timeDoc, nullDoc, regexDoc, int32Doc, timestampDoc, int64Doc,
eof, smallDoc, shortDoc, invalidDoc, smallArray, shortArray, invalidArray, duplicateKeys,
}
)
Expand Down Expand Up @@ -314,6 +461,10 @@ func TestDocument(t *testing.T) {
actual, err := doc.Encode()
require.NoError(t, err)
assert.Equal(t, tc.raw, actual, "actual:\n%s", hex.Dump(actual))

ls := doc.LogValue().Resolve().String()
assert.NotContains(t, ls, "panicked")
assert.NotContains(t, ls, "called too many times")
})
})

Expand Down Expand Up @@ -356,6 +507,10 @@ func TestDocument(t *testing.T) {
actual, err := doc.Convert()
require.NoError(t, err)
testutil.AssertEqual(t, tc.doc, actual)

ls := doc.LogValue().Resolve().String()
assert.NotContains(t, ls, "panicked")
assert.NotContains(t, ls, "called too many times")
})
})
})
Expand Down Expand Up @@ -383,6 +538,10 @@ func FuzzDocument(f *testing.F) {
actual, err := doc.Encode()
require.NoError(t, err)
assert.Equal(t, raw, actual, "actual:\n%s", hex.Dump(actual))

ls := doc.LogValue().Resolve().String()
assert.NotContains(t, ls, "panicked")
assert.NotContains(t, ls, "called too many times")
})

t.Run("cross", func(t *testing.T) {
Expand All @@ -391,8 +550,8 @@ func FuzzDocument(f *testing.F) {
br := bytes.NewReader(b)
bufr := bufio.NewReader(br)

var doc1 bson.Document
err1 := doc1.ReadFrom(bufr)
var bdoc1 bson.Document
err1 := bdoc1.ReadFrom(bufr)

if err1 != nil {
doc2, err2 := raw.Decode()
Expand All @@ -410,16 +569,43 @@ func FuzzDocument(f *testing.F) {
// remove extra tail
cb := b[:len(b)-bufr.Buffered()-br.Len()]

doc2, err2 := RawDocument(cb).Decode()
// decode

bdoc2, err2 := RawDocument(cb).Decode()
require.NoError(t, err2)

d1, err := types.ConvertDocument(&doc1)
ls := bdoc2.LogValue().Resolve().String()
assert.NotContains(t, ls, "panicked")
assert.NotContains(t, ls, "called too many times")

doc1, err := types.ConvertDocument(&bdoc1)
require.NoError(t, err)

doc2, err := bdoc2.Convert()
require.NoError(t, err)

testutil.AssertEqual(t, doc1, doc2)

// encode

bdoc1e, err := bson.ConvertDocument(doc1)
require.NoError(t, err)

bdoc2e, err := ConvertDocument(doc2)
require.NoError(t, err)

ls = bdoc2e.LogValue().Resolve().String()
assert.NotContains(t, ls, "panicked")
assert.NotContains(t, ls, "called too many times")

b1, err := bdoc1e.MarshalBinary()
require.NoError(t, err)

d2, err := doc2.Convert()
b2, err := bdoc2e.Encode()
require.NoError(t, err)

testutil.AssertEqual(t, d1, d2)
assert.Equal(t, b1, []byte(b2))
assert.Equal(t, cb, []byte(b2))
})
})
}
7 changes: 5 additions & 2 deletions internal/bson2/slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

package bson2

import "log/slog"
import (
"fmt"
"log/slog"
)

// slogValue converts any BSON value to slog.Value.
func slogValue(v any) slog.Value {
// TODO https://github.com/FerretDB/FerretDB/issues/3759
return slog.AnyValue(v)
return slog.StringValue(fmt.Sprintf("%#v", v))
}
8 changes: 4 additions & 4 deletions tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ require (
golang.org/x/crypto v0.18.0 // indirect; always use @latest
golang.org/x/oauth2 v0.16.0
golang.org/x/perf v0.0.0-20240108191414-4ad5199aa6b5
golang.org/x/pkgsite v0.0.0-20240108210435-8b25f917fd1e
golang.org/x/tools v0.16.1
golang.org/x/vuln v1.0.1
golang.org/x/pkgsite v0.0.0-20240117231634-9bc5594cdf34
golang.org/x/tools v0.17.0
golang.org/x/vuln v1.0.2
mvdan.cc/gofumpt v0.5.0
)

Expand Down Expand Up @@ -100,7 +100,7 @@ require (
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
16 changes: 8 additions & 8 deletions tools/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,13 @@ golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/perf v0.0.0-20240108191414-4ad5199aa6b5 h1:PwYdPa+q0X1wHuyhWfR6JandSrksyjCIz7AGeeuyvqk=
golang.org/x/perf v0.0.0-20240108191414-4ad5199aa6b5/go.mod h1:kCrIb1Um4s5xKJaUgzwyqbtv/ILXfS+eNQkThFkWW6c=
golang.org/x/pkgsite v0.0.0-20240108210435-8b25f917fd1e h1:Qka5pw8LeTLZHdNRTs+puGcWPeRLRbeJHthJr0llXX0=
golang.org/x/pkgsite v0.0.0-20240108210435-8b25f917fd1e/go.mod h1:IFRpJ4lLx3YX8ftRTZZohAwgzTe6s7WdISSixMoEu+o=
golang.org/x/pkgsite v0.0.0-20240117231634-9bc5594cdf34 h1:dqSLLASPj5Apou5DA+Z8+Do758D30ZFYrA05xoF/J5E=
golang.org/x/pkgsite v0.0.0-20240117231634-9bc5594cdf34/go.mod h1:saEYxTRYGIEQMDs70N08MTCmv5jIpCzDwHNkRd2pwCI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -320,10 +320,10 @@ golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/vuln v1.0.1 h1:KUas02EjQK5LTuIx1OylBQdKKZ9jeugs+HiqO5HormU=
golang.org/x/vuln v1.0.1/go.mod h1:bb2hMwln/tqxg32BNY4CcxHWtHXuYa3SbIBmtsyjxtM=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/vuln v1.0.2 h1:ZoomxsEYcaFJadvW6E9cOBC5MChT8G2F++5RYZgRfnc=
golang.org/x/vuln v1.0.2/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
Expand Down
2 changes: 2 additions & 0 deletions tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
_ "github.com/quasilyte/go-consistent"
_ "golang.org/x/perf/cmd/benchstat"
_ "golang.org/x/pkgsite/cmd/pkgsite"
_ "golang.org/x/tools/cmd/deadcode"
_ "golang.org/x/tools/cmd/goimports"
_ "golang.org/x/tools/cmd/stringer"
_ "golang.org/x/vuln/cmd/govulncheck"
Expand All @@ -47,6 +48,7 @@ import (
//go:generate go build -v -o ../bin/ github.com/quasilyte/go-consistent
//go:generate go build -v -o ../bin/ golang.org/x/perf/cmd/benchstat
//go:generate go build -v -o ../bin/ golang.org/x/pkgsite/cmd/pkgsite
//go:generate go build -v -o ../bin/ golang.org/x/tools/cmd/deadcode
//go:generate go build -v -o ../bin/ golang.org/x/tools/cmd/goimports
//go:generate go build -v -o ../bin/ golang.org/x/tools/cmd/stringer
//go:generate go build -v -o ../bin/ golang.org/x/vuln/cmd/govulncheck
Expand Down

0 comments on commit 2b45f90

Please sign in to comment.