diff options
| -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 | ||||
| -rw-r--r-- | prog/rand.go | 17 |
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") } |
