aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--pkg/ifuzz/ifuzz.go293
-rw-r--r--pkg/ifuzz/ifuzz_test.go54
-rw-r--r--pkg/ifuzz/ifuzzimpl/ifuzzimpl.go145
-rw-r--r--pkg/ifuzz/x86/decode.go (renamed from pkg/ifuzz/decode.go)28
-rw-r--r--pkg/ifuzz/x86/encode.go (renamed from pkg/ifuzz/encode.go)21
-rw-r--r--pkg/ifuzz/x86/gen/all-enc-instructions.txt (renamed from pkg/ifuzz/gen/all-enc-instructions.txt)0
-rw-r--r--pkg/ifuzz/x86/gen/gen.go (renamed from pkg/ifuzz/gen/gen.go)40
-rw-r--r--pkg/ifuzz/x86/generated/empty.go (renamed from pkg/ifuzz/generated/empty.go)0
-rw-r--r--pkg/ifuzz/x86/generated/insns.go (renamed from pkg/ifuzz/generated/insns.go)8
-rw-r--r--pkg/ifuzz/x86/pseudo.go (renamed from pkg/ifuzz/pseudo.go)113
-rw-r--r--pkg/ifuzz/x86/x86.go186
-rw-r--r--pkg/ifuzz/x86/xed.go (renamed from pkg/ifuzz/xed.go)2
-rw-r--r--prog/rand.go17
13 files changed, 520 insertions, 387 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}
diff --git a/pkg/ifuzz/ifuzz_test.go b/pkg/ifuzz/ifuzz_test.go
index 90ca2a440..a5052e581 100644
--- a/pkg/ifuzz/ifuzz_test.go
+++ b/pkg/ifuzz/ifuzz_test.go
@@ -10,21 +10,23 @@ import (
"testing"
"time"
- . "github.com/google/syzkaller/pkg/ifuzz"
- _ "github.com/google/syzkaller/pkg/ifuzz/generated"
+ "github.com/google/syzkaller/pkg/ifuzz"
+ "github.com/google/syzkaller/pkg/ifuzz/ifuzzimpl"
+ _ "github.com/google/syzkaller/pkg/ifuzz/x86/generated"
)
-func TestMode(t *testing.T) {
- all := make(map[*Insn]bool)
- for mode := 0; mode < ModeLast; mode++ {
+func testmodearch(t *testing.T, arch string) {
+ all := make(map[ifuzz.Insn]bool)
+ for mode := 0; mode < ifuzz.ModeLast; mode++ {
for priv := 0; priv < 2; priv++ {
for exec := 0; exec < 2; exec++ {
- cfg := &Config{
+ cfg := &ifuzz.Config{
+ Arch: arch,
Mode: mode,
Priv: priv != 0,
Exec: exec != 0,
}
- insns := ModeInsns(cfg)
+ insns := ifuzzimpl.ModeInsns(cfg)
t.Logf("mode=%v priv=%v exec=%v: %v instructions", mode, priv, exec, len(insns))
for _, insn := range insns {
all[insn] = true
@@ -35,7 +37,16 @@ func TestMode(t *testing.T) {
t.Logf("total: %v instructions", len(all))
}
-func TestDecode(t *testing.T) {
+func TestMode(t *testing.T) {
+ testmodearch(t, ifuzz.ArchX86)
+}
+
+func testdecodearch(t *testing.T, arch string) {
+ insnset := ifuzzimpl.Types[arch]
+ xedEnabled := false
+ if _, err := insnset.DecodeExt(0, nil); err == nil {
+ xedEnabled = true
+ }
seed := time.Now().UnixNano()
if os.Getenv("CI") != "" {
seed = 0 // required for deterministic coverage reports
@@ -44,30 +55,31 @@ func TestDecode(t *testing.T) {
r := rand.New(rand.NewSource(seed))
for repeat := 0; repeat < 10; repeat++ {
- for mode := 0; mode < ModeLast; mode++ {
- cfg := &Config{
+ for mode := 0; mode < ifuzz.ModeLast; mode++ {
+ cfg := &ifuzz.Config{
+ Arch: arch,
Mode: mode,
Priv: true,
Exec: true,
}
failed := false
- for _, insn := range ModeInsns(cfg) {
+ for _, insn := range ifuzzimpl.ModeInsns(cfg) {
text0 := insn.Encode(cfg, r)
text := text0
repeat:
- size, err := Decode(mode, text)
+ size, err := insnset.Decode(mode, text)
if err != nil {
- t.Errorf("decoding %v %v failed (mode=%v): %v", insn.Name, hex.EncodeToString(text), mode, err)
+ t.Errorf("decoding %v %v failed (mode=%v): %v", insn.GetName(), hex.EncodeToString(text), mode, err)
if len(text) != len(text0) {
t.Errorf("whole: %v", hex.EncodeToString(text0))
}
failed = true
continue
}
- if XedDecode != nil {
- xedSize, xedErr := XedDecode(mode, text)
+ if xedEnabled {
+ xedSize, xedErr := insnset.DecodeExt(mode, text)
if xedErr != nil {
- t.Errorf("xed decoding %v %v failed (mode=%v): %v", insn.Name, hex.EncodeToString(text), mode, xedErr)
+ t.Errorf("xed decoding %v %v failed (mode=%v): %v", insn.GetName(), hex.EncodeToString(text), mode, xedErr)
if len(text) != len(text0) {
t.Errorf("whole: %v", hex.EncodeToString(text0))
}
@@ -76,7 +88,7 @@ func TestDecode(t *testing.T) {
}
if size != xedSize {
t.Errorf("decoding %v %v failed (mode=%v): decoded %v/%v, xed decoded %v/%v",
- insn.Name, hex.EncodeToString(text), mode, size, xedSize, size, len(text))
+ insn.GetName(), hex.EncodeToString(text), mode, size, xedSize, size, len(text))
if len(text) != len(text0) {
t.Errorf("whole: %v", hex.EncodeToString(text0))
}
@@ -84,13 +96,13 @@ func TestDecode(t *testing.T) {
continue
}
}
- if insn.Pseudo && size >= 0 && size < len(text) {
+ if insn.GetPseudo() && size >= 0 && size < len(text) {
text = text[size:]
goto repeat
}
if size != len(text) {
t.Errorf("decoding %v %v failed (mode=%v): decoded %v/%v",
- insn.Name, hex.EncodeToString(text), mode, size, len(text))
+ insn.GetName(), hex.EncodeToString(text), mode, size, len(text))
if len(text) != len(text0) {
t.Errorf("whole: %v", hex.EncodeToString(text0))
}
@@ -103,3 +115,7 @@ func TestDecode(t *testing.T) {
}
}
}
+
+func TestDecode(t *testing.T) {
+ testdecodearch(t, ifuzz.ArchX86)
+}
diff --git a/pkg/ifuzz/ifuzzimpl/ifuzzimpl.go b/pkg/ifuzz/ifuzzimpl/ifuzzimpl.go
new file mode 100644
index 000000000..f1ea64f37
--- /dev/null
+++ b/pkg/ifuzz/ifuzzimpl/ifuzzimpl.go
@@ -0,0 +1,145 @@
+// 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 ifuzzimpl
+
+import (
+ "github.com/google/syzkaller/pkg/ifuzz"
+ "math/rand"
+)
+
+var (
+ Types = make(map[string]ifuzz.InsnSet)
+)
+
+func Register(arch string, insns ifuzz.InsnSet) {
+ Types[arch] = insns
+}
+
+// ModeInsns returns list of all instructions for the given mode.
+func ModeInsns(cfg *ifuzz.Config) []ifuzz.Insn {
+ insnset := Types[cfg.Arch]
+ if cfg.Mode < 0 || cfg.Mode >= ifuzz.ModeLast {
+ panic("bad mode")
+ }
+ var insns []ifuzz.Insn
+ insns = append(insns, insnset.GetInsns(cfg.Mode, ifuzz.TypeUser)...)
+ if cfg.Priv {
+ insns = append(insns, insnset.GetInsns(cfg.Mode, ifuzz.TypePriv)...)
+ if cfg.Exec {
+ insns = append(insns, insnset.GetInsns(cfg.Mode, ifuzz.TypeExec)...)
+ }
+ }
+ return insns
+}
+
+func Generate(cfg *ifuzz.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 *ifuzz.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.
+ 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 *ifuzz.Config, r *rand.Rand) ifuzz.Insn {
+ insnset := Types[cfg.Arch]
+ var insns []ifuzz.Insn
+ if cfg.Priv && cfg.Exec {
+ insns = insnset.GetInsns(cfg.Mode, r.Intn(3))
+ } else if cfg.Priv {
+ insns = insnset.GetInsns(cfg.Mode, r.Intn(2))
+ } else {
+ insns = insnset.GetInsns(cfg.Mode, ifuzz.TypeUser)
+ }
+ return insns[r.Intn(len(insns))]
+}
+
+func split(cfg *ifuzz.Config, text []byte) [][]byte {
+ insnset := Types[cfg.Arch]
+ text = append([]byte{}, text...)
+ var insns [][]byte
+ var bad []byte
+ for len(text) != 0 {
+ n, err := insnset.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
+}
diff --git a/pkg/ifuzz/decode.go b/pkg/ifuzz/x86/decode.go
index 7fecf312c..ca611ac69 100644
--- a/pkg/ifuzz/decode.go
+++ b/pkg/ifuzz/x86/decode.go
@@ -1,29 +1,30 @@
// 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 ifuzz
+package x86
import (
"fmt"
+ "github.com/google/syzkaller/pkg/ifuzz"
)
// Decode decodes instruction length for the given mode.
// It can have falsely decode incorrect instructions,
// but should not fail to decode correct instructions.
// nolint: gocyclo, nestif, gocognit, funlen
-func Decode(mode int, text []byte) (int, error) {
+func (insnset *InsnSetX86) Decode(mode int, text []byte) (int, error) {
if len(text) == 0 {
return 0, fmt.Errorf("zero-length instruction")
}
prefixes := prefixes32
var operSize, immSize, dispSize, addrSize int
switch mode {
- case ModeLong64:
+ case ifuzz.ModeLong64:
operSize, immSize, dispSize, addrSize = 4, 4, 4, 8
prefixes = prefixes64
- case ModeProt32:
+ case ifuzz.ModeProt32:
operSize, immSize, dispSize, addrSize = 4, 4, 4, 4
- case ModeProt16, ModeReal16:
+ case ifuzz.ModeProt16, ifuzz.ModeReal16:
operSize, immSize, dispSize, addrSize = 2, 2, 2, 2
default:
panic("bad mode")
@@ -34,7 +35,7 @@ func Decode(mode int, text []byte) (int, error) {
if len(text) > 1 {
// There are only 2 32-bit instructions that look like VEX-prefixed but are actually not: LDS, LES.
// They always reference memory (mod!=3), but all VEX instructions have "mod=3" where LDS/LES would have mod.
- if (text[0] == 0xc4 || text[0] == 0xc5) && (mode == ModeLong64 || text[1]&0xc0 == 0xc0) {
+ if (text[0] == 0xc4 || text[0] == 0xc5) && (mode == ifuzz.ModeLong64 || text[1]&0xc0 == 0xc0) {
vex = true
}
// There is only one instruction that looks like XOP-prefixed but is actually not: POP.
@@ -95,7 +96,10 @@ func Decode(mode int, text []byte) (int, error) {
}
}
nextInsn:
- for _, insn := range modeInsns[mode][typeAll] {
+ for _, insn := range insnset.Insns {
+ if (insn.Mode & (1 << mode)) == 0 {
+ continue nextInsn
+ }
if vex != (insn.Vex != 0) {
continue nextInsn
}
@@ -220,3 +224,13 @@ var (
0x4e: true, 0x4f: true,
}
)
+
+func (insnset *InsnSetX86) DecodeExt(mode int, text []byte) (int, error) {
+ if XedDecode != nil && text != nil && len(text) > 0 {
+ return XedDecode(mode, text)
+ }
+ if XedDecode == nil {
+ return 0, fmt.Errorf("no XED")
+ }
+ return 0, nil // tells the caller XED is enabled
+}
diff --git a/pkg/ifuzz/encode.go b/pkg/ifuzz/x86/encode.go
index 5d27a29fd..b73a22c14 100644
--- a/pkg/ifuzz/encode.go
+++ b/pkg/ifuzz/x86/encode.go
@@ -5,15 +5,16 @@
// and AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions
// for details of instruction encoding.
-package ifuzz
+package x86
import (
+ "github.com/google/syzkaller/pkg/ifuzz"
"math/rand"
)
// nolint: gocyclo, nestif, gocognit, funlen
-func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
- if !insn.isCompatible(cfg) {
+func (insn *Insn) Encode(cfg *ifuzz.Config, r *rand.Rand) []byte {
+ if !insn.IsCompatible(cfg) {
panic("instruction is not suitable for this mode")
}
if insn.Pseudo {
@@ -22,11 +23,11 @@ func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
var operSize, immSize, dispSize, addrSize int
switch cfg.Mode {
- case ModeLong64:
+ case ifuzz.ModeLong64:
operSize, immSize, dispSize, addrSize = 4, 4, 4, 8
- case ModeProt32:
+ case ifuzz.ModeProt32:
operSize, immSize, dispSize, addrSize = 4, 4, 4, 4
- case ModeProt16, ModeReal16:
+ case ifuzz.ModeProt16, ifuzz.ModeReal16:
operSize, immSize, dispSize, addrSize = 2, 2, 2, 2
default:
panic("bad mode")
@@ -52,7 +53,7 @@ func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
if !insn.No66Prefix {
prefixes = append(prefixes, 0x66) // operand size
}
- if cfg.Mode == ModeLong64 || !insn.Mem32 {
+ if cfg.Mode == ifuzz.ModeLong64 || !insn.Mem32 {
prefixes = append(prefixes, 0x67) // address size
}
if !insn.NoRepPrefix {
@@ -69,7 +70,7 @@ func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
// REX
var rex byte
- if cfg.Mode == ModeLong64 && r.Intn(2) == 0 {
+ if cfg.Mode == ifuzz.ModeLong64 && r.Intn(2) == 0 {
// bit 0 - B
// bit 1 - X
// bit 2 - R
@@ -117,7 +118,7 @@ func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
code = append(code, insn.Vex)
vexR = byte(1)
vexX = byte(1)
- if cfg.Mode == ModeLong64 {
+ if cfg.Mode == ifuzz.ModeLong64 {
vexR = byte(r.Intn(2))
vexX = byte(r.Intn(2))
}
@@ -145,7 +146,7 @@ func (insn *Insn) Encode(cfg *Config, r *rand.Rand) []byte {
code = append(code, vexR<<7|vexX<<6|vexB<<5|insn.VexMap)
code = append(code, W<<7|vvvv<<3|L<<2|pp)
// TODO: short encoding
- if cfg.Mode != ModeLong64 {
+ if cfg.Mode != ifuzz.ModeLong64 {
vvvv |= 8
}
}
diff --git a/pkg/ifuzz/gen/all-enc-instructions.txt b/pkg/ifuzz/x86/gen/all-enc-instructions.txt
index 717ac8ec2..717ac8ec2 100644
--- a/pkg/ifuzz/gen/all-enc-instructions.txt
+++ b/pkg/ifuzz/x86/gen/all-enc-instructions.txt
diff --git a/pkg/ifuzz/gen/gen.go b/pkg/ifuzz/x86/gen/gen.go
index 654a3cf10..d1a490625 100644
--- a/pkg/ifuzz/gen/gen.go
+++ b/pkg/ifuzz/x86/gen/gen.go
@@ -1,7 +1,7 @@
// 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.
-// gen generates instruction tables (ifuzz/insns.go) from Intel XED tables.
+// gen generates instruction tables (ifuzz_types/insns.go) from Intel XED tables.
// Tables used to generate insns.go are checked in in all-enc-instructions.txt.
package main
@@ -14,6 +14,7 @@ import (
"strings"
"github.com/google/syzkaller/pkg/ifuzz"
+ "github.com/google/syzkaller/pkg/ifuzz/x86"
"github.com/google/syzkaller/pkg/serializer"
)
@@ -30,8 +31,8 @@ func main() {
skipped := 0
saved := ""
- var insns []*ifuzz.Insn
- var insn, insn1 *ifuzz.Insn
+ var insns []*x86.Insn
+ var insn, insn1 *x86.Insn
s := bufio.NewScanner(f)
for i := 1; s.Scan(); i++ {
reportError := func(msg string, args ...interface{}) {
@@ -53,7 +54,7 @@ func main() {
line = saved + line
saved = ""
if line == "{" {
- insn = new(ifuzz.Insn)
+ insn = new(x86.Insn)
continue
}
if line == "}" {
@@ -108,7 +109,7 @@ func main() {
if insn1 != nil {
insns = append(insns, insn1)
}
- insn1 = new(ifuzz.Insn)
+ insn1 = new(x86.Insn)
*insn1 = *insn
if err := parsePattern(insn1, vals); err != nil {
if _, ok := err.(errSkip); !ok {
@@ -137,7 +138,7 @@ func main() {
}
}
- var deduped []*ifuzz.Insn
+ var deduped []*x86.Insn
nextInsn:
for _, insn := range insns {
if insn.Extension == "AVX512VEX" || insn.Extension == "AVX512EVEX" {
@@ -163,12 +164,21 @@ nextInsn:
fmt.Fprintf(os.Stderr, "deduped %v instructions\n", len(insns)-len(deduped))
insns = deduped
- fmt.Printf("// Code generated by pkg/ifuzz/gen. DO NOT EDIT.\n\n")
- fmt.Printf("// +build !codeanalysis\n\n")
- fmt.Printf("package generated\n\n")
- fmt.Printf("import . \"github.com/google/syzkaller/pkg/ifuzz\"\n\n")
- fmt.Printf("func init() { Insns = insns }\n\n")
- fmt.Printf("var insns = ")
+ fmt.Printf(`
+// Code generated by pkg/ifuzz/gen. DO NOT EDIT.
+
+// +build !codeanalysis
+
+package x86
+
+import "github.com/google/syzkaller/pkg/ifuzz/x86"
+
+func init() {
+ x86.Register(insns_x86)
+}
+
+var insns_x86 = []*Insn{
+`)
serializer.Write(os.Stdout, insns)
fmt.Fprintf(os.Stderr, "handled %v, skipped %v\n", len(insns), skipped)
@@ -181,7 +191,7 @@ func (err errSkip) Error() string {
}
// nolint: gocyclo, gocognit, funlen
-func parsePattern(insn *ifuzz.Insn, vals []string) error {
+func parsePattern(insn *x86.Insn, vals []string) error {
if insn.Opcode != nil {
return fmt.Errorf("PATTERN is already parsed for the instruction")
}
@@ -490,7 +500,7 @@ func parsePattern(insn *ifuzz.Insn, vals []string) error {
return nil
}
-func parseOperands(insn *ifuzz.Insn, vals []string) error {
+func parseOperands(insn *x86.Insn, vals []string) error {
for _, v := range vals {
switch v {
case "REG0=SEG():r", "REG1=SEG():r", "REG0=SEG():w":
@@ -538,7 +548,7 @@ func parseModrm(v string) (int8, error) {
return vv, nil
}
-func addImm(insn *ifuzz.Insn, imm int8) {
+func addImm(insn *x86.Insn, imm int8) {
if insn.Imm == 0 {
insn.Imm = imm
return
diff --git a/pkg/ifuzz/generated/empty.go b/pkg/ifuzz/x86/generated/empty.go
index 35a053a49..35a053a49 100644
--- a/pkg/ifuzz/generated/empty.go
+++ b/pkg/ifuzz/x86/generated/empty.go
diff --git a/pkg/ifuzz/generated/insns.go b/pkg/ifuzz/x86/generated/insns.go
index 3a2f747fe..e66b34d72 100644
--- a/pkg/ifuzz/generated/insns.go
+++ b/pkg/ifuzz/x86/generated/insns.go
@@ -4,11 +4,13 @@
package generated
-import . "github.com/google/syzkaller/pkg/ifuzz"
+import "github.com/google/syzkaller/pkg/ifuzz/x86"
-func init() { Insns = insns }
+func init() {
+ x86.Register(insns)
+}
-var insns = []*Insn{
+var insns = []*x86.Insn{
{Name: "FADD", Extension: "X87", Mode: 15, Opcode: []uint8{216}, Modrm: true, Mod: -3, Rm: -1, Mem32: true, VexP: -1},
{Name: "FMUL", Extension: "X87", Mode: 15, Opcode: []uint8{216}, Modrm: true, Mod: -3, Reg: 1, Rm: -1, Mem32: true, VexP: -1},
{Name: "FCOMP", Extension: "X87", Mode: 15, Opcode: []uint8{216}, Modrm: true, Mod: -3, Reg: 3, Rm: -1, Mem32: true, VexP: -1},
diff --git a/pkg/ifuzz/pseudo.go b/pkg/ifuzz/x86/pseudo.go
index e9903f909..1efbcfbcc 100644
--- a/pkg/ifuzz/pseudo.go
+++ b/pkg/ifuzz/x86/pseudo.go
@@ -1,20 +1,21 @@
// 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 ifuzz
+package x86
import (
+ "github.com/google/syzkaller/pkg/ifuzz"
"math/rand"
)
// nolint: funlen
-func initPseudo() {
- Insns = append(Insns, &Insn{
+func (insnset *InsnSetX86) initPseudo() {
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_RDMSR",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
msr := msrs[r.Intn(len(msrs))]
gen.mov32(regECX, msr)
@@ -22,12 +23,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_WRMSR",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
msr := msrs[r.Intn(len(msrs))]
v := generateInt(cfg, r, 8)
@@ -38,12 +39,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_PCI_READ",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
addr, port, size := pciAddrPort(r)
gen.out32(0xcf8, addr)
@@ -51,12 +52,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_PCI_WRITE",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
addr, port, size := pciAddrPort(r)
val := generateInt(cfg, r, 4)
@@ -65,24 +66,24 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_PORT_READ",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
port := ports[r.Intn(len(ports))]
gen.in(port, r.Intn(3))
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_PORT_WRITE",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
port := ports[r.Intn(len(ports))]
val := generateInt(cfg, r, 4)
@@ -90,12 +91,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_XOR_CR",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
cr := controlRegisters[r.Intn(len(controlRegisters))]
var v uint32
@@ -111,12 +112,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_XOR_EFER",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
gen.mov32(regECX, eferMSR)
gen.byte(0x0f, 0x32) // rdmsr
@@ -126,18 +127,18 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_SET_BREAK",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
br := uint8(r.Intn(4))
loc := uint32(r.Intn(4))
typ := uint32(r.Intn(16))
addr := generateInt(cfg, r, 8)
- if cfg.Mode == ModeLong64 {
+ if cfg.Mode == ifuzz.ModeLong64 {
gen.mov64(regRAX, addr)
} else {
gen.mov32(regEAX, uint32(addr))
@@ -149,15 +150,15 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_LOAD_SEG",
- Mode: 1<<ModeLast - 1,
+ Mode: 1<<ifuzz.ModeLast - 1,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
sel := randSelector(r)
- if cfg.Mode == ModeReal16 {
+ if cfg.Mode == ifuzz.ModeReal16 {
sel = uint16(generateInt(cfg, r, 8)) >> 4
}
reg := uint8(r.Intn(6))
@@ -166,16 +167,16 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_FAR_JMP",
- Mode: 1<<ModeLong64 | 1<<ModeProt32 | 1<<ModeProt16,
+ Mode: 1<<ifuzz.ModeLong64 | 1<<ifuzz.ModeProt32 | 1<<ifuzz.ModeProt16,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
sel := randSelector(r)
off := generateInt(cfg, r, 4)
- if cfg.Mode == ModeLong64 {
+ if cfg.Mode == ifuzz.ModeLong64 {
gen.mov32toSPaddr(uint32(sel), 0)
gen.mov32toSPaddr(uint32(off), 2)
if r.Intn(2) == 0 {
@@ -189,7 +190,7 @@ func initPseudo() {
} else {
gen.byte(0x9a) // lcall $imm16, $imm16/32
}
- if cfg.Mode == ModeProt16 {
+ if cfg.Mode == ifuzz.ModeProt16 {
gen.imm16(uint16(off))
} else {
gen.imm32(uint32(off))
@@ -199,12 +200,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_LTR_LLDT",
- Mode: 1<<ModeLong64 | 1<<ModeProt32 | 1<<ModeProt16,
+ Mode: 1<<ifuzz.ModeLong64 | 1<<ifuzz.ModeProt32 | 1<<ifuzz.ModeProt16,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
sel := randSelector(r)
gen.mov16(regAX, sel)
@@ -216,12 +217,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_LGIDT",
- Mode: 1<<ModeLong64 | 1<<ModeProt32 | 1<<ModeProt16,
+ Mode: 1<<ifuzz.ModeLong64 | 1<<ifuzz.ModeProt32 | 1<<ifuzz.ModeProt16,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
limit := uint32(generateInt(cfg, r, 2))
base := uint32(generateInt(cfg, r, 4))
@@ -237,12 +238,12 @@ func initPseudo() {
return gen.text
},
})
- Insns = append(Insns, &Insn{
+ insnset.Insns = append(insnset.Insns, &Insn{
Name: "PSEUDO_HYPERCALL",
- Mode: 1<<ModeLong64 | 1<<ModeProt32 | 1<<ModeProt16,
+ Mode: 1<<ifuzz.ModeLong64 | 1<<ifuzz.ModeProt32 | 1<<ifuzz.ModeProt16,
Priv: true,
Pseudo: true,
- generator: func(cfg *Config, r *rand.Rand) []byte {
+ generator: func(cfg *ifuzz.Config, r *rand.Rand) []byte {
gen := makeGen(cfg, r)
switch r.Intn(2) {
case 0:
@@ -284,7 +285,7 @@ type generator struct {
text []byte
}
-func makeGen(cfg *Config, r *rand.Rand) *generator {
+func makeGen(cfg *ifuzz.Config, r *rand.Rand) *generator {
return &generator{
mode: cfg.Mode,
r: r,
@@ -310,9 +311,9 @@ func (gen *generator) imm64(v uint64) {
func (gen *generator) operand16() {
switch gen.mode {
- case ModeLong64, ModeProt32:
+ case ifuzz.ModeLong64, ifuzz.ModeProt32:
gen.byte(0x66)
- case ModeProt16, ModeReal16:
+ case ifuzz.ModeProt16, ifuzz.ModeReal16:
default:
panic("bad mode")
}
@@ -320,8 +321,8 @@ func (gen *generator) operand16() {
func (gen *generator) operand32() {
switch gen.mode {
- case ModeLong64, ModeProt32:
- case ModeProt16, ModeReal16:
+ case ifuzz.ModeLong64, ifuzz.ModeProt32:
+ case ifuzz.ModeProt16, ifuzz.ModeReal16:
gen.byte(0x66)
default:
panic("bad mode")
@@ -330,8 +331,8 @@ func (gen *generator) operand32() {
func (gen *generator) addr32() {
switch gen.mode {
- case ModeLong64, ModeProt32:
- case ModeProt16, ModeReal16:
+ case ifuzz.ModeLong64, ifuzz.ModeProt32:
+ case ifuzz.ModeProt16, ifuzz.ModeReal16:
gen.byte(0x67)
default:
panic("bad mode")
@@ -383,7 +384,7 @@ func (gen *generator) mov32(reg int, v uint32) {
}
func (gen *generator) mov64(reg int, v uint64) {
- if gen.mode != ModeLong64 {
+ if gen.mode != ifuzz.ModeLong64 {
panic("bad mode")
}
gen.byte(0x48)
diff --git a/pkg/ifuzz/x86/x86.go b/pkg/ifuzz/x86/x86.go
new file mode 100644
index 000000000..1583040ad
--- /dev/null
+++ b/pkg/ifuzz/x86/x86.go
@@ -0,0 +1,186 @@
+// 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 x86 allows to generate and mutate x86 machine code.
+package x86
+
+import (
+ "github.com/google/syzkaller/pkg/ifuzz"
+ "github.com/google/syzkaller/pkg/ifuzz/ifuzzimpl"
+ "math/rand"
+)
+
+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 *ifuzz.Config, r *rand.Rand) []byte // for pseudo instructions
+}
+
+const (
+ typeExec = iota
+ typePriv
+ typeUser
+ typeAll
+ typeLast
+)
+
+type InsnSetX86 struct {
+ modeInsns [ifuzz.ModeLast][typeLast][]ifuzz.Insn
+ Insns []*Insn
+}
+
+func Register(insns []*Insn) {
+ var insnset InsnSetX86
+
+ insnset.Insns = insns
+ if len(insnset.Insns) == 0 {
+ panic("no instructions")
+ }
+ insnset.initPseudo()
+ for mode := 0; mode < ifuzz.ModeLast; mode++ {
+ for _, insn := range insnset.Insns {
+ if insn.Mode&(1<<uint(mode)) == 0 {
+ continue
+ }
+ if insn.Pseudo {
+ insnset.modeInsns[mode][typeExec] =
+ append(insnset.modeInsns[mode][typeExec], ifuzz.Insn(insn))
+ } else if insn.Priv {
+ insnset.modeInsns[mode][typePriv] =
+ append(insnset.modeInsns[mode][typePriv], ifuzz.Insn(insn))
+ insnset.modeInsns[mode][typeAll] =
+ append(insnset.modeInsns[mode][typeAll], ifuzz.Insn(insn))
+ } else {
+ insnset.modeInsns[mode][typeUser] =
+ append(insnset.modeInsns[mode][typeUser], ifuzz.Insn(insn))
+ insnset.modeInsns[mode][typeAll] =
+ append(insnset.modeInsns[mode][typeAll], ifuzz.Insn(insn))
+ }
+ }
+ }
+
+ ifuzzimpl.Register(ifuzz.ArchX86, ifuzz.InsnSet(&insnset))
+}
+
+func (insnset *InsnSetX86) GetInsns(mode, insntype int) []ifuzz.Insn {
+ return insnset.modeInsns[mode][insntype]
+}
+
+func (insn Insn) GetName() string {
+ return insn.Name
+}
+
+func (insn Insn) GetMode() int {
+ return insn.Mode
+}
+
+func (insn Insn) GetPriv() bool {
+ return insn.Priv
+}
+
+func (insn Insn) GetPseudo() bool {
+ return insn.Pseudo
+}
+
+func generateArg(cfg *ifuzz.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 *ifuzz.Config) bool {
+ if cfg.Mode < 0 || cfg.Mode >= ifuzz.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 *ifuzz.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 = ifuzz.SpecialNumbers[r.Intn(len(ifuzz.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
+}
diff --git a/pkg/ifuzz/xed.go b/pkg/ifuzz/x86/xed.go
index dfc66d82a..769ea8b68 100644
--- a/pkg/ifuzz/xed.go
+++ b/pkg/ifuzz/x86/xed.go
@@ -9,7 +9,7 @@
// +build xed
-package ifuzz
+package x86
/*
#include "xed-interface.h"
diff --git a/prog/rand.go b/prog/rand.go
index a115b22ec..85998b942 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -13,7 +13,8 @@ import (
"strings"
"github.com/google/syzkaller/pkg/ifuzz"
- _ "github.com/google/syzkaller/pkg/ifuzz/generated" // pull in generated instruction descriptions
+ "github.com/google/syzkaller/pkg/ifuzz/ifuzzimpl"
+ _ "github.com/google/syzkaller/pkg/ifuzz/x86/generated" // pull in generated instruction descriptions
)
const (
@@ -426,7 +427,7 @@ func (r *randGen) generateText(kind TextKind) []byte {
switch kind {
case TextTarget:
if cfg := createTargetIfuzzConfig(r.target); cfg != nil {
- return ifuzz.Generate(cfg, r.Rand)
+ return ifuzzimpl.Generate(cfg, r.Rand)
}
fallthrough
case TextArm64:
@@ -438,7 +439,7 @@ func (r *randGen) generateText(kind TextKind) []byte {
return text
default:
cfg := createIfuzzConfig(kind)
- return ifuzz.Generate(cfg, r.Rand)
+ return ifuzzimpl.Generate(cfg, r.Rand)
}
}
@@ -446,14 +447,14 @@ func (r *randGen) mutateText(kind TextKind, text []byte) []byte {
switch kind {
case TextTarget:
if cfg := createTargetIfuzzConfig(r.target); cfg != nil {
- return ifuzz.Mutate(cfg, r.Rand, text)
+ return ifuzzimpl.Mutate(cfg, r.Rand, text)
}
fallthrough
case TextArm64:
return mutateData(r, text, 40, 60)
default:
cfg := createIfuzzConfig(kind)
- return ifuzz.Mutate(cfg, r.Rand, text)
+ return ifuzzimpl.Mutate(cfg, r.Rand, text)
}
}
@@ -474,8 +475,10 @@ func createTargetIfuzzConfig(target *Target) *ifuzz.Config {
switch target.Arch {
case "amd64":
cfg.Mode = ifuzz.ModeLong64
+ cfg.Arch = ifuzz.ArchX86
case "386":
cfg.Mode = ifuzz.ModeProt32
+ cfg.Arch = ifuzz.ArchX86
default:
return nil
}
@@ -504,12 +507,16 @@ func createIfuzzConfig(kind TextKind) *ifuzz.Config {
switch kind {
case TextX86Real:
cfg.Mode = ifuzz.ModeReal16
+ cfg.Arch = ifuzz.ArchX86
case TextX86bit16:
cfg.Mode = ifuzz.ModeProt16
+ cfg.Arch = ifuzz.ArchX86
case TextX86bit32:
cfg.Mode = ifuzz.ModeProt32
+ cfg.Arch = ifuzz.ArchX86
case TextX86bit64:
cfg.Mode = ifuzz.ModeLong64
+ cfg.Arch = ifuzz.ArchX86
default:
panic("unknown text kind")
}