304 lines
6.8 KiB
Go
304 lines
6.8 KiB
Go
|
// 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"
|
||
|
"reflect"
|
||
|
"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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func min(a, b int) int {
|
||
|
if a < b {
|
||
|
return a
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// 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(uint64(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() {
|
||
|
oldHdr := (*reflect.SliceHeader)(unsafe.Pointer(ptrB))
|
||
|
newHdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||
|
if oldHdr.Data != newHdr.Data {
|
||
|
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))
|
||
|
}
|
||
|
}
|