diff options
| author | Alexey Kardashevskiy <aik@linux.ibm.com> | 2020-09-02 18:11:22 +1000 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-11-20 15:31:42 +0100 |
| commit | e72f8f11e096d36aefc41a35c718dced97c45dea (patch) | |
| tree | 6619d0089d8ac172c64853c76c0b1acc9485d192 /pkg/ifuzz | |
| parent | 740ff4615a9ced4a8a016365aa44674b9b0e807d (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')
| -rw-r--r-- | pkg/ifuzz/ifuzz.go | 293 | ||||
| -rw-r--r-- | pkg/ifuzz/ifuzz_test.go | 54 | ||||
| -rw-r--r-- | pkg/ifuzz/ifuzzimpl/ifuzzimpl.go | 145 | ||||
| -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.go | 186 | ||||
| -rw-r--r-- | pkg/ifuzz/x86/xed.go (renamed from pkg/ifuzz/xed.go) | 2 |
12 files changed, 508 insertions, 382 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" |
