tools: add syz-expand

The syz-expand tools allows to parse a program and print it including all
the default values. This is mainly useful for debugging, like doing manual
program modifications while trying to come up with a reproducer for some
particular kernel behavior.
This commit is contained in:
Andrey Konovalov 2019-09-23 15:34:59 +02:00 committed by Andrey Konovalov
parent 1e9788a0d9
commit 2b854f96b1
4 changed files with 99 additions and 14 deletions

View File

@ -172,6 +172,9 @@ trace2syz:
usbgen:
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-usbgen github.com/google/syzkaller/tools/syz-usbgen
expand:
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-expand github.com/google/syzkaller/tools/syz-expand
# `extract` extracts const files from various kernel sources, and may only
# re-generate parts of files.
extract: bin/syz-extract

View File

@ -25,11 +25,20 @@ func (p *Prog) String() string {
}
func (p *Prog) Serialize() []byte {
return p.serialize(false)
}
func (p *Prog) SerializeVerbose() []byte {
return p.serialize(true)
}
func (p *Prog) serialize(verbose bool) []byte {
p.debugValidate()
ctx := &serializer{
target: p.Target,
buf: new(bytes.Buffer),
vars: make(map[*ResultArg]int),
target: p.Target,
buf: new(bytes.Buffer),
vars: make(map[*ResultArg]int),
verbose: verbose,
}
for _, c := range p.Calls {
ctx.call(c)
@ -38,10 +47,11 @@ func (p *Prog) Serialize() []byte {
}
type serializer struct {
target *Target
buf *bytes.Buffer
vars map[*ResultArg]int
varSeq int
target *Target
buf *bytes.Buffer
vars map[*ResultArg]int
varSeq int
verbose bool
}
func (ctx *serializer) printf(text string, args ...interface{}) {
@ -91,7 +101,7 @@ func (a *PointerArg) serialize(ctx *serializer) {
}
target := ctx.target
ctx.printf("&%v", target.serializeAddr(a))
if a.Res != nil && isDefault(a.Res) && !target.isAnyPtr(a.Type()) {
if a.Res != nil && !ctx.verbose && isDefault(a.Res) && !target.isAnyPtr(a.Type()) {
return
}
ctx.printf("=")
@ -136,7 +146,7 @@ func (a *GroupArg) serialize(ctx *serializer) {
}
ctx.buf.WriteByte(delims[0])
lastNonDefault := len(a.Inner) - 1
if a.fixedInnerSize() {
if !ctx.verbose && a.fixedInnerSize() {
for ; lastNonDefault >= 0; lastNonDefault-- {
if !isDefault(a.Inner[lastNonDefault]) {
break
@ -158,7 +168,7 @@ func (a *GroupArg) serialize(ctx *serializer) {
func (a *UnionArg) serialize(ctx *serializer) {
ctx.printf("@%v", a.Option.Type().FieldName())
if isDefault(a.Option) {
if !ctx.verbose && isDefault(a.Option) {
return
}
ctx.printf("=")

View File

@ -46,19 +46,31 @@ func TestDefaultCallArgs(t *testing.T) {
}
}
func TestSerialize(t *testing.T) {
func testSerialize(t *testing.T, verbose bool) {
target, rs, iters := initTest(t)
for i := 0; i < iters; i++ {
p := target.Generate(rs, 10, nil)
data := p.Serialize()
p1, err := target.Deserialize(data, NonStrict)
var data []byte
mode := NonStrict
if verbose {
data = p.SerializeVerbose()
mode = Strict
} else {
data = p.Serialize()
}
p1, err := target.Deserialize(data, mode)
if err != nil {
t.Fatalf("failed to deserialize program: %v\n%s", err, data)
}
if p1 == nil {
t.Fatalf("deserialized nil program:\n%s", data)
}
data1 := p1.Serialize()
var data1 []byte
if verbose {
data1 = p1.SerializeVerbose()
} else {
data1 = p1.Serialize()
}
if len(p.Calls) != len(p1.Calls) {
t.Fatalf("different number of calls")
}
@ -68,6 +80,14 @@ func TestSerialize(t *testing.T) {
}
}
func TestSerialize(t *testing.T) {
testSerialize(t, false)
}
func TestSerializeVerbose(t *testing.T) {
testSerialize(t, true)
}
func TestVmaType(t *testing.T) {
target, rs, iters := initRandomTargetTest(t, "test", "64")
meta := target.SyscallMap["test$vma0"]

View File

@ -0,0 +1,52 @@
// Copyright 2019 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
// Parses a program and prints it including all default values.
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/google/syzkaller/prog"
_ "github.com/google/syzkaller/sys"
)
var (
flagOS = flag.String("os", runtime.GOOS, "target os")
flagArch = flag.String("arch", runtime.GOARCH, "target arch")
flagProg = flag.String("prog", "", "file with program to expand")
flagStrict = flag.Bool("strict", false, "parse input program in strict mode")
)
func main() {
flag.Parse()
if *flagProg == "" {
flag.Usage()
os.Exit(1)
}
target, err := prog.GetTarget(*flagOS, *flagArch)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
data, err := ioutil.ReadFile(*flagProg)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read prog file: %v\n", err)
os.Exit(1)
}
mode := prog.NonStrict
if *flagStrict {
mode = prog.Strict
}
p, err := target.Deserialize(data, mode)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err)
os.Exit(1)
}
fmt.Printf("%s", p.SerializeVerbose())
}