Skip to content

Commit

Permalink
Merge branch 'main' into operators-in-stages-2679
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Jun 23, 2023
2 parents da16a46 + ced4756 commit f042aa6
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 200 deletions.
18 changes: 8 additions & 10 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,20 +279,18 @@ tasks:
cmds:
- go test -count=0 ./...

# Those commands should still run tests (i.e., should not have -run=XXX flags)
# to fill seed corpus for fuzz tests that use WriteSeedCorpusFile (e.g., FuzzHandler).
fuzz:
desc: "Fuzz for about 2 minutes (with default FUZZ_TIME)"
cmds:
- go test -list='Fuzz.*' ./...
- go test -fuzz=FuzzArray -fuzztime={{.FUZZ_TIME}} ./internal/bson/
- go test -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/bson/
- go test -fuzz=FuzzArray -fuzztime={{.FUZZ_TIME}} ./internal/handlers/sjson/
- go test -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/handlers/sjson/
- go test -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/handlers/tigris/tjson/
- go test -fuzz=FuzzMsg -fuzztime={{.FUZZ_TIME}} ./internal/wire/
- go test -fuzz=FuzzQuery -fuzztime={{.FUZZ_TIME}} ./internal/wire/
- go test -fuzz=FuzzReply -fuzztime={{.FUZZ_TIME}} ./internal/wire/
- go test -run=XXX -fuzz=FuzzArray -fuzztime={{.FUZZ_TIME}} ./internal/bson/
- go test -run=XXX -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/bson/
- go test -run=XXX -fuzz=FuzzArray -fuzztime={{.FUZZ_TIME}} ./internal/handlers/sjson/
- go test -run=XXX -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/handlers/sjson/
- go test -run=XXX -fuzz=FuzzDocument -fuzztime={{.FUZZ_TIME}} ./internal/handlers/tigris/tjson/
- go test -run=XXX -fuzz=FuzzMsg -fuzztime={{.FUZZ_TIME}} ./internal/wire/
- go test -run=XXX -fuzz=FuzzQuery -fuzztime={{.FUZZ_TIME}} ./internal/wire/
- go test -run=XXX -fuzz=FuzzReply -fuzztime={{.FUZZ_TIME}} ./internal/wire/

fuzz-corpus:
desc: "Sync seed and generated fuzz corpora with FUZZ_CORPUS"
Expand Down
5 changes: 3 additions & 2 deletions cmd/fuzztool/fuzztool.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package main
import (
"bytes"
"encoding/hex"
"errors"
"io"
"io/fs"
"os"
Expand All @@ -43,7 +44,7 @@ func generatedCorpus() (string, error) {
path := filepath.Join(string(bytes.TrimSpace(b)), "fuzz", "github.com", "FerretDB", "FerretDB")

if _, err = os.Stat(path); err != nil {
if os.IsNotExist(err) {
if errors.Is(err, fs.ErrNotExist) {
err = os.MkdirAll(path, 0o777)
}

Expand Down Expand Up @@ -129,7 +130,7 @@ func copyFile(src, dst string) error {
dir := filepath.Dir(dst)

_, err = os.Stat(dir)
if os.IsNotExist(err) {
if errors.Is(err, fs.ErrNotExist) {
err = os.MkdirAll(dir, 0o777)
}

Expand Down
46 changes: 46 additions & 0 deletions internal/handlers/sjson/sjson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import (
"errors"
"fmt"
"math"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/wire"
)

type testCase struct {
Expand Down Expand Up @@ -159,6 +161,50 @@ func fuzzJSON(f *testing.F, testCases []testCase, newFunc func() sjsontype) {
}
}

// load recorded documents only if we are fuzzing documents
if _, ok := newFunc().(*documentType); ok && !testing.Short() {
records, err := wire.LoadRecords(filepath.Join("..", "..", "..", "tmp", "records"), 1000)
require.NoError(f, err)

var n int

for _, rec := range records {
var docs []*types.Document

switch b := rec.Body.(type) {
case *wire.OpMsg:
doc, err := b.Document()
require.NoError(f, err)
docs = append(docs, doc)

case *wire.OpQuery:
if doc := b.Query; doc != nil {
docs = append(docs, doc)
}

if doc := b.ReturnFieldsSelector; doc != nil {
docs = append(docs, doc)
}

case *wire.OpReply:
docs = append(docs, b.Documents...)
}

for _, doc := range docs {
j, err := MarshalSingleValue(doc)
require.NoError(f, err)

sch, err := marshalSchemaForDoc(doc)
require.NoError(f, err)

f.Add(string(j), string(sch))
n++
}
}

f.Logf("%d recorded documents were added to the seed corpus", n)
}

f.Fuzz(func(t *testing.T, j, jsch string) {
t.Parallel()

Expand Down
46 changes: 0 additions & 46 deletions internal/util/testutil/fuzz.go

This file was deleted.

3 changes: 3 additions & 0 deletions internal/wire/bits.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type flagBit uint32

type flags uint32

// flagsSize represents flags size in bytes.
const flagsSize = 4

func (flags flags) strings(bitStringer func(flagBit) string) []string {
res := make([]string, 0, 2)
for shift := 0; shift < 32; shift++ {
Expand Down
9 changes: 3 additions & 6 deletions internal/wire/msg_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ type MsgBody interface {
// indicating that connection was closed by the client.
var ErrZeroRead = errors.New("zero bytes read")

// kFlagBitSize represents the size of the flag bits field.
const kFlagBitSize = 4

// ReadMessage reads from reader and returns wire header and body.
//
// Error is (possibly wrapped) ErrZeroRead if zero bytes was read.
Expand Down Expand Up @@ -144,7 +141,7 @@ func getChecksum(data []byte) (uint32, error) {
// ensure that the length of the body is at least the size of a flagbit
// and a crc32 checksum
n := len(data)
if n < crc32.Size+kFlagBitSize {
if n < crc32.Size+flagsSize {
return 0, lazyerrors.New("Invalid message size for an OpMsg containing a checksum")
}

Expand All @@ -158,11 +155,11 @@ func getChecksum(data []byte) (uint32, error) {
//
// TODO The callers of checksum validation should be closer to OP_MSG handling: https://github.com/FerretDB/FerretDB/issues/2690
func validateChecksum(header *MsgHeader, body []byte) error {
if len(body) < kFlagBitSize {
if len(body) < flagsSize {
return lazyerrors.New("Message contains illegal flags value")
}

flagBit := OpMsgFlags(binary.LittleEndian.Uint32(body[:kFlagBitSize]))
flagBit := OpMsgFlags(binary.LittleEndian.Uint32(body[:flagsSize]))
if !flagBit.FlagSet(OpMsgChecksumPresent) {
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/wire/op_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ type OpMsgSection struct {
type OpMsg struct {
FlagBits OpMsgFlags

checksum uint32
sections []OpMsgSection
checksum uint32
}

// SetSections of the OpMsg.
Expand Down
6 changes: 3 additions & 3 deletions internal/wire/op_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ var msgTestCases = []testCase{{
},
msgBody: &OpMsg{
FlagBits: OpMsgFlags(OpMsgChecksumPresent),
checksum: 1737537506,
sections: []OpMsgSection{{
Kind: 1,
Identifier: "documents",
Expand All @@ -307,6 +306,7 @@ var msgTestCases = []testCase{{
"$db", "test",
))},
}},
checksum: 1737537506,
},
command: "insert",
}, {
Expand Down Expand Up @@ -364,7 +364,6 @@ var msgTestCases = []testCase{{
},
msgBody: &OpMsg{
FlagBits: OpMsgFlags(OpMsgChecksumPresent),
checksum: 2932997361,
sections: []OpMsgSection{{
Kind: 1,
Identifier: "updates",
Expand All @@ -387,6 +386,7 @@ var msgTestCases = []testCase{{
"$db", "test",
))},
}},
checksum: 2932997361,
},
command: "update",
}, {
Expand Down Expand Up @@ -429,7 +429,6 @@ var msgTestCases = []testCase{{
},
msgBody: &OpMsg{
FlagBits: OpMsgFlags(OpMsgChecksumPresent),
checksum: 1737537506,
sections: []OpMsgSection{{
Kind: 1,
Identifier: "documents",
Expand All @@ -444,6 +443,7 @@ var msgTestCases = []testCase{{
"$db", "test",
))},
}},
checksum: 1737537506,
},
err: "OP_MSG checksum does not match contents.",
}}
Expand Down
122 changes: 122 additions & 0 deletions internal/wire/record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2021 FerretDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wire

import (
"bufio"
"errors"
"io/fs"
"math/rand"
"os"
"path/filepath"

"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
)

// Record represents a single recorded wire protocol message, loaded from a .bin file.
type Record struct {
Header *MsgHeader
Body MsgBody
HeaderB []byte
BodyB []byte
}

// LoadRecords finds all .bin files recursively, selects up to the limit at random (or all if limit <= 0), and parses them.
func LoadRecords(dir string, limit int) ([]Record, error) {
var files []string
err := filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return lazyerrors.Error(err)
}

if filepath.Ext(entry.Name()) == ".bin" {
files = append(files, path)
}

return nil
})

switch {
case errors.Is(err, fs.ErrNotExist):
return nil, nil
case err != nil:
return nil, lazyerrors.Error(err)
}

if limit > 0 && len(files) > limit {
f := make([]string, limit)
for fI, filesI := range rand.Perm(len(files))[:limit] {
f[fI] = files[filesI]
}
files = f
}

var res []Record

for _, file := range files {
r, err := loadRecordFile(file)
if err != nil {
return nil, lazyerrors.Errorf("%s: %w", file, err)
}

res = append(res, r...)
}

return res, nil
}

// loadRecordFile parses a single .bin file.
func loadRecordFile(file string) ([]Record, error) {
f, err := os.Open(file)
if err != nil {
return nil, lazyerrors.Error(err)
}

defer f.Close() //nolint:errcheck // we are only reading it

r := bufio.NewReader(f)

var res []Record

for {
header, body, err := ReadMessage(r)
if errors.Is(err, ErrZeroRead) {
break
}

if err != nil {
return nil, lazyerrors.Error(err)
}

headerB, err := header.MarshalBinary()
if err != nil {
return nil, lazyerrors.Error(err)
}

bodyB, err := body.MarshalBinary()
if err != nil {
return nil, lazyerrors.Error(err)
}

res = append(res, Record{
Header: header,
Body: body,
HeaderB: headerB,
BodyB: bodyB,
})
}

return res, nil
}
Loading

0 comments on commit f042aa6

Please sign in to comment.