github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ifuzz/ifuzz.go (about) 1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package ifuzz 5 6 import ( 7 "math/rand" 8 9 _ "github.com/google/syzkaller/pkg/ifuzz/arm64/generated" // pull in generated instruction descriptions 10 "github.com/google/syzkaller/pkg/ifuzz/iset" 11 _ "github.com/google/syzkaller/pkg/ifuzz/powerpc/generated" // pull in generated instruction descriptions 12 _ "github.com/google/syzkaller/pkg/ifuzz/x86/generated" // pull in generated instruction descriptions 13 ) 14 15 type ( 16 Config = iset.Config 17 MemRegion = iset.MemRegion 18 Mode = iset.Mode 19 ) 20 21 const ( 22 ArchX86 = iset.ArchX86 23 ArchPowerPC = iset.ArchPowerPC 24 ArchArm64 = iset.ArchArm64 25 ModeLong64 = iset.ModeLong64 26 ModeProt32 = iset.ModeProt32 27 ModeProt16 = iset.ModeProt16 28 ModeReal16 = iset.ModeReal16 29 ) 30 31 func Generate(cfg *Config, r *rand.Rand) []byte { 32 var text []byte 33 for i := 0; i < cfg.Len; i++ { 34 insn := randInsn(cfg, r) 35 text = append(text, insn.Encode(cfg, r)...) 36 } 37 return text 38 } 39 40 func Mutate(cfg *Config, r *rand.Rand, text []byte) []byte { 41 insns := split(cfg, text) 42 retry := false 43 for stop := false; !stop || retry || len(insns) == 0; stop = r.Intn(2) == 0 { 44 retry = false 45 switch x := r.Intn(100); { 46 case x < 10 && len(insns) != 0: 47 // Delete instruction. 48 i := r.Intn(len(insns)) 49 copy(insns[i:], insns[i+1:]) 50 insns = insns[:len(insns)-1] 51 case x < 40 && len(insns) != 0: 52 // Replace instruction with another. 53 insn := randInsn(cfg, r) 54 text1 := insn.Encode(cfg, r) 55 i := r.Intn(len(insns)) 56 insns[i] = text1 57 case x < 70 && len(insns) != 0: 58 // Mutate instruction. 59 i := r.Intn(len(insns)) 60 text1 := insns[i] 61 for stop := false; !stop || len(text1) == 0; stop = r.Intn(2) == 0 { 62 switch x := r.Intn(100); { 63 case x < 5 && len(text1) != 0: 64 // Delete byte. 65 pos := r.Intn(len(text1)) 66 copy(text1[pos:], text1[pos+1:]) 67 text1 = text1[:len(text1)-1] 68 case x < 40 && len(text1) != 0: 69 // Replace a byte. 70 pos := r.Intn(len(text1)) 71 text1[pos] = byte(r.Intn(256)) 72 case x < 70 && len(text1) != 0: 73 // Flip a bit. 74 pos := r.Intn(len(text1)) 75 text1[pos] ^= 1 << byte(r.Intn(8)) 76 default: 77 // Insert a byte. 78 pos := r.Intn(len(text1) + 1) 79 text1 = append(text1, 0) 80 copy(text1[pos+1:], text1[pos:]) 81 text1[pos] = byte(r.Intn(256)) 82 } 83 } 84 insns[i] = text1 85 case len(insns) < cfg.Len: 86 // Insert a new instruction. 87 insn := randInsn(cfg, r) 88 text1 := insn.Encode(cfg, r) 89 i := r.Intn(len(insns) + 1) 90 insns = append(insns, nil) 91 copy(insns[i+1:], insns[i:]) 92 insns[i] = text1 93 default: 94 retry = true 95 } 96 } 97 text = nil 98 for _, insn := range insns { 99 text = append(text, insn...) 100 } 101 return text 102 } 103 104 func randInsn(cfg *Config, r *rand.Rand) iset.Insn { 105 insnset := iset.Arches[cfg.Arch] 106 var insns []iset.Insn 107 if cfg.Priv && cfg.Exec { 108 insns = insnset.GetInsns(cfg.Mode, iset.Type(r.Intn(3))) 109 } else if cfg.Priv { 110 insns = insnset.GetInsns(cfg.Mode, iset.Type(r.Intn(2))) 111 } else { 112 insns = insnset.GetInsns(cfg.Mode, iset.TypeUser) 113 } 114 return insns[r.Intn(len(insns))] 115 } 116 117 func split(cfg *Config, text []byte) [][]byte { 118 insnset := iset.Arches[cfg.Arch] 119 text = append([]byte{}, text...) 120 var insns [][]byte 121 var bad []byte 122 for len(text) != 0 { 123 n, err := insnset.Decode(cfg.Mode, text) 124 if err != nil || n == 0 { 125 bad = append(bad, text[0]) 126 text = text[1:] 127 continue 128 } 129 if bad != nil { 130 insns = append(insns, bad) 131 bad = nil 132 } 133 insns = append(insns, text[:n]) 134 text = text[n:] 135 } 136 if bad != nil { 137 insns = append(insns, bad) 138 } 139 return insns 140 }