// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fuzz import ( "encoding/binary" "fmt" "math" "unsafe" ) type mutator struct { r mutatorRand scratch []byte // scratch slice to avoid additional allocations } func newMutator() *mutator { return &mutator{r: newPcgRand()} } func (m *mutator) rand(n int) int { return m.r.intn(n) } func (m *mutator) randByteOrder() binary.ByteOrder { if m.r.bool() { return binary.LittleEndian } return binary.BigEndian } // chooseLen chooses length of range mutation in range [1,n]. It gives // preference to shorter ranges. func (m *mutator) chooseLen(n int) int { switch x := m.rand(100); { case x < 90: return m.rand(min(8, n)) + 1 case x < 99: return m.rand(min(32, n)) + 1 default: return m.rand(n) + 1 } } // mutate performs several mutations on the provided values. func (m *mutator) mutate(vals []any, maxBytes int) { // TODO(katiehockman): pull some of these functions into helper methods and // test that each case is working as expected. // TODO(katiehockman): perform more types of mutations for []byte. // maxPerVal will represent the maximum number of bytes that each value be // allowed after mutating, giving an equal amount of capacity to each line. // Allow a little wiggle room for the encoding. maxPerVal := maxBytes/len(vals) - 100 // Pick a random value to mutate. // TODO: consider mutating more than one value at a time. i := m.rand(len(vals)) switch v := vals[i].(type) { case int: vals[i] = int(m.mutateInt(int64(v), maxInt)) case int8: vals[i] = int8(m.mutateInt(int64(v), math.MaxInt8)) case int16: vals[i] = int16(m.mutateInt(int64(v), math.MaxInt16)) case int64: vals[i] = m.mutateInt(v, maxInt) case uint: vals[i] = uint(m.mutateUInt(uint64(v), maxUint)) case uint16: vals[i] = uint16(m.mutateUInt(uint64(v), math.MaxUint16)) case uint32: vals[i] = uint32(m.mutateUInt(uint64(v), math.MaxUint32)) case uint64: vals[i] = m.mutateUInt(v, maxUint) case float32: vals[i] = float32(m.mutateFloat(float64(v), math.MaxFloat32)) case float64: vals[i] = m.mutateFloat(v, math.MaxFloat64) case bool: if m.rand(2) == 1 { vals[i] = !v // 50% chance of flipping the bool } case rune: // int32 vals[i] = rune(m.mutateInt(int64(v), math.MaxInt32)) case byte: // uint8 vals[i] = byte(m.mutateUInt(uint64(v), math.MaxUint8)) case string: if len(v) > maxPerVal { panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) } if cap(m.scratch) < maxPerVal { m.scratch = append(make([]byte, 0, maxPerVal), v...) } else { m.scratch = m.scratch[:len(v)] copy(m.scratch, v) } m.mutateBytes(&m.scratch) vals[i] = string(m.scratch) case []byte: if len(v) > maxPerVal { panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v))) } if cap(m.scratch) < maxPerVal { m.scratch = append(make([]byte, 0, maxPerVal), v...) } else { m.scratch = m.scratch[:len(v)] copy(m.scratch, v) } m.mutateBytes(&m.scratch) vals[i] = m.scratch default: panic(fmt.Sprintf("type not supported for mutating: %T", vals[i])) } } func (m *mutator) mutateInt(v, maxValue int64) int64 { var max int64 for { max = 100 switch m.rand(2) { case 0: // Add a random number if v >= maxValue { continue } if v > 0 && maxValue-v < max { // Don't let v exceed maxValue max = maxValue - v } v += int64(1 + m.rand(int(max))) return v case 1: // Subtract a random number if v <= -maxValue { continue } if v < 0 && maxValue+v < max { // Don't let v drop below -maxValue max = maxValue + v } v -= int64(1 + m.rand(int(max))) return v } } } func (m *mutator) mutateUInt(v, maxValue uint64) uint64 { var max uint64 for { max = 100 switch m.rand(2) { case 0: // Add a random number if v >= maxValue { continue } if v > 0 && maxValue-v < max { // Don't let v exceed maxValue max = maxValue - v } v += uint64(1 + m.rand(int(max))) return v case 1: // Subtract a random number if v <= 0 { continue } if v < max { // Don't let v drop below 0 max = v } v -= uint64(1 + m.rand(int(max))) return v } } } func (m *mutator) mutateFloat(v, maxValue float64) float64 { var max float64 for { switch m.rand(4) { case 0: // Add a random number if v >= maxValue { continue } max = 100 if v > 0 && maxValue-v < max { // Don't let v exceed maxValue max = maxValue - v } v += float64(1 + m.rand(int(max))) return v case 1: // Subtract a random number if v <= -maxValue { continue } max = 100 if v < 0 && maxValue+v < max { // Don't let v drop below -maxValue max = maxValue + v } v -= float64(1 + m.rand(int(max))) return v case 2: // Multiply by a random number absV := math.Abs(v) if v == 0 || absV >= maxValue { continue } max = 10 if maxValue/absV < max { // Don't let v go beyond the minimum or maximum value max = maxValue / absV } v *= float64(1 + m.rand(int(max))) return v case 3: // Divide by a random number if v == 0 { continue } v /= float64(1 + m.rand(10)) return v } } } type byteSliceMutator func(*mutator, []byte) []byte var byteSliceMutators = []byteSliceMutator{ byteSliceRemoveBytes, byteSliceInsertRandomBytes, byteSliceDuplicateBytes, byteSliceOverwriteBytes, byteSliceBitFlip, byteSliceXORByte, byteSliceSwapByte, byteSliceArithmeticUint8, byteSliceArithmeticUint16, byteSliceArithmeticUint32, byteSliceArithmeticUint64, byteSliceOverwriteInterestingUint8, byteSliceOverwriteInterestingUint16, byteSliceOverwriteInterestingUint32, byteSliceInsertConstantBytes, byteSliceOverwriteConstantBytes, byteSliceShuffleBytes, byteSliceSwapBytes, } func (m *mutator) mutateBytes(ptrB *[]byte) { b := *ptrB defer func() { if unsafe.SliceData(*ptrB) != unsafe.SliceData(b) { panic("data moved to new address") } *ptrB = b }() for { mut := byteSliceMutators[m.rand(len(byteSliceMutators))] if mutated := mut(m, b); mutated != nil { b = mutated return } } } var ( interesting8 = []int8{-128, -1, 0, 1, 16, 32, 64, 100, 127} interesting16 = []int16{-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767} interesting32 = []int32{-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647} ) const ( maxUint = uint64(^uint(0)) maxInt = int64(maxUint >> 1) ) func init() { for _, v := range interesting8 { interesting16 = append(interesting16, int16(v)) } for _, v := range interesting16 { interesting32 = append(interesting32, int32(v)) } }