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.
This commit is contained in:
Dmitry Vyukov 2018-12-09 18:05:58 +01:00
parent 95fe19c19e
commit ba64d006de
11 changed files with 83 additions and 60 deletions

@ -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.

@ -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 {

@ -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

@ -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
}

@ -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)`,
},
{

@ -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

@ -1 +1 @@
syz_mmap() # EINVAL
syz_mmap(0x0, 0x0) # EINVAL

@ -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)
}

@ -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

@ -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

@ -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