| // 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" |
| "reflect" |
| ) |
| |
| type ExecProg struct { |
| Calls []ExecCall |
| Vars []uint64 |
| } |
| |
| type ExecCall struct { |
| Meta *Syscall |
| Props CallProps |
| 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 |
| Format BinaryFormat |
| Value uint64 |
| BitfieldOffset uint64 |
| BitfieldLength uint64 |
| PidStride uint64 |
| } |
| |
| type ExecArgResult struct { |
| Size uint64 |
| Format BinaryFormat |
| Index uint64 |
| DivOp uint64 |
| AddOp uint64 |
| Default uint64 |
| } |
| |
| type ExecArgData struct { |
| Data []byte |
| Readable bool |
| } |
| |
| 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 |
| } |
| if uint64(len(dec.vars)) != dec.numVars { |
| return ExecProg{}, fmt.Errorf("mismatching number of vars: %v/%v", |
| len(dec.vars), dec.numVars) |
| } |
| p := ExecProg{ |
| Calls: dec.calls, |
| Vars: dec.vars, |
| } |
| return p, nil |
| } |
| |
| type execDecoder struct { |
| target *Target |
| data []byte |
| err error |
| numVars uint64 |
| vars []uint64 |
| call ExecCall |
| calls []ExecCall |
| } |
| |
| func (dec *execDecoder) parse() { |
| for dec.err == nil { |
| 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.read(), |
| Addr: dec.read(), |
| Size: dec.read(), |
| }) |
| case execInstrEOF: |
| dec.commitCall() |
| return |
| case execInstrSetProps: |
| dec.commitCall() |
| dec.readCallProps(&dec.call.Props) |
| 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.read() |
| 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 |
| } |
| } |
| } |
| } |
| } |
| |
| func (dec *execDecoder) readCallProps(props *CallProps) { |
| props.ForeachProp(func(_, _ string, value reflect.Value) { |
| arg := dec.read() |
| switch kind := value.Kind(); kind { |
| case reflect.Int: |
| value.SetInt(int64(arg)) |
| case reflect.Bool: |
| if arg == 1 { |
| value.SetBool(true) |
| } |
| default: |
| panic("Unsupported (yet) kind: " + kind.String()) |
| } |
| }) |
| } |
| |
| func (dec *execDecoder) readArg() ExecArg { |
| switch typ := dec.read(); typ { |
| case execArgConst: |
| meta := dec.read() |
| return ExecArgConst{ |
| Value: dec.read(), |
| Size: meta & 0xff, |
| Format: BinaryFormat((meta >> 8) & 0xff), |
| BitfieldOffset: (meta >> 16) & 0xff, |
| BitfieldLength: (meta >> 24) & 0xff, |
| PidStride: meta >> 32, |
| } |
| case execArgResult: |
| meta := dec.read() |
| arg := ExecArgResult{ |
| Size: meta & 0xff, |
| Format: BinaryFormat((meta >> 8) & 0xff), |
| Index: dec.read(), |
| DivOp: dec.read(), |
| AddOp: dec.read(), |
| Default: dec.read(), |
| } |
| for uint64(len(dec.vars)) <= arg.Index { |
| dec.vars = append(dec.vars, 0) |
| } |
| dec.vars[arg.Index] = arg.Default |
| return arg |
| case execArgData: |
| flags := dec.read() |
| size := flags & ^execArgDataReadable |
| readable := flags&execArgDataReadable != 0 |
| return ExecArgData{ |
| Data: dec.readBlob(size), |
| Readable: readable, |
| } |
| 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 |
| } |
| v := HostEndian.Uint64(dec.data) |
| 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 { |
| return |
| } |
| if dec.call.Index != ExecNoCopyout && dec.numVars < dec.call.Index+1 { |
| dec.numVars = dec.call.Index + 1 |
| } |
| for _, copyout := range dec.call.Copyout { |
| if dec.numVars < copyout.Index+1 { |
| dec.numVars = copyout.Index + 1 |
| } |
| } |
| dec.calls = append(dec.calls, dec.call) |
| dec.call = ExecCall{} |
| } |