Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions pkg/manager/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, f
}()
}

var progs []cover.Prog
var progs []coverProgRaw
if sig := r.FormValue("input"); sig != "" {
inp := corpus.Item(sig)
if inp == nil {
Expand All @@ -530,16 +530,16 @@ func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, f
http.Error(w, "bad call_id", http.StatusBadRequest)
return
}
progs = append(progs, cover.Prog{
Sig: sig,
Data: string(inp.Prog.Serialize()),
PCs: CoverToPCs(serv.Cfg, inp.Updates[updateID].RawCover),
progs = append(progs, coverProgRaw{
sig: sig,
prog: inp.Prog,
pcs: CoverToPCs(serv.Cfg, inp.Updates[updateID].RawCover),
})
} else {
progs = append(progs, cover.Prog{
Sig: sig,
Data: string(inp.Prog.Serialize()),
PCs: CoverToPCs(serv.Cfg, inp.Cover),
progs = append(progs, coverProgRaw{
sig: sig,
prog: inp.Prog,
pcs: CoverToPCs(serv.Cfg, inp.Cover),
})
}
} else {
Expand All @@ -548,10 +548,10 @@ func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, f
if call != "" && call != inp.StringCall() {
continue
}
progs = append(progs, cover.Prog{
Sig: inp.Sig,
Data: string(inp.Prog.Serialize()),
PCs: CoverToPCs(serv.Cfg, inp.Cover),
progs = append(progs, coverProgRaw{
sig: inp.Sig,
prog: inp.Prog,
pcs: CoverToPCs(serv.Cfg, inp.Cover),
})
}
}
Expand All @@ -566,7 +566,7 @@ func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, f
}

params := cover.HandlerParams{
Progs: progs,
Progs: serv.serializeCoverProgs(progs),
Filter: coverFilter,
Debug: r.FormValue("debug") != "",
Force: r.FormValue("force") != "",
Expand Down Expand Up @@ -599,6 +599,44 @@ func (serv *HTTPServer) httpCoverCover(w http.ResponseWriter, r *http.Request, f
}
}

type coverProgRaw struct {
sig string
prog *prog.Prog
pcs []uint64
}

// Once the total size of corpus programs exceeds 100MB, skip fs images from it.
const compactProgsCutOff = 100 * 1000 * 1000

func (serv *HTTPServer) serializeCoverProgs(rawProgs []coverProgRaw) []cover.Prog {
skipImages := false
outerLoop:
for {
var flags []prog.SerializeFlag
if skipImages {
flags = append(flags, prog.SkipImages)
}
totalSize := 0
var ret []cover.Prog
for _, item := range rawProgs {
prog := cover.Prog{
Sig: item.sig,
Data: string(item.prog.Serialize(flags...)),
PCs: item.pcs,
}
totalSize += len(prog.Data)
if totalSize > compactProgsCutOff && !skipImages {
log.Logf(0, "total size of corpus programs is too big, "+
"full fs image won't be included in the cover reports")
skipImages = true
continue outerLoop
}
ret = append(ret, prog)
}
return ret
}
}

func (serv *HTTPServer) httpCoverFallback(w http.ResponseWriter, r *http.Request) {
corpus := serv.Corpus.Load()
if corpus == nil {
Expand Down
52 changes: 35 additions & 17 deletions prog/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,48 @@ func (p *Prog) String() string {
return buf.String()
}

func (p *Prog) Serialize() []byte {
return p.serialize(false)
}
type SerializeFlag int

func (p *Prog) SerializeVerbose() []byte {
return p.serialize(true)
}
const (
// Include all field values, even if they have default values.
Verbose SerializeFlag = 0
// Don't serialize compressed fs images.
// This is used in coverage report generation to prevent the bloating of the resulting HTML file.
SkipImages SerializeFlag = 1
)

func (p *Prog) serialize(verbose bool) []byte {
func (p *Prog) Serialize(flags ...SerializeFlag) []byte {
p.debugValidate()
ctx := &serializer{
target: p.Target,
buf: new(bytes.Buffer),
vars: make(map[*ResultArg]int),
verbose: verbose,
target: p.Target,
buf: new(bytes.Buffer),
vars: make(map[*ResultArg]int),
}
for _, flag := range flags {
switch flag {
case Verbose:
ctx.verbose = true
case SkipImages:
ctx.skipImages = true
}
}
for _, c := range p.Calls {
ctx.call(c)
}
return ctx.buf.Bytes()
}

func (p *Prog) SerializeVerbose() []byte {
return p.Serialize(Verbose)
}

type serializer struct {
target *Target
buf *bytes.Buffer
vars map[*ResultArg]int
varSeq int
verbose bool
target *Target
buf *bytes.Buffer
vars map[*ResultArg]int
varSeq int
verbose bool
skipImages bool
}

func (ctx *serializer) print(text string) {
Expand Down Expand Up @@ -154,7 +168,11 @@ func (a *DataArg) serialize(ctx *serializer) {
}
data := a.Data()
if typ.IsCompressed() {
serializeCompressedData(ctx.buf, data)
if ctx.skipImages {
ctx.printf(`"<<IMAGE>>"`)
} else {
serializeCompressedData(ctx.buf, data)
}
} else {
// Statically typed data will be padded with 0s during deserialization,
// so we can strip them here for readability always. For variable-size
Expand Down
13 changes: 13 additions & 0 deletions prog/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"testing"

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

func setToArray(s map[string]struct{}) []string {
Expand Down Expand Up @@ -570,3 +571,15 @@ func TestHasNext(t *testing.T) {
}
}
}

// nolint: lll
func TestDeserializeSkipImage(t *testing.T) {
target := initTargetTest(t, "linux", "amd64")
p, err := target.Deserialize([]byte(`
syz_mount_image$ext4(&(0x7f0000000080)='ext4\x00', &(0x7f00000000c0)='./file0\x00', 0x0, &(0x7f0000000100), 0x1, 0x71, &(0x7f0000000140)="$eJzszrENAVAUBdDrLyASnUIYwA5GESWdiljJDiYwgg0UWs1XfArfABI5J3kvue827/I4Tc6zpA6T2tntD5vVtu3wl0qSUZJxkum85duydYNXf70f1+/59b8AAAAAAAAAwLeSRZ8/Ds8AAAD//9ZiI98=")
`), Strict)
require.NoError(t, err)
assert.Equal(t, `syz_mount_image$ext4(&(0x7f0000000080)='ext4\x00', &(0x7f00000000c0)='./file0\x00', 0x0, &(0x7f0000000100), 0x1, 0x71, &(0x7f0000000140)="<<IMAGE>>")
`,
string(p.Serialize(SkipImages)))
}