diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-12-15 13:42:28 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-12-17 11:39:14 +0100 |
| commit | fea5478f4686624974d34edd8b4268d7a6af9584 (patch) | |
| tree | 08acde56db25b1a666c586401c985f3d1e4eb98e /prog/decodeexec.go | |
| parent | 431d3c90b1a4a31a6156ad1cd5fc692af3b1a314 (diff) | |
prog: add DeserializeExec
Factor out program parsing from pkg/csource.
csource code that parses program and at the same time
formats output is very messy and complex.
New aproach also allows to understand e.g.
when a call has copyout instructions which is
useful for better C source output.
Diffstat (limited to 'prog/decodeexec.go')
| -rw-r--r-- | prog/decodeexec.go | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/prog/decodeexec.go b/prog/decodeexec.go new file mode 100644 index 000000000..696de461e --- /dev/null +++ b/prog/decodeexec.go @@ -0,0 +1,213 @@ +// 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 prog + +import ( + "fmt" +) + +type ExecProg struct { + Calls []ExecCall + NumVars uint64 +} + +type ExecCall struct { + Meta *Syscall + Index uint64 + Args []ExecArg + Copyin []ExecCopyin + Copyout []ExecCopyout +} + +type ExecCopyin struct { + Addr uint64 + Arg ExecArg +} + +type ExecCopyout struct { + Index uint64 + Addr uint64 + Size uint64 +} + +type ExecArg interface{} // one of ExecArg* + +type ExecArgConst struct { + Size uint64 + Value uint64 + BitfieldOffset uint64 + BitfieldLength uint64 +} + +type ExecArgResult struct { + Size uint64 + Index uint64 + DivOp uint64 + AddOp uint64 +} + +type ExecArgData struct { + Data []byte +} + +type ExecArgCsum struct { + Size uint64 + Kind uint64 + Chunks []ExecCsumChunk +} + +type ExecCsumChunk struct { + Kind uint64 + Value uint64 + Size uint64 +} + +func (target *Target) DeserializeExec(exec []byte) (ExecProg, error) { + dec := &execDecoder{target: target, data: exec} + dec.parse() + if dec.err != nil { + return ExecProg{}, dec.err + } + p := ExecProg{ + Calls: dec.calls, + NumVars: dec.numInstr, + } + return p, nil +} + +type execDecoder struct { + target *Target + data []byte + err error + numInstr uint64 + call ExecCall + calls []ExecCall +} + +func (dec *execDecoder) parse() { + for ; dec.err == nil; dec.numInstr++ { + switch instr := dec.read(); instr { + case execInstrCopyin: + dec.commitCall() + dec.call.Copyin = append(dec.call.Copyin, ExecCopyin{ + Addr: dec.read(), + Arg: dec.readArg(), + }) + case execInstrCopyout: + dec.call.Copyout = append(dec.call.Copyout, ExecCopyout{ + Index: dec.numInstr, + Addr: dec.read(), + Size: dec.read(), + }) + default: + dec.commitCall() + if instr >= uint64(len(dec.target.Syscalls)) { + dec.setErr(fmt.Errorf("bad syscall %v", instr)) + return + } + dec.call.Meta = dec.target.Syscalls[instr] + dec.call.Index = dec.numInstr + for i := dec.read(); i > 0; i-- { + switch arg := dec.readArg(); arg.(type) { + case ExecArgConst, ExecArgResult: + dec.call.Args = append(dec.call.Args, arg) + default: + dec.setErr(fmt.Errorf("bad call arg %+v", arg)) + return + } + } + case execInstrEOF: + dec.commitCall() + return + } + } +} + +func (dec *execDecoder) readArg() ExecArg { + switch typ := dec.read(); typ { + case execArgConst: + return ExecArgConst{ + Size: dec.read(), + Value: dec.read(), + BitfieldOffset: dec.read(), + BitfieldLength: dec.read(), + } + case execArgResult: + return ExecArgResult{ + Size: dec.read(), + Index: dec.read(), + DivOp: dec.read(), + AddOp: dec.read(), + } + case execArgData: + return ExecArgData{ + Data: dec.readBlob(dec.read()), + } + case execArgCsum: + size := dec.read() + switch kind := dec.read(); kind { + case ExecArgCsumInet: + chunks := make([]ExecCsumChunk, dec.read()) + for i := range chunks { + chunks[i] = ExecCsumChunk{ + Kind: dec.read(), + Value: dec.read(), + Size: dec.read(), + } + } + return ExecArgCsum{ + Size: size, + Kind: kind, + Chunks: chunks, + } + default: + dec.setErr(fmt.Errorf("unknown csum kind %v", kind)) + return nil + } + default: + dec.setErr(fmt.Errorf("bad argument type %v", typ)) + return nil + } +} + +func (dec *execDecoder) read() uint64 { + if len(dec.data) < 8 { + dec.setErr(fmt.Errorf("exec program overflow")) + } + if dec.err != nil { + return 0 + } + var v uint64 + for i := 0; i < 8; i++ { + v |= uint64(dec.data[i]) << uint(i*8) + } + dec.data = dec.data[8:] + return v +} + +func (dec *execDecoder) readBlob(size uint64) []byte { + padded := (size + 7) / 8 * 8 + if uint64(len(dec.data)) < padded { + dec.setErr(fmt.Errorf("exec program overflow")) + } + if dec.err != nil { + return nil + } + data := dec.data[:size] + dec.data = dec.data[padded:] + return data +} + +func (dec *execDecoder) setErr(err error) { + if dec.err == nil { + dec.err = err + } +} + +func (dec *execDecoder) commitCall() { + if dec.call.Meta != nil { + dec.calls = append(dec.calls, dec.call) + dec.call = ExecCall{} + } +} |
