From e72f8f11e096d36aefc41a35c718dced97c45dea Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 2 Sep 2020 18:11:22 +1000 Subject: 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 --- pkg/ifuzz/ifuzz.go | 293 ++++------------------------------------------------- 1 file changed, 22 insertions(+), 271 deletions(-) (limited to 'pkg/ifuzz/ifuzz.go') 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<= 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<