mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-30 23:00:29 +00:00
6aad5879f7
Not tested, but const extraction and build works. Update #324 Update #191
300 lines
7.2 KiB
Go
300 lines
7.2 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.
|
|
|
|
//go:generate bash -c "echo -e 'package ifuzz\nvar Insns []*Insn' > insns.go"
|
|
//go:generate bash -c "go run gen/gen.go gen/all-enc-instructions.txt > /tmp/insns.go"
|
|
//go:generate mv /tmp/insns.go insns.go
|
|
//go:generate go fmt insns.go
|
|
|
|
// Package ifuzz allows to generate and mutate x86 machine code.
|
|
package ifuzz
|
|
|
|
import (
|
|
"math/rand"
|
|
)
|
|
|
|
const (
|
|
ModeLong64 = iota
|
|
ModeProt32
|
|
ModeProt16
|
|
ModeReal16
|
|
ModeLast
|
|
)
|
|
|
|
type Insn struct {
|
|
Name string
|
|
Extension string
|
|
|
|
Mode int // bitmask of compatible modes
|
|
Priv bool // CPL=0
|
|
Pseudo bool // pseudo instructions can consist of several real instructions
|
|
|
|
Opcode []byte
|
|
Prefix []byte
|
|
Suffix []byte
|
|
Modrm bool
|
|
Mod int8
|
|
Reg int8 // -6 - segment register, -8 - control register
|
|
Rm int8
|
|
Srm bool // register is embed in the first byte
|
|
NoSibDisp bool // no SIB/disp even if modrm says otherwise
|
|
Imm int8 // immediate size, -1 - immediate size, -2 - address size, -3 - operand size
|
|
Imm2 int8
|
|
NoRepPrefix bool
|
|
No66Prefix bool
|
|
Rexw int8 // 1 must be set, -1 must not be set
|
|
Mem32 bool // instruction always references 32-bit memory operand, 0x67 is illegal
|
|
Mem16 bool // instruction always references 16-bit memory operand
|
|
|
|
Vex byte
|
|
VexMap byte
|
|
VexL int8
|
|
VexNoR bool
|
|
VexP int8
|
|
Avx2Gather bool
|
|
|
|
generator func(cfg *Config, r *rand.Rand) []byte // for pseudo instructions
|
|
}
|
|
|
|
type Config struct {
|
|
Len int // number of instructions to generate
|
|
Mode int // one of ModeXXX
|
|
Priv bool // generate CPL=0 instructions
|
|
Exec bool // generate instructions sequences interesting for execution
|
|
MemRegions []MemRegion // generated instructions will reference these regions
|
|
}
|
|
|
|
type MemRegion struct {
|
|
Start uint64
|
|
Size uint64
|
|
}
|
|
|
|
const (
|
|
typeExec = iota
|
|
typePriv
|
|
typeUser
|
|
typeAll
|
|
typeLast
|
|
)
|
|
|
|
var modeInsns [ModeLast][typeLast][]*Insn
|
|
|
|
func init() {
|
|
initPseudo()
|
|
for mode := 0; mode < ModeLast; mode++ {
|
|
for _, insn := range Insns {
|
|
if insn.Mode&(1<<uint(mode)) == 0 {
|
|
continue
|
|
}
|
|
if insn.Pseudo {
|
|
modeInsns[mode][typeExec] = append(modeInsns[mode][typeExec], insn)
|
|
} else if insn.Priv {
|
|
modeInsns[mode][typePriv] = append(modeInsns[mode][typePriv], insn)
|
|
modeInsns[mode][typeAll] = append(modeInsns[mode][typeAll], insn)
|
|
} else {
|
|
modeInsns[mode][typeUser] = append(modeInsns[mode][typeUser], insn)
|
|
modeInsns[mode][typeAll] = append(modeInsns[mode][typeAll], insn)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ModeInsns returns list of all instructions for the given mode.
|
|
func ModeInsns(cfg *Config) []*Insn {
|
|
if cfg.Mode < 0 || cfg.Mode >= ModeLast {
|
|
panic("bad mode")
|
|
}
|
|
var insns []*Insn
|
|
insns = append(insns, modeInsns[cfg.Mode][typeUser]...)
|
|
if cfg.Priv {
|
|
insns = append(insns, modeInsns[cfg.Mode][typePriv]...)
|
|
if cfg.Exec {
|
|
insns = append(insns, modeInsns[cfg.Mode][typeExec]...)
|
|
}
|
|
}
|
|
return insns
|
|
}
|
|
|
|
func Generate(cfg *Config, r *rand.Rand) []byte {
|
|
var text []byte
|
|
for i := 0; i < cfg.Len; i++ {
|
|
insn := randInsn(cfg, r)
|
|
text = append(text, insn.Encode(cfg, r)...)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func Mutate(cfg *Config, r *rand.Rand, text []byte) []byte {
|
|
insns := split(cfg, text)
|
|
retry := false
|
|
for stop := false; !stop || retry || len(insns) == 0; stop = r.Intn(2) == 0 {
|
|
retry = false
|
|
switch x := r.Intn(100); {
|
|
case x < 10 && len(insns) != 0:
|
|
// delete instruction
|
|
i := r.Intn(len(insns))
|
|
copy(insns[i:], insns[i+1:])
|
|
insns = insns[:len(insns)-1]
|
|
case x < 40 && len(insns) != 0:
|
|
// replace instruction with another
|
|
insn := randInsn(cfg, r)
|
|
text1 := insn.Encode(cfg, r)
|
|
i := r.Intn(len(insns))
|
|
insns[i] = text1
|
|
case x < 70 && len(insns) != 0:
|
|
// mutate instruction
|
|
i := r.Intn(len(insns))
|
|
text1 := insns[i]
|
|
for stop := false; !stop || len(text1) == 0; stop = r.Intn(2) == 0 {
|
|
switch x := r.Intn(100); {
|
|
case x < 5 && len(text1) != 0:
|
|
// delete byte
|
|
i := r.Intn(len(text1))
|
|
copy(text1[i:], text1[i+1:])
|
|
text1 = text1[:len(text1)-1]
|
|
case x < 40 && len(text1) != 0:
|
|
// replace a byte
|
|
i := r.Intn(len(text1))
|
|
text1[i] = byte(r.Intn(256))
|
|
case x < 70 && len(text1) != 0:
|
|
// flip a bit
|
|
i := r.Intn(len(text1))
|
|
text1[i] ^= 1 << byte(r.Intn(8))
|
|
default:
|
|
// insert a byte
|
|
i := r.Intn(len(text1) + 1)
|
|
text1 = append(text1, 0)
|
|
copy(text1[i+1:], text1[i:])
|
|
text1[i] = byte(r.Intn(256))
|
|
}
|
|
}
|
|
insns[i] = text1
|
|
case len(insns) < cfg.Len:
|
|
// insert a new instruction
|
|
insn := randInsn(cfg, r)
|
|
text1 := insn.Encode(cfg, r)
|
|
i := r.Intn(len(insns) + 1)
|
|
insns = append(insns, nil)
|
|
copy(insns[i+1:], insns[i:])
|
|
insns[i] = text1
|
|
default:
|
|
retry = true
|
|
}
|
|
}
|
|
text = nil
|
|
for _, insn := range insns {
|
|
text = append(text, insn...)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func randInsn(cfg *Config, r *rand.Rand) *Insn {
|
|
var insns []*Insn
|
|
if cfg.Priv && cfg.Exec {
|
|
insns = modeInsns[cfg.Mode][r.Intn(3)]
|
|
} else if cfg.Priv {
|
|
insns = modeInsns[cfg.Mode][r.Intn(2)]
|
|
} else {
|
|
insns = modeInsns[cfg.Mode][typeUser]
|
|
}
|
|
return insns[r.Intn(len(insns))]
|
|
}
|
|
|
|
func split(cfg *Config, text []byte) [][]byte {
|
|
text = append([]byte{}, text...)
|
|
var insns [][]byte
|
|
var bad []byte
|
|
for len(text) != 0 {
|
|
n, err := Decode(cfg.Mode, text)
|
|
if err != nil || n == 0 {
|
|
bad = append(bad, text[0])
|
|
text = text[1:]
|
|
continue
|
|
}
|
|
if bad != nil {
|
|
insns = append(insns, bad)
|
|
bad = nil
|
|
}
|
|
insns = append(insns, text[:n])
|
|
text = text[n:]
|
|
}
|
|
if bad != nil {
|
|
insns = append(insns, bad)
|
|
}
|
|
return insns
|
|
}
|
|
|
|
func generateArg(cfg *Config, r *rand.Rand, size int) []byte {
|
|
v := generateInt(cfg, r, size)
|
|
arg := make([]byte, size)
|
|
for i := 0; i < size; i++ {
|
|
arg[i] = byte(v)
|
|
v >>= 8
|
|
}
|
|
return arg
|
|
}
|
|
|
|
func (insn *Insn) isCompatible(cfg *Config) bool {
|
|
if cfg.Mode < 0 || cfg.Mode >= ModeLast {
|
|
panic("bad mode")
|
|
}
|
|
if insn.Priv && !cfg.Priv {
|
|
return false
|
|
}
|
|
if insn.Pseudo && !cfg.Exec {
|
|
return false
|
|
}
|
|
if insn.Mode&(1<<uint(cfg.Mode)) == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func generateInt(cfg *Config, r *rand.Rand, size int) uint64 {
|
|
if size != 1 && size != 2 && size != 4 && size != 8 {
|
|
panic("bad arg size")
|
|
}
|
|
var v uint64
|
|
switch x := r.Intn(60); {
|
|
case x < 10:
|
|
v = uint64(r.Intn(1 << 4))
|
|
case x < 20:
|
|
v = uint64(r.Intn(1 << 16))
|
|
case x < 25:
|
|
v = uint64(r.Int63()) % (1 << 32)
|
|
case x < 30:
|
|
v = uint64(r.Int63())
|
|
case x < 40:
|
|
v = specialNumbers[r.Intn(len(specialNumbers))]
|
|
if r.Intn(5) == 0 {
|
|
v += uint64(r.Intn(33)) - 16
|
|
}
|
|
case x < 50 && len(cfg.MemRegions) != 0:
|
|
mem := cfg.MemRegions[r.Intn(len(cfg.MemRegions))]
|
|
switch x := r.Intn(100); {
|
|
case x < 25:
|
|
v = mem.Start
|
|
case x < 50:
|
|
v = mem.Start + mem.Size
|
|
case x < 75:
|
|
v = mem.Start + mem.Size/2
|
|
default:
|
|
v = mem.Start + uint64(r.Int63())%mem.Size
|
|
}
|
|
if r.Intn(10) == 0 {
|
|
v += uint64(r.Intn(33)) - 16
|
|
}
|
|
default:
|
|
v = uint64(r.Intn(1 << 8))
|
|
}
|
|
if r.Intn(50) == 0 {
|
|
v = uint64(-int64(v))
|
|
}
|
|
if r.Intn(50) == 0 && size != 1 {
|
|
v &^= 1<<12 - 1
|
|
}
|
|
return v
|
|
}
|
|
|
|
var specialNumbers = []uint64{0, 1 << 15, 1 << 16, 1 << 31, 1 << 32, 1 << 47, 1 << 47, 1 << 63}
|