mirror of
https://github.com/reactos/syzkaller.git
synced 2025-03-02 16:45:38 +00:00
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:
parent
95fe19c19e
commit
ba64d006de
prog
sys
syz-fuzzer
syz-manager
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user