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  }