From fea5478f4686624974d34edd8b4268d7a6af9584 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 15 Dec 2017 13:42:28 +0100 Subject: 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. --- prog/decodeexec.go | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 prog/decodeexec.go (limited to 'prog/decodeexec.go') 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{} + } +} -- cgit mrf-deployment