From 19b893936bebc6189c7627d56d1dc454fbd42714 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 18 Aug 2017 16:38:41 +0200 Subject: sys/syz-extract: switch to the new parser --- pkg/ast/ast.go | 9 +++-- pkg/ast/parser.go | 13 +++++- pkg/ast/scanner.go | 6 +++ pkg/ast/walk.go | 94 +++++++++++++++++++++++++++++++++++++++++++ pkg/compiler/compiler.go | 65 ++++++++++++++++++++++++++++++ pkg/compiler/compiler_test.go | 59 +++++++++++++++++++++++++++ 6 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 pkg/ast/walk.go create mode 100644 pkg/compiler/compiler.go create mode 100644 pkg/compiler/compiler_test.go (limited to 'pkg') diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index 27497b2a9..e2ddc0224 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -47,10 +47,11 @@ type Resource struct { } type Call struct { - Pos Pos - Name *Ident - Args []*Field - Ret *Type + Pos Pos + Name *Ident + CallName string + Args []*Field + Ret *Type } type Struct struct { diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go index 737d08068..10f389424 100644 --- a/pkg/ast/parser.go +++ b/pkg/ast/parser.go @@ -210,8 +210,9 @@ func (p *parser) parseResource() *Resource { func (p *parser) parseCall(name *Ident) *Call { c := &Call{ - Pos: name.Pos, - Name: name, + Pos: name.Pos, + Name: name, + CallName: callName(name.Name), } p.consume(tokLParen) for p.tok != tokRParen { @@ -226,6 +227,14 @@ func (p *parser) parseCall(name *Ident) *Call { return c } +func callName(s string) string { + pos := strings.IndexByte(s, '$') + if pos == -1 { + return s + } + return s[:pos] +} + func (p *parser) parseFlags(name *Ident) interface{} { p.consume(tokEq) switch p.tok { diff --git a/pkg/ast/scanner.go b/pkg/ast/scanner.go index ee15cee03..d3e35895c 100644 --- a/pkg/ast/scanner.go +++ b/pkg/ast/scanner.go @@ -5,6 +5,7 @@ package ast import ( "fmt" + "os" "strconv" ) @@ -101,6 +102,11 @@ type scanner struct { } func newScanner(data []byte, filename string, errorHandler func(pos Pos, msg string)) *scanner { + if errorHandler == nil { + errorHandler = func(pos Pos, msg string) { + fmt.Fprintf(os.Stderr, "%v:%v:%v: %v\n", pos.File, pos.Line, pos.Col, msg) + } + } s := &scanner{ data: data, filename: filename, diff --git a/pkg/ast/walk.go b/pkg/ast/walk.go new file mode 100644 index 000000000..f4daeaccd --- /dev/null +++ b/pkg/ast/walk.go @@ -0,0 +1,94 @@ +// Copyright 2017 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. + +package ast + +import ( + "fmt" +) + +// Walk calls callback cb for every node in AST. +func Walk(top []interface{}, cb func(n interface{})) { + for _, decl := range top { + walkNode(decl, cb) + } +} + +func walkNode(n0 interface{}, cb func(n interface{})) { + switch n := n0.(type) { + case *NewLine: + cb(n) + case *Comment: + cb(n) + case *Include: + cb(n) + walkNode(n.File, cb) + case *Incdir: + cb(n) + walkNode(n.Dir, cb) + case *Define: + cb(n) + walkNode(n.Name, cb) + walkNode(n.Value, cb) + case *Resource: + cb(n) + walkNode(n.Name, cb) + walkNode(n.Base, cb) + for _, v := range n.Values { + walkNode(v, cb) + } + case *Call: + cb(n) + walkNode(n.Name, cb) + for _, f := range n.Args { + walkNode(f, cb) + } + if n.Ret != nil { + walkNode(n.Ret, cb) + } + case *Struct: + cb(n) + walkNode(n.Name, cb) + for _, f := range n.Fields { + walkNode(f, cb) + } + for _, a := range n.Attrs { + walkNode(a, cb) + } + for _, c := range n.Comments { + walkNode(c, cb) + } + case *IntFlags: + cb(n) + walkNode(n.Name, cb) + for _, v := range n.Values { + walkNode(v, cb) + } + case *StrFlags: + cb(n) + walkNode(n.Name, cb) + for _, v := range n.Values { + walkNode(v, cb) + } + case *Ident: + cb(n) + case *String: + cb(n) + case *Int: + cb(n) + case *Type: + cb(n) + for _, t := range n.Args { + walkNode(t, cb) + } + case *Field: + cb(n) + walkNode(n.Name, cb) + walkNode(n.Type, cb) + for _, c := range n.Comments { + walkNode(c, cb) + } + default: + panic(fmt.Sprintf("unknown AST node: %#v", n)) + } +} diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go new file mode 100644 index 000000000..7892661fa --- /dev/null +++ b/pkg/compiler/compiler.go @@ -0,0 +1,65 @@ +// Copyright 2017 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. + +package compiler + +import ( + "fmt" + "sort" + "strings" + + "github.com/google/syzkaller/pkg/ast" +) + +// ExtractConsts returns list of literal constants and other info required const value extraction. +func ExtractConsts(top []interface{}) (consts, includes, incdirs []string, defines map[string]string) { + constMap := make(map[string]bool) + defines = make(map[string]string) + + ast.Walk(top, func(n1 interface{}) { + switch n := n1.(type) { + case *ast.Include: + includes = append(includes, n.File.Value) + case *ast.Incdir: + incdirs = append(incdirs, n.Dir.Value) + case *ast.Define: + v := fmt.Sprint(n.Value.Value) + switch { + case n.Value.CExpr != "": + v = n.Value.CExpr + case n.Value.Ident != "": + v = n.Value.Ident + } + defines[n.Name.Name] = v + constMap[n.Name.Name] = true + case *ast.Call: + if !strings.HasPrefix(n.CallName, "syz_") { + constMap["__NR_"+n.CallName] = true + } + case *ast.Type: + if n.Ident == "const" && len(n.Args) > 0 { + constMap[n.Args[0].Ident] = true + } + if n.Ident == "array" && len(n.Args) > 1 { + constMap[n.Args[1].Ident] = true + } + case *ast.Int: + constMap[n.Ident] = true + } + }) + + consts = toArray(constMap) + return +} + +func toArray(m map[string]bool) []string { + delete(m, "") + var res []string + for v := range m { + if v != "" { + res = append(res, v) + } + } + sort.Strings(res) + return res +} diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go new file mode 100644 index 000000000..13ed97b4c --- /dev/null +++ b/pkg/compiler/compiler_test.go @@ -0,0 +1,59 @@ +// Copyright 2017 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. + +package compiler + +import ( + "reflect" + "testing" + + "github.com/google/syzkaller/pkg/ast" +) + +func TestExtractConsts(t *testing.T) { + top, ok := ast.Parse([]byte(extractConstsInput), "test", nil) + if !ok { + t.Fatalf("failed to parse input") + } + consts, includes, incdirs, defines := ExtractConsts(top) + wantConsts := []string{"CONST1", "CONST2", "CONST3", "CONST4", "CONST5", + "CONST6", "CONST7", "__NR_bar", "__NR_foo"} + if !reflect.DeepEqual(consts, wantConsts) { + t.Fatalf("got consts:\n%q\nwant:\n%q", consts, wantConsts) + } + wantIncludes := []string{"foo/bar.h", "bar/foo.h"} + if !reflect.DeepEqual(includes, wantIncludes) { + t.Fatalf("got includes:\n%q\nwant:\n%q", includes, wantIncludes) + } + wantIncdirs := []string{"/foo", "/bar"} + if !reflect.DeepEqual(incdirs, wantIncdirs) { + t.Fatalf("got incdirs:\n%q\nwant:\n%q", incdirs, wantIncdirs) + } + wantDefines := map[string]string{ + "CONST1": "1", + "CONST2": "FOOBAR + 1", + } + if !reflect.DeepEqual(defines, wantDefines) { + t.Fatalf("got defines:\n%q\nwant:\n%q", defines, wantDefines) + } +} + +const extractConstsInput = ` +include +incdir +include +incdir + +flags = CONST3, CONST2, CONST1 + +define CONST1 1 +define CONST2 FOOBAR + 1 + +foo(x const[CONST4]) ptr[out, array[int32, CONST5]] +bar$BAR() + +str { + f1 const[CONST6, int32] + f2 array[array[int8, CONST7]] +} +` -- cgit mrf-deployment