blob: 01137b6eac8aba99bd9acda40fa31db7bb43affd [file] [log] [blame] [edit]
// 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
import (
"math/rand"
"github.com/google/syzkaller/pkg/ifuzz/iset"
_ "github.com/google/syzkaller/pkg/ifuzz/powerpc/generated" // pull in generated instruction descriptions
_ "github.com/google/syzkaller/pkg/ifuzz/x86/generated" // pull in generated instruction descriptions
)
type (
Config = iset.Config
MemRegion = iset.MemRegion
Mode = iset.Mode
)
const (
ArchX86 = iset.ArchX86
ArchPowerPC = iset.ArchPowerPC
ModeLong64 = iset.ModeLong64
ModeProt32 = iset.ModeProt32
ModeProt16 = iset.ModeProt16
ModeReal16 = iset.ModeReal16
)
func Generate(cfg *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 *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 *Config, r *rand.Rand) iset.Insn {
insnset := iset.Arches[cfg.Arch]
var insns []iset.Insn
if cfg.Priv && cfg.Exec {
insns = insnset.GetInsns(cfg.Mode, iset.Type(r.Intn(3)))
} else if cfg.Priv {
insns = insnset.GetInsns(cfg.Mode, iset.Type(r.Intn(2)))
} else {
insns = insnset.GetInsns(cfg.Mode, iset.TypeUser)
}
return insns[r.Intn(len(insns))]
}
func split(cfg *Config, text []byte) [][]byte {
insnset := iset.Arches[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
}