mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-24 20:09:49 +00:00
14dae29c2a
We currently use -1 as default value for resources when the actual value is not available. -1 is good for fd's, but is not the right default value for pointers/keys/etc. Pass from prog and use in executor proper default value for resources.
240 lines
4.7 KiB
Go
240 lines
4.7 KiB
Go
// 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
|
|
Vars []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
|
|
PidStride uint64
|
|
BigEndian bool
|
|
}
|
|
|
|
type ExecArgResult struct {
|
|
Size uint64
|
|
Index uint64
|
|
DivOp uint64
|
|
AddOp uint64
|
|
Default 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
|
|
}
|
|
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(),
|
|
})
|
|
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
|
|
}
|
|
}
|
|
case execInstrEOF:
|
|
dec.commitCall()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (dec *execDecoder) readArg() ExecArg {
|
|
switch typ := dec.read(); typ {
|
|
case execArgConst:
|
|
meta := dec.read()
|
|
return ExecArgConst{
|
|
Value: dec.read(),
|
|
Size: meta & 0xff,
|
|
BitfieldOffset: (meta >> 16) & 0xff,
|
|
BitfieldLength: (meta >> 24) & 0xff,
|
|
PidStride: meta >> 32,
|
|
BigEndian: (meta & (1 << 8)) != 0,
|
|
}
|
|
case execArgResult:
|
|
arg := ExecArgResult{
|
|
Size: dec.read(),
|
|
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:
|
|
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 {
|
|
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{}
|
|
}
|