diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-12-09 18:05:58 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-12-10 16:37:01 +0100 |
| commit | ba64d006de17a5bfa0c99e4b96711ba42f82e199 (patch) | |
| tree | 9625242c1510aef3b4ff0d793b885e66f1b29914 | |
| parent | 95fe19c19e596446412626b048d950de6ce8c886 (diff) | |
prog: implement strict parsing mode
Add bulk of checks for strict parsing mode.
Probably not complete, but we can extend then in future as needed.
Turns out we can't easily use it for serialized programs
as they omit default args and during deserialization it looks like missing args.
| -rw-r--r-- | prog/encoding.go | 50 | ||||
| -rw-r--r-- | prog/encoding_test.go | 63 | ||||
| -rw-r--r-- | prog/mutation_test.go | 2 | ||||
| -rw-r--r-- | prog/prog_test.go | 8 | ||||
| -rw-r--r-- | sys/linux/init_test.go | 4 | ||||
| -rw-r--r-- | sys/test/test/align0 | 4 | ||||
| -rw-r--r-- | sys/test/test/test | 2 | ||||
| -rw-r--r-- | syz-fuzzer/fuzzer.go | 2 | ||||
| -rw-r--r-- | syz-fuzzer/testing.go | 2 | ||||
| -rw-r--r-- | syz-manager/html.go | 4 | ||||
| -rw-r--r-- | syz-manager/manager.go | 2 |
11 files changed, 83 insertions, 60 deletions
diff --git a/prog/encoding.go b/prog/encoding.go index 9ba9a6bb4..2b4e4bf8c 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -189,10 +189,10 @@ const ( func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, error) { p := newParser(target, data, mode == Strict) prog, err := p.parseProg() - if err != nil { + if err := p.Err(); err != nil { return nil, err } - if err := p.Err(); err != nil { + if err != nil { return nil, err } // This validation is done even in non-debug mode because deserialization @@ -247,10 +247,7 @@ func (p *parser) parseProg() (*Prog, error) { p.Parse('(') for i := 0; p.Char() != ')'; i++ { if i >= len(meta.Args) { - if p.strict { - return nil, fmt.Errorf("excessive syscall arguments (line #%v)", p.l) - } - p.eatExcessive(false) + p.eatExcessive(false, "excessive syscall arguments") break } typ := meta.Args[i] @@ -278,6 +275,7 @@ func (p *parser) parseProg() (*Prog, error) { c.Comment = strings.TrimSpace(p.s[p.i+1:]) } for i := len(c.Args); i < len(meta.Args); i++ { + p.strictFailf("missing syscall args") c.Args = append(c.Args, meta.Args[i].DefaultArg()) } if len(c.Args) != len(meta.Args) { @@ -342,7 +340,6 @@ func (p *parser) parseArgImpl(typ Type) (Arg, error) { p.Parse('i') p.Parse('l') return nil, nil - default: return nil, fmt.Errorf("failed to parse argument at %v (line #%v/%v: %v)", int(p.Char()), p.l, p.i, p.s) @@ -364,7 +361,7 @@ func (p *parser) parseArgInt(typ Type) (Arg, error) { index := -v % uint64(len(p.target.SpecialPointers)) return MakeSpecialPointerArg(typ, index), nil default: - p.eatExcessive(true) + p.eatExcessive(true, "wrong int arg") return typ.DefaultArg(), nil } } @@ -392,6 +389,7 @@ func (p *parser) parseArgRes(typ Type) (Arg, error) { } v := p.vars[id] if v == nil { + p.strictFailf("undeclared variable %v", id) return typ.DefaultArg(), nil } arg := MakeResultArg(typ, v, 0) @@ -407,7 +405,7 @@ func (p *parser) parseArgAddr(typ Type) (Arg, error) { typ1 = t1.Type case *VmaType: default: - p.eatExcessive(true) + p.eatExcessive(true, "wrong addr arg") return typ.DefaultArg(), nil } p.Parse('&') @@ -442,7 +440,7 @@ func (p *parser) parseArgAddr(typ Type) (Arg, error) { func (p *parser) parseArgString(typ Type) (Arg, error) { if _, ok := typ.(*BufferType); !ok { - p.eatExcessive(true) + p.eatExcessive(true, "wrong string arg") return typ.DefaultArg(), nil } data, err := p.deserializeData() @@ -477,14 +475,14 @@ func (p *parser) parseArgStruct(typ Type) (Arg, error) { p.Parse('{') t1, ok := typ.(*StructType) if !ok { - p.eatExcessive(false) + p.eatExcessive(false, "wrong struct arg") p.Parse('}') return typ.DefaultArg(), nil } var inner []Arg for i := 0; p.Char() != '}'; i++ { if i >= len(t1.Fields) { - p.eatExcessive(false) + p.eatExcessive(false, "excessive struct %v fields", typ.Name()) break } fld := t1.Fields[i] @@ -503,7 +501,11 @@ func (p *parser) parseArgStruct(typ Type) (Arg, error) { } p.Parse('}') for len(inner) < len(t1.Fields) { - inner = append(inner, t1.Fields[len(inner)].DefaultArg()) + fld := t1.Fields[len(inner)] + if !IsPad(fld) { + p.strictFailf("missing struct %v fields %v/%v", typ.Name(), len(inner), len(t1.Fields)) + } + inner = append(inner, fld.DefaultArg()) } return MakeGroupArg(typ, inner), nil } @@ -512,7 +514,7 @@ func (p *parser) parseArgArray(typ Type) (Arg, error) { p.Parse('[') t1, ok := typ.(*ArrayType) if !ok { - p.eatExcessive(false) + p.eatExcessive(false, "wrong array arg") p.Parse(']') return typ.DefaultArg(), nil } @@ -530,6 +532,7 @@ func (p *parser) parseArgArray(typ Type) (Arg, error) { p.Parse(']') if t1.Kind == ArrayRangeLen && t1.RangeBegin == t1.RangeEnd { for uint64(len(inner)) < t1.RangeBegin { + p.strictFailf("missing array elements") inner = append(inner, t1.Type.DefaultArg()) } inner = inner[:t1.RangeBegin] @@ -540,7 +543,7 @@ func (p *parser) parseArgArray(typ Type) (Arg, error) { func (p *parser) parseArgUnion(typ Type) (Arg, error) { t1, ok := typ.(*UnionType) if !ok { - p.eatExcessive(true) + p.eatExcessive(true, "wrong union arg") return typ.DefaultArg(), nil } p.Parse('@') @@ -553,7 +556,7 @@ func (p *parser) parseArgUnion(typ Type) (Arg, error) { } } if optType == nil { - p.eatExcessive(true) + p.eatExcessive(true, "wrong union option") return typ.DefaultArg(), nil } var opt Arg @@ -571,7 +574,10 @@ func (p *parser) parseArgUnion(typ Type) (Arg, error) { } // Eats excessive call arguments and struct fields to recover after description changes. -func (p *parser) eatExcessive(stopAtComma bool) { +func (p *parser) eatExcessive(stopAtComma bool, what string, args ...interface{}) { + if p.strict { + p.failf(what, args...) + } paren, brack, brace := 0, 0, 0 for !p.EOF() && p.e == nil { ch := p.Char() @@ -906,7 +912,15 @@ func (p *parser) Ident() string { } func (p *parser) failf(msg string, args ...interface{}) { - p.e = fmt.Errorf("%v\nline #%v: %v", fmt.Sprintf(msg, args...), p.l, p.s) + if p.e == nil { + p.e = fmt.Errorf("%v\nline #%v:%v: %v", fmt.Sprintf(msg, args...), p.l, p.i, p.s) + } +} + +func (p *parser) strictFailf(msg string, args ...interface{}) { + if p.strict { + p.failf(msg, args...) + } } // CallSet returns a set of all calls in the program. diff --git a/prog/encoding_test.go b/prog/encoding_test.go index a98b5ee7b..acaf642d5 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -137,8 +137,9 @@ func TestDeserialize(t *testing.T) { input: `test$struct(&(0x7f0000000000)={0x0, {0x0}})`, }, { - input: `test$struct(&(0x7f0000000000)=0x0)`, - output: `test$struct(&(0x7f0000000000))`, + input: `test$struct(&(0x7f0000000000)=0x0)`, + output: `test$struct(&(0x7f0000000000))`, + strictErr: regexp.MustCompile("wrong int arg"), }, { input: `test$regression1(&(0x7f0000000000)=[{"000000"}, {"0000000000"}])`, @@ -164,57 +165,65 @@ func TestDeserialize(t *testing.T) { }, { input: `test$excessive_fields1(&(0x7f0000000000)={0x1, &(0x7f0000000000)=[{0x0}, 0x2]}, {0x1, 0x2, [0x1, 0x2]})`, - strictErr: regexp.MustCompile("excessive syscall arguments"), + strictErr: regexp.MustCompile("excessive struct excessive_fields fields"), }, { input: `test$excessive_fields1(0x0)`, output: `test$excessive_fields1(0x0)`, }, { - input: `test$excessive_fields1(r0)`, - output: `test$excessive_fields1(&(0x7f0000000000))`, + input: `test$excessive_fields1(r0)`, + output: `test$excessive_fields1(&(0x7f0000000000))`, + strictErr: regexp.MustCompile("undeclared variable r0"), }, { - input: `test$excessive_args2(r1)`, - output: `test$excessive_args2(0x0)`, + input: `test$excessive_args2(r1)`, + output: `test$excessive_args2(0x0)`, + strictErr: regexp.MustCompile("undeclared variable r1"), }, { - input: `test$excessive_args2({0x0, 0x1})`, - output: `test$excessive_args2(0x0)`, + input: `test$excessive_args2({0x0, 0x1})`, + output: `test$excessive_args2(0x0)`, + strictErr: regexp.MustCompile("wrong struct arg"), }, { input: `test$excessive_args2([0x0], 0x0)`, output: `test$excessive_args2(0x0)`, - strictErr: regexp.MustCompile("excessive syscall arguments"), + strictErr: regexp.MustCompile("wrong array arg"), }, { - input: `test$excessive_args2(@foo)`, - output: `test$excessive_args2(0x0)`, + input: `test$excessive_args2(@foo)`, + output: `test$excessive_args2(0x0)`, + strictErr: regexp.MustCompile("wrong union arg"), }, { - input: `test$excessive_args2('foo')`, - output: `test$excessive_args2(0x0)`, + input: `test$excessive_args2('foo')`, + output: `test$excessive_args2(0x0)`, + strictErr: regexp.MustCompile("wrong string arg"), }, { - input: `test$excessive_args2(&(0x7f0000000000)={0x0, 0x1})`, - output: `test$excessive_args2(0x0)`, + input: `test$excessive_args2(&(0x7f0000000000)={0x0, 0x1})`, + output: `test$excessive_args2(0x0)`, + strictErr: regexp.MustCompile("wrong addr arg"), }, { input: `test$excessive_args2(nil)`, output: `test$excessive_args2(0x0)`, }, { - input: `test$type_confusion1(&(0x7f0000000000)=@unknown)`, - output: `test$type_confusion1(&(0x7f0000000000))`, + input: `test$type_confusion1(&(0x7f0000000000)=@unknown)`, + output: `test$type_confusion1(&(0x7f0000000000))`, + strictErr: regexp.MustCompile("wrong union option"), }, { input: `test$type_confusion1(&(0x7f0000000000)=@unknown={0x0, 'abc'}, 0x0)`, output: `test$type_confusion1(&(0x7f0000000000))`, - strictErr: regexp.MustCompile("excessive syscall arguments"), + strictErr: regexp.MustCompile("wrong union option"), }, { - input: `test$excessive_fields1(&(0x7f0000000000)=0x0)`, - output: `test$excessive_fields1(&(0x7f0000000000))`, + input: `test$excessive_fields1(&(0x7f0000000000)=0x0)`, + output: `test$excessive_fields1(&(0x7f0000000000))`, + strictErr: regexp.MustCompile("wrong int arg"), }, { input: `test$excessive_fields1(0x0)`, @@ -345,7 +354,7 @@ func testSerializeDeserialize(t *testing.T, p0 *Prog, data0, data1 []byte) (bool t.Fatal(err) } serialized := p0.Serialize() - p1, err := p0.Target.Deserialize(serialized, Strict) + p1, err := p0.Target.Deserialize(serialized, NonStrict) if err != nil { t.Fatal(err) } @@ -364,15 +373,15 @@ func TestDeserializeComments(t *testing.T) { p, err := target.Deserialize([]byte(` # comment1 # comment2 -serialize0() -serialize0() +serialize0(0x0) +serialize0(0x0) # comment3 -serialize0() +serialize0(0x0) # comment4 -serialize0() # comment5 +serialize0(0x0) # comment5 #comment6 -serialize0() +serialize0(0x0) #comment7 `), Strict) if err != nil { diff --git a/prog/mutation_test.go b/prog/mutation_test.go index 3f5d4f10b..a29191539 100644 --- a/prog/mutation_test.go +++ b/prog/mutation_test.go @@ -44,7 +44,7 @@ func TestMutateRandom(t *testing.T) { if bytes.Equal(data, data1) { continue } - if _, err := target.Deserialize(data1, Strict); err != nil { + if _, err := target.Deserialize(data1, NonStrict); err != nil { t.Fatalf("Deserialize failed after Mutate: %v\n%s", err, data1) } continue next diff --git a/prog/prog_test.go b/prog/prog_test.go index 1edb7690b..45ab56cfe 100644 --- a/prog/prog_test.go +++ b/prog/prog_test.go @@ -37,7 +37,7 @@ func TestDefaultCallArgs(t *testing.T) { for _, meta := range target.SyscallMap { // Ensure that we can restore all arguments of all calls. prog := fmt.Sprintf("%v()", meta.Name) - p, err := target.Deserialize([]byte(prog), Strict) + p, err := target.Deserialize([]byte(prog), NonStrict) if err != nil { t.Fatalf("failed to restore default args in prog %q: %v", prog, err) } @@ -52,7 +52,7 @@ func TestSerialize(t *testing.T) { for i := 0; i < iters; i++ { p := target.Generate(rs, 10, nil) data := p.Serialize() - p1, err := target.Deserialize(data, Strict) + p1, err := target.Deserialize(data, NonStrict) if err != nil { t.Fatalf("failed to deserialize program: %v\n%s", err, data) } @@ -154,7 +154,7 @@ func testCrossTarget(t *testing.T, target *Target, crossTargets []*Target) { for i := 0; i < iters; i++ { p := target.Generate(rs, 20, nil) testCrossArchProg(t, p, crossTargets) - p, err := target.Deserialize(p.Serialize(), Strict) + p, err := target.Deserialize(p.Serialize(), NonStrict) if err != nil { t.Fatal(err) } @@ -171,7 +171,7 @@ func testCrossTarget(t *testing.T, target *Target, crossTargets []*Target) { func testCrossArchProg(t *testing.T, p *Prog, crossTargets []*Target) { serialized := p.Serialize() for _, crossTarget := range crossTargets { - _, err := crossTarget.Deserialize(serialized, Strict) + _, err := crossTarget.Deserialize(serialized, NonStrict) if err == nil || strings.Contains(err.Error(), "unknown syscall") { continue } diff --git a/sys/linux/init_test.go b/sys/linux/init_test.go index 9f59be16c..104d3c068 100644 --- a/sys/linux/init_test.go +++ b/sys/linux/init_test.go @@ -39,11 +39,11 @@ func TestSanitize(t *testing.T) { `ptrace(0xffffffffffffffff, 0x0)`, }, { - `ptrace$peek(0x0)`, + `ptrace$peek(0x0, 0x0, &(0x7f0000000000))`, `ptrace$peek(0xffffffffffffffff, 0x0, &(0x7f0000000000))`, }, { - `ptrace(0x1)`, + `ptrace(0x1, 0x0)`, `ptrace(0x1, 0x0)`, }, { diff --git a/sys/test/test/align0 b/sys/test/test/align0 index ae83c3c84..e2e5b9275 100644 --- a/sys/test/test/align0 +++ b/sys/test/test/align0 @@ -1,3 +1,3 @@ syz_compare(&(0x7f0000000000)="010000000200000003000400000000000500000000000000", 0x18, &(0x7f0000001000)=@align0={0x1, 0x2, 0x3, 0x4, 0x5}, 0x18) -syz_compare(&(0x7f0000000000)="", 0x18, &(0x7f0000001000)=@align0={}, 0x17) # EBADF -syz_compare(&(0x7f0000000000)="", 0x18, &(0x7f0000001000)=@align0={0x1}, 0x18) # EINVAL +syz_compare(&(0x7f0000000000)="", 0x18, &(0x7f0000001000)=@align0={0x0, 0x0, 0x0, 0x0, 0x0}, 0x17) # EBADF +syz_compare(&(0x7f0000000000)="", 0x18, &(0x7f0000001000)=@align0={0x1, 0x0, 0x0, 0x0, 0x0}, 0x18) # EINVAL diff --git a/sys/test/test/test b/sys/test/test/test index 1803a6108..8715e198d 100644 --- a/sys/test/test/test +++ b/sys/test/test/test @@ -1 +1 @@ -syz_mmap() # EINVAL +syz_mmap(0x0, 0x0) # EINVAL diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index d009d6ff3..890ba64d3 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -327,7 +327,7 @@ func (fuzzer *Fuzzer) sendInputToManager(inp rpctype.RPCInput) { } func (fuzzer *Fuzzer) addInputFromAnotherFuzzer(inp rpctype.RPCInput) { - p, err := fuzzer.target.Deserialize(inp.Prog, prog.Strict) + p, err := fuzzer.target.Deserialize(inp.Prog, prog.NonStrict) if err != nil { log.Fatalf("failed to deserialize prog from another fuzzer: %v", err) } diff --git a/syz-fuzzer/testing.go b/syz-fuzzer/testing.go index 416468499..15ac9c6ab 100644 --- a/syz-fuzzer/testing.go +++ b/syz-fuzzer/testing.go @@ -90,7 +90,7 @@ func convertTestReq(target *prog.Target, req *rpctype.RunTestPollRes) *runtest.R test.Bin = bin } if len(req.Prog) != 0 { - p, err := target.Deserialize(req.Prog, prog.Strict) + p, err := target.Deserialize(req.Prog, prog.NonStrict) if err != nil { test.Err = err return test diff --git a/syz-manager/html.go b/syz-manager/html.go index 9856a0024..b572d59a9 100644 --- a/syz-manager/html.go +++ b/syz-manager/html.go @@ -188,7 +188,7 @@ func (mgr *Manager) httpCorpus(w http.ResponseWriter, r *http.Request) { if data.Call != "" && data.Call != inp.Call { continue } - p, err := mgr.target.Deserialize(inp.Prog, prog.Strict) + p, err := mgr.target.Deserialize(inp.Prog, prog.NonStrict) if err != nil { http.Error(w, fmt.Sprintf("failed to deserialize program: %v", err), http.StatusInternalServerError) return @@ -303,7 +303,7 @@ func (mgr *Manager) httpPrio(w http.ResponseWriter, r *http.Request) { var corpus []*prog.Prog for _, inp := range mgr.corpus { - p, err := mgr.target.Deserialize(inp.Prog, prog.Strict) + p, err := mgr.target.Deserialize(inp.Prog, prog.NonStrict) if err != nil { http.Error(w, fmt.Sprintf("failed to deserialize program: %v", err), http.StatusInternalServerError) return diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 325533ed8..93e518611 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -988,7 +988,7 @@ func (mgr *Manager) NewInput(a *rpctype.NewInputArgs, r *int) error { log.Fatalf("fuzzer %v is not connected", a.Name) } - if _, err := mgr.target.Deserialize(a.RPCInput.Prog, prog.Strict); err != nil { + if _, err := mgr.target.Deserialize(a.RPCInput.Prog, prog.NonStrict); err != nil { // This should not happen, but we see such cases episodically, reason unknown. log.Logf(0, "failed to deserialize program from fuzzer: %v\n%s", err, a.RPCInput.Prog) return nil |
