aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/ifuzz/ifuzz.go
diff options
context:
space:
mode:
authorAlexey Kardashevskiy <aik@linux.ibm.com>2020-09-02 18:11:22 +1000
committerDmitry Vyukov <dvyukov@google.com>2020-11-20 15:31:42 +0100
commite72f8f11e096d36aefc41a35c718dced97c45dea (patch)
tree6619d0089d8ac172c64853c76c0b1acc9485d192 /pkg/ifuzz/ifuzz.go
parent740ff4615a9ced4a8a016365aa44674b9b0e807d (diff)
pkg/ifuzz: reorganize files to allow other architectures
At the moment ifuzz only generates x86 instructions. In order to support instruction fuzzing for others (ARM, POWERPC), some separation of the common and arch layers is needed. This adds 2 packages: 1. "x86" where x86 instruction generator goes to 2. "ifuzzimpl which contains some common code. The goal was to keep changes to the rand.go to the minimum. The next patch will use this when adding PPC64. This should cause no behavioural change. Signed-off-by: Alexey Kardashevskiy <aik@linux.ibm.com>
Diffstat (limited to 'pkg/ifuzz/ifuzz.go')
-rw-r--r--pkg/ifuzz/ifuzz.go293
1 files changed, 22 insertions, 271 deletions
diff --git a/pkg/ifuzz/ifuzz.go b/pkg/ifuzz/ifuzz.go
index 5d6269926..1939f440e 100644
--- a/pkg/ifuzz/ifuzz.go
+++ b/pkg/ifuzz/ifuzz.go
@@ -1,14 +1,10 @@
// 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 "go run gen/gen.go gen/all-enc-instructions.txt > generated/insns.go"
-
-// Package ifuzz allows to generate and mutate x86 machine code.
package ifuzz
import (
"math/rand"
- "sync"
)
const (
@@ -19,45 +15,11 @@ const (
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 {
+ Arch string
Len int // number of instructions to generate
Mode int // one of ModeXXX
- Priv bool // generate CPL=0 instructions
+ Priv bool // generate CPL=0 instructions (x86), HV/!PR mode (PPC)
Exec bool // generate instructions sequences interesting for execution
MemRegions []MemRegion // generated instructions will reference these regions
}
@@ -68,241 +30,30 @@ type MemRegion struct {
}
const (
- typeExec = iota
- typePriv
- typeUser
- typeAll
- typeLast
+ TypeExec = iota
+ TypePriv
+ TypeUser
+ TypeAll
+ TypeLast
)
-var modeInsns [ModeLast][typeLast][]*Insn
-
-var (
- Insns []*Insn
- initOnce sync.Once
-)
-
-func initInsns() {
- if len(Insns) == 0 {
- panic("no instructions")
- }
- 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)
- }
- }
- }
+type Insn interface {
+ GetName() string
+ GetMode() int
+ GetPseudo() bool
+ GetPriv() bool
+ IsCompatible(cfg *Config) bool
+ Encode(cfg *Config, r *rand.Rand) []byte
}
-// ModeInsns returns list of all instructions for the given mode.
-func ModeInsns(cfg *Config) []*Insn {
- initOnce.Do(initInsns)
- 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
+type InsnSet interface {
+ GetInsns(mode, insntype int) []Insn
+ Decode(mode int, text []byte) (int, error)
+ DecodeExt(mode int, text []byte) (int, error) // XED, to keep ifuzz_test happy
}
-func Generate(cfg *Config, r *rand.Rand) []byte {
- initOnce.Do(initInsns)
- 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 {
- initOnce.Do(initInsns)
- 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.
- pos := r.Intn(len(text1))
- copy(text1[pos:], text1[pos+1:])
- text1 = text1[:len(text1)-1]
- case x < 40 && len(text1) != 0:
- // Replace a byte.
- pos := r.Intn(len(text1))
- text1[pos] = byte(r.Intn(256))
- case x < 70 && len(text1) != 0:
- // Flip a bit.
- pos := r.Intn(len(text1))
- text1[pos] ^= 1 << byte(r.Intn(8))
- default:
- // Insert a byte.
- pos := r.Intn(len(text1) + 1)
- text1 = append(text1, 0)
- copy(text1[pos+1:], text1[pos:])
- text1[pos] = 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
-}
+const (
+ ArchX86 = "x86"
+)
-var specialNumbers = []uint64{0, 1 << 15, 1 << 16, 1 << 31, 1 << 32, 1 << 47, 1 << 47, 1 << 63}
+var SpecialNumbers = [...]uint64{0, 1 << 15, 1 << 16, 1 << 31, 1 << 32, 1 << 47, 1 << 47, 1 << 63}