// 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{} }