933 lines
22 KiB
Go
933 lines
22 KiB
Go
|
package gofuzzheaders
|
||
|
|
||
|
import (
|
||
|
"archive/tar"
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"math"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"time"
|
||
|
"unsafe"
|
||
|
|
||
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
MaxTotalLen = uint32(2000000)
|
||
|
)
|
||
|
|
||
|
func SetMaxTotalLen(newLen uint32) {
|
||
|
MaxTotalLen = newLen
|
||
|
}
|
||
|
|
||
|
type ConsumeFuzzer struct {
|
||
|
data []byte
|
||
|
CommandPart []byte
|
||
|
RestOfArray []byte
|
||
|
NumberOfCalls int
|
||
|
position uint32
|
||
|
fuzzUnexportedFields bool
|
||
|
Funcs map[reflect.Type]reflect.Value
|
||
|
}
|
||
|
|
||
|
func IsDivisibleBy(n int, divisibleby int) bool {
|
||
|
return (n % divisibleby) == 0
|
||
|
}
|
||
|
|
||
|
func NewConsumer(fuzzData []byte) *ConsumeFuzzer {
|
||
|
fuzzMap := make(map[reflect.Type]reflect.Value)
|
||
|
f := &ConsumeFuzzer{data: fuzzData, position: 0, Funcs: fuzzMap}
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) Split(minCalls, maxCalls int) error {
|
||
|
if len(f.data) == 0 {
|
||
|
return errors.New("Could not split")
|
||
|
}
|
||
|
numberOfCalls := int(f.data[0])
|
||
|
if numberOfCalls < minCalls || numberOfCalls > maxCalls {
|
||
|
return errors.New("Bad number of calls")
|
||
|
|
||
|
}
|
||
|
if len(f.data) < numberOfCalls+numberOfCalls+1 {
|
||
|
return errors.New("Length of data does not match required parameters")
|
||
|
}
|
||
|
|
||
|
// Define part 2 and 3 of the data array
|
||
|
commandPart := f.data[1 : numberOfCalls+1]
|
||
|
restOfArray := f.data[numberOfCalls+1:]
|
||
|
|
||
|
// Just a small check. It is necessary
|
||
|
if len(commandPart) != numberOfCalls {
|
||
|
return errors.New("Length of commandPart does not match number of calls")
|
||
|
}
|
||
|
|
||
|
// Check if restOfArray is divisible by numberOfCalls
|
||
|
if !IsDivisibleBy(len(restOfArray), numberOfCalls) {
|
||
|
return errors.New("Length of commandPart does not match number of calls")
|
||
|
}
|
||
|
f.CommandPart = commandPart
|
||
|
f.RestOfArray = restOfArray
|
||
|
f.NumberOfCalls = numberOfCalls
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) AllowUnexportedFields() {
|
||
|
f.fuzzUnexportedFields = true
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) DisallowUnexportedFields() {
|
||
|
f.fuzzUnexportedFields = false
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GenerateStruct(targetStruct interface{}) error {
|
||
|
v := reflect.ValueOf(targetStruct)
|
||
|
/*if !v.CanSet() {
|
||
|
return errors.New("This interface cannot be set")
|
||
|
}*/
|
||
|
e := v.Elem()
|
||
|
err := f.fuzzStruct(e, false)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) setCustom(v reflect.Value) error {
|
||
|
// First: see if we have a fuzz function for it.
|
||
|
doCustom, ok := f.Funcs[v.Type()]
|
||
|
if !ok {
|
||
|
return fmt.Errorf("Could not find a custom function")
|
||
|
}
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.Ptr:
|
||
|
if v.IsNil() {
|
||
|
if !v.CanSet() {
|
||
|
return fmt.Errorf("Could not use a custom function")
|
||
|
}
|
||
|
v.Set(reflect.New(v.Type().Elem()))
|
||
|
}
|
||
|
case reflect.Map:
|
||
|
if v.IsNil() {
|
||
|
if !v.CanSet() {
|
||
|
return fmt.Errorf("Could not use a custom function")
|
||
|
}
|
||
|
v.Set(reflect.MakeMap(v.Type()))
|
||
|
}
|
||
|
default:
|
||
|
return fmt.Errorf("Could not use a custom function")
|
||
|
}
|
||
|
|
||
|
verr := doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
|
||
|
F: f,
|
||
|
})})
|
||
|
// check if we return an error
|
||
|
if verr[0].IsNil() {
|
||
|
return nil
|
||
|
}
|
||
|
return fmt.Errorf("Could not use a custom function")
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) fuzzStruct(e reflect.Value, customFunctions bool) error {
|
||
|
|
||
|
// We check if we should check for custom functions
|
||
|
if customFunctions {
|
||
|
if e.IsValid() {
|
||
|
if e.CanAddr() {
|
||
|
err := f.setCustom(e.Addr())
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
/* return f.setCustom(e)
|
||
|
_, ok := f.Funcs[e.Type()]
|
||
|
if ok {
|
||
|
if e.CanAddr() {
|
||
|
err := f.setCustom(e.Addr())
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
}*/
|
||
|
//return f.setCustom(e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch e.Kind() {
|
||
|
case reflect.Struct:
|
||
|
for i := 0; i < e.NumField(); i++ {
|
||
|
var v reflect.Value
|
||
|
if !e.Field(i).CanSet() {
|
||
|
if f.fuzzUnexportedFields {
|
||
|
v = reflect.NewAt(e.Field(i).Type(), unsafe.Pointer(e.Field(i).UnsafeAddr())).Elem()
|
||
|
}
|
||
|
err := f.fuzzStruct(v, customFunctions)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
} else {
|
||
|
/*if e.Field(i).Kind() == reflect.Struct {
|
||
|
//e = reflect.NewAt(e.Type(), unsafe.Pointer(e.UnsafeAddr())).Elem()
|
||
|
//e.Field(i).Set(reflect.New(e.Field(i).Type()))
|
||
|
}*/
|
||
|
v = e.Field(i)
|
||
|
//v = reflect.New(e.Field(i).Type())
|
||
|
err := f.fuzzStruct(v, customFunctions)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
/*if e.Field(i).CanSet() {
|
||
|
e.Field(i).Set(v.Elem())
|
||
|
}*/
|
||
|
}
|
||
|
}
|
||
|
case reflect.String:
|
||
|
str, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetString(str)
|
||
|
}
|
||
|
case reflect.Slice:
|
||
|
var maxElements uint32
|
||
|
// Byte slices should not be restricted
|
||
|
if e.Type().String() == "[]uint8" {
|
||
|
maxElements = 10000000
|
||
|
} else {
|
||
|
maxElements = 50
|
||
|
}
|
||
|
|
||
|
randQty, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var numOfElements uint32
|
||
|
numOfElements = randQty % maxElements
|
||
|
if (uint32(len(f.data)) - f.position) < numOfElements {
|
||
|
numOfElements = uint32(len(f.data)) - f.position
|
||
|
}
|
||
|
|
||
|
uu := reflect.MakeSlice(e.Type(), int(numOfElements), int(numOfElements))
|
||
|
|
||
|
for i := 0; i < int(numOfElements); i++ {
|
||
|
err := f.fuzzStruct(uu.Index(i), customFunctions)
|
||
|
// If we have more than 10, then we can proceed with that.
|
||
|
if err != nil {
|
||
|
if i >= 10 {
|
||
|
if e.CanSet() {
|
||
|
e.Set(uu)
|
||
|
}
|
||
|
return nil
|
||
|
} else {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.Set(uu)
|
||
|
}
|
||
|
case reflect.Uint16:
|
||
|
newInt, err := f.GetUint16()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetUint(uint64(newInt))
|
||
|
}
|
||
|
case reflect.Uint32:
|
||
|
newInt, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetUint(uint64(newInt))
|
||
|
}
|
||
|
case reflect.Uint64:
|
||
|
newInt, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetUint(uint64(newInt))
|
||
|
}
|
||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
|
newInt, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetInt(int64(newInt))
|
||
|
}
|
||
|
case reflect.Float32:
|
||
|
newFloat, err := f.GetFloat32()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetFloat(float64(newFloat))
|
||
|
}
|
||
|
case reflect.Float64:
|
||
|
newFloat, err := f.GetFloat64()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetFloat(float64(newFloat))
|
||
|
}
|
||
|
case reflect.Map:
|
||
|
if e.CanSet() {
|
||
|
e.Set(reflect.MakeMap(e.Type()))
|
||
|
maxElements := 50
|
||
|
randQty, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
numOfElements := randQty % maxElements
|
||
|
for i := 0; i < numOfElements; i++ {
|
||
|
key := reflect.New(e.Type().Key()).Elem()
|
||
|
err := f.fuzzStruct(key, customFunctions)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
val := reflect.New(e.Type().Elem()).Elem()
|
||
|
err = f.fuzzStruct(val, customFunctions)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
e.SetMapIndex(key, val)
|
||
|
}
|
||
|
}
|
||
|
case reflect.Ptr:
|
||
|
if e.CanSet() {
|
||
|
e.Set(reflect.New(e.Type().Elem()))
|
||
|
err := f.fuzzStruct(e.Elem(), customFunctions)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
case reflect.Uint8:
|
||
|
b, err := f.GetByte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if e.CanSet() {
|
||
|
e.SetUint(uint64(b))
|
||
|
}
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetStringArray() (reflect.Value, error) {
|
||
|
// The max size of the array:
|
||
|
max := uint32(20)
|
||
|
|
||
|
arraySize := f.position
|
||
|
if arraySize > max {
|
||
|
arraySize = max
|
||
|
}
|
||
|
elemType := reflect.TypeOf("string")
|
||
|
stringArray := reflect.MakeSlice(reflect.SliceOf(elemType), int(arraySize), int(arraySize))
|
||
|
if f.position+arraySize >= uint32(len(f.data)) {
|
||
|
return stringArray, errors.New("Could not make string array")
|
||
|
}
|
||
|
|
||
|
for i := 0; i < int(arraySize); i++ {
|
||
|
stringSize := uint32(f.data[f.position])
|
||
|
|
||
|
if f.position+stringSize >= uint32(len(f.data)) {
|
||
|
return stringArray, nil
|
||
|
}
|
||
|
stringToAppend := string(f.data[f.position : f.position+stringSize])
|
||
|
strVal := reflect.ValueOf(stringToAppend)
|
||
|
stringArray = reflect.Append(stringArray, strVal)
|
||
|
f.position = f.position + stringSize
|
||
|
}
|
||
|
return stringArray, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetInt() (int, error) {
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return 0, errors.New("Not enough bytes to create int")
|
||
|
}
|
||
|
returnInt := int(f.data[f.position])
|
||
|
f.position++
|
||
|
return returnInt, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetByte() (byte, error) {
|
||
|
if len(f.data) == 0 {
|
||
|
return 0x00, errors.New("Not enough bytes to get byte")
|
||
|
}
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return 0x00, errors.New("Not enough bytes to get byte")
|
||
|
}
|
||
|
returnByte := f.data[f.position]
|
||
|
f.position += 1
|
||
|
return returnByte, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetNBytes(numberOfBytes int) ([]byte, error) {
|
||
|
returnBytes := make([]byte, 0)
|
||
|
if len(f.data) == 0 {
|
||
|
return returnBytes, errors.New("Not enough bytes to get byte")
|
||
|
}
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return returnBytes, errors.New("Not enough bytes to get byte")
|
||
|
}
|
||
|
for i := 0; i < numberOfBytes; i++ {
|
||
|
newByte, err := f.GetByte()
|
||
|
if err != nil {
|
||
|
return returnBytes, err
|
||
|
}
|
||
|
returnBytes = append(returnBytes, newByte)
|
||
|
}
|
||
|
return returnBytes, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetUint16() (uint16, error) {
|
||
|
u16, err := f.GetNBytes(2)
|
||
|
if err != nil {
|
||
|
return uint16(0), err
|
||
|
}
|
||
|
littleEndian, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
return uint16(0), err
|
||
|
}
|
||
|
if littleEndian {
|
||
|
u16LE := binary.LittleEndian.Uint16(u16)
|
||
|
return u16LE, nil
|
||
|
}
|
||
|
u16BE := binary.BigEndian.Uint16(u16)
|
||
|
return u16BE, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetUint32() (uint32, error) {
|
||
|
u32, err := f.GetNBytes(4)
|
||
|
if err != nil {
|
||
|
return uint32(0), err
|
||
|
}
|
||
|
littleEndian, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
return uint32(0), err
|
||
|
}
|
||
|
if littleEndian {
|
||
|
u32LE := binary.LittleEndian.Uint32(u32)
|
||
|
return u32LE, nil
|
||
|
}
|
||
|
u32BE := binary.BigEndian.Uint32(u32)
|
||
|
return u32BE, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetUint64() (uint64, error) {
|
||
|
u64, err := f.GetNBytes(8)
|
||
|
if err != nil {
|
||
|
return uint64(0), err
|
||
|
}
|
||
|
littleEndian, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
return uint64(0), err
|
||
|
}
|
||
|
if littleEndian {
|
||
|
u64LE := binary.LittleEndian.Uint64(u64)
|
||
|
return u64LE, nil
|
||
|
}
|
||
|
u64BE := binary.BigEndian.Uint64(u64)
|
||
|
return u64BE, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetBytes() ([]byte, error) {
|
||
|
if len(f.data) == 0 || f.position >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
length, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
if f.position+length > MaxTotalLen {
|
||
|
return nil, errors.New("Created too large a string")
|
||
|
}
|
||
|
byteBegin := f.position - 1
|
||
|
if byteBegin >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
if length == 0 {
|
||
|
return nil, errors.New("Zero-length is not supported")
|
||
|
}
|
||
|
if byteBegin+length >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
if byteBegin+length < byteBegin {
|
||
|
return nil, errors.New("Nunmbers overflow. Returning")
|
||
|
}
|
||
|
b := f.data[byteBegin : byteBegin+length]
|
||
|
f.position = byteBegin + length
|
||
|
return b, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetString() (string, error) {
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
length, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
if f.position > MaxTotalLen {
|
||
|
return "nil", errors.New("Created too large a string")
|
||
|
}
|
||
|
byteBegin := f.position - 1
|
||
|
if byteBegin >= uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
if byteBegin+length > uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
if byteBegin > byteBegin+length {
|
||
|
return "nil", errors.New("Numbers overflow. Returning")
|
||
|
}
|
||
|
str := string(f.data[byteBegin : byteBegin+length])
|
||
|
f.position = byteBegin + length
|
||
|
return str, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetBool() (bool, error) {
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return false, errors.New("Not enough bytes to create bool")
|
||
|
}
|
||
|
if IsDivisibleBy(int(f.data[f.position]), 2) {
|
||
|
f.position++
|
||
|
return true, nil
|
||
|
} else {
|
||
|
f.position++
|
||
|
return false, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) FuzzMap(m interface{}) error {
|
||
|
err := f.GenerateStruct(m)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func returnTarBytes(buf []byte) ([]byte, error) {
|
||
|
reader := bytes.NewReader(buf)
|
||
|
tr := tar.NewReader(reader)
|
||
|
|
||
|
// Count files
|
||
|
var fileCounter int
|
||
|
fileCounter = 0
|
||
|
for {
|
||
|
_, err := tr.Next()
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
fileCounter++
|
||
|
}
|
||
|
if fileCounter > 4 {
|
||
|
return buf, nil
|
||
|
}
|
||
|
return nil, fmt.Errorf("Not enough files were created\n")
|
||
|
}
|
||
|
|
||
|
func setTarHeaderFormat(hdr *tar.Header, f *ConsumeFuzzer) error {
|
||
|
ind, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch ind % 4 {
|
||
|
case 0:
|
||
|
hdr.Format = tar.FormatUnknown
|
||
|
case 1:
|
||
|
hdr.Format = tar.FormatUSTAR
|
||
|
case 2:
|
||
|
hdr.Format = tar.FormatPAX
|
||
|
case 3:
|
||
|
hdr.Format = tar.FormatGNU
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func setTarHeaderTypeflag(hdr *tar.Header, f *ConsumeFuzzer) error {
|
||
|
ind, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch ind % 13 {
|
||
|
case 0:
|
||
|
hdr.Typeflag = tar.TypeReg
|
||
|
case 1:
|
||
|
hdr.Typeflag = tar.TypeLink
|
||
|
linkname, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
hdr.Linkname = linkname
|
||
|
case 2:
|
||
|
hdr.Typeflag = tar.TypeSymlink
|
||
|
linkname, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
hdr.Linkname = linkname
|
||
|
case 3:
|
||
|
hdr.Typeflag = tar.TypeChar
|
||
|
case 4:
|
||
|
hdr.Typeflag = tar.TypeBlock
|
||
|
case 5:
|
||
|
hdr.Typeflag = tar.TypeDir
|
||
|
case 6:
|
||
|
hdr.Typeflag = tar.TypeFifo
|
||
|
case 7:
|
||
|
hdr.Typeflag = tar.TypeCont
|
||
|
case 8:
|
||
|
hdr.Typeflag = tar.TypeXHeader
|
||
|
case 9:
|
||
|
hdr.Typeflag = tar.TypeXGlobalHeader
|
||
|
case 10:
|
||
|
hdr.Typeflag = tar.TypeGNUSparse
|
||
|
case 11:
|
||
|
hdr.Typeflag = tar.TypeGNULongName
|
||
|
case 12:
|
||
|
hdr.Typeflag = tar.TypeGNULongLink
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) createTarFileBody() ([]byte, error) {
|
||
|
if len(f.data) == 0 || f.position >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
length, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
|
||
|
// A bit of optimization to attempt to create a file body
|
||
|
// when we don't have as many bytes left as "length"
|
||
|
remainingBytes := (uint32(len(f.data)) - f.position)
|
||
|
totalDataLen := uint32(len(f.data))
|
||
|
if uint32(len(f.data))-f.position < 50 {
|
||
|
if remainingBytes == 0 {
|
||
|
return nil, errors.New("Created too large a string")
|
||
|
}
|
||
|
length = length % remainingBytes
|
||
|
} else if len(f.data) < 500 {
|
||
|
if totalDataLen == 0 {
|
||
|
return nil, errors.New("Created too large a string")
|
||
|
}
|
||
|
length = length % totalDataLen
|
||
|
}
|
||
|
if f.position+length > MaxTotalLen {
|
||
|
return nil, errors.New("Created too large a string")
|
||
|
}
|
||
|
byteBegin := f.position - 1
|
||
|
if byteBegin >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
if length == 0 {
|
||
|
return nil, errors.New("Zero-length is not supported")
|
||
|
}
|
||
|
if byteBegin+length >= uint32(len(f.data)) {
|
||
|
return nil, errors.New("Not enough bytes to create byte array")
|
||
|
}
|
||
|
if byteBegin+length < byteBegin {
|
||
|
return nil, errors.New("Nunmbers overflow. Returning")
|
||
|
}
|
||
|
filebody := f.data[byteBegin : byteBegin+length]
|
||
|
f.position = byteBegin + length
|
||
|
return filebody, nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// Is similar to GetString(), but creates string based on the length
|
||
|
// of the length of f.data to increase the likelihood of not overflowing
|
||
|
// f.data
|
||
|
func (f *ConsumeFuzzer) getTarFilename() (string, error) {
|
||
|
if f.position >= uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
length, err := f.GetUint32()
|
||
|
if err != nil {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
|
||
|
// A bit of optimization to attempt to create a file name
|
||
|
// when we don't have as many bytes left as "length"
|
||
|
remainingBytes := (uint32(len(f.data)) - f.position)
|
||
|
totalDataLen := uint32(len(f.data))
|
||
|
if uint32(len(f.data))-f.position < 50 {
|
||
|
if remainingBytes == 0 {
|
||
|
return "nil", errors.New("Created too large a string")
|
||
|
}
|
||
|
length = length % remainingBytes
|
||
|
} else if len(f.data) < 500 {
|
||
|
if totalDataLen == 0 {
|
||
|
return "nil", errors.New("Created too large a string")
|
||
|
}
|
||
|
length = length % totalDataLen
|
||
|
}
|
||
|
if f.position > MaxTotalLen {
|
||
|
return "nil", errors.New("Created too large a string")
|
||
|
}
|
||
|
byteBegin := f.position - 1
|
||
|
if byteBegin >= uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
if byteBegin+length > uint32(len(f.data)) {
|
||
|
return "nil", errors.New("Not enough bytes to create string")
|
||
|
}
|
||
|
if byteBegin > byteBegin+length {
|
||
|
return "nil", errors.New("Numbers overflow. Returning")
|
||
|
}
|
||
|
str := string(f.data[byteBegin : byteBegin+length])
|
||
|
f.position = byteBegin + length
|
||
|
return str, nil
|
||
|
}
|
||
|
|
||
|
// TarBytes returns valid bytes for a tar archive
|
||
|
func (f *ConsumeFuzzer) TarBytes() ([]byte, error) {
|
||
|
numberOfFiles, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var buf bytes.Buffer
|
||
|
tw := tar.NewWriter(&buf)
|
||
|
defer tw.Close()
|
||
|
|
||
|
maxNoOfFiles := 1000
|
||
|
for i := 0; i < numberOfFiles%maxNoOfFiles; i++ {
|
||
|
filename, err := f.getTarFilename()
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
filebody, err := f.createTarFileBody()
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
hdr := &tar.Header{}
|
||
|
|
||
|
err = setTarHeaderTypeflag(hdr, f)
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
|
||
|
sec, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
|
||
|
nsec, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
|
||
|
hdr.ModTime = time.Unix(int64(sec), int64(nsec))
|
||
|
|
||
|
hdr.Name = filename
|
||
|
hdr.Size = int64(len(filebody))
|
||
|
hdr.Mode = 0600
|
||
|
|
||
|
err = setTarHeaderFormat(hdr, f)
|
||
|
if err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
|
||
|
if err := tw.WriteHeader(hdr); err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
if _, err := tw.Write(filebody); err != nil {
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
}
|
||
|
return returnTarBytes(buf.Bytes())
|
||
|
}
|
||
|
|
||
|
// Creates pseudo-random files in rootDir.
|
||
|
// Will create subdirs and place the files there.
|
||
|
// It is the callers responsibility to ensure that
|
||
|
// rootDir exists.
|
||
|
func (f *ConsumeFuzzer) CreateFiles(rootDir string) error {
|
||
|
var noOfCreatedFiles int
|
||
|
noOfCreatedFiles = 0
|
||
|
numberOfFiles, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
maxNumberOfFiles := numberOfFiles % 4000 // This is completely arbitrary
|
||
|
if maxNumberOfFiles == 0 {
|
||
|
return errors.New("maxNumberOfFiles is nil")
|
||
|
}
|
||
|
|
||
|
for i := 0; i < maxNumberOfFiles; i++ {
|
||
|
// The file to create:
|
||
|
fileName, err := f.GetString()
|
||
|
if err != nil {
|
||
|
if noOfCreatedFiles > 0 {
|
||
|
// If files have been created, we don't return
|
||
|
// an error
|
||
|
break
|
||
|
} else {
|
||
|
return errors.New("Could not get fileName")
|
||
|
}
|
||
|
}
|
||
|
var fullFilePath string
|
||
|
fullFilePath, err = securejoin.SecureJoin(rootDir, fileName)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Find the subdirectory of the file
|
||
|
subDir := filepath.Dir(fileName)
|
||
|
if subDir != "" && subDir != "." {
|
||
|
// create the dir first
|
||
|
|
||
|
// Avoid going outside the root dir
|
||
|
if strings.Contains(subDir, "../") || (len(subDir) > 0 && subDir[0] == 47) || strings.Contains(subDir, "\\") {
|
||
|
continue
|
||
|
}
|
||
|
dirPath := filepath.Join(rootDir, subDir)
|
||
|
dirPath, err := securejoin.SecureJoin(rootDir, subDir)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||
|
err2 := os.MkdirAll(dirPath, 0777)
|
||
|
if err2 != nil {
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
fullFilePath, err = securejoin.SecureJoin(dirPath, fileName)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
} else {
|
||
|
// Create symlink
|
||
|
createSymlink, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
if noOfCreatedFiles > 0 {
|
||
|
break
|
||
|
} else {
|
||
|
return errors.New("Could not create the symlink")
|
||
|
}
|
||
|
}
|
||
|
if createSymlink {
|
||
|
symlinkTarget, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
os.Symlink(symlinkTarget, fullFilePath)
|
||
|
// stop loop here, since a symlink needs no further action
|
||
|
noOfCreatedFiles++
|
||
|
continue
|
||
|
}
|
||
|
// We create a normal file
|
||
|
fileContents, err := f.GetBytes()
|
||
|
if err != nil {
|
||
|
if noOfCreatedFiles > 0 {
|
||
|
break
|
||
|
} else {
|
||
|
return errors.New("Could not create the file")
|
||
|
}
|
||
|
}
|
||
|
createdFile, err := os.Create(fullFilePath)
|
||
|
if err != nil {
|
||
|
createdFile.Close()
|
||
|
continue
|
||
|
}
|
||
|
_, err = createdFile.Write(fileContents)
|
||
|
if err != nil {
|
||
|
createdFile.Close()
|
||
|
continue
|
||
|
}
|
||
|
createdFile.Close()
|
||
|
noOfCreatedFiles++
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Returns a string that can only consists of characters that are
|
||
|
// included in possibleChars. Will return an error if the created
|
||
|
// string does not have the specified length
|
||
|
func (f *ConsumeFuzzer) GetStringFrom(possibleChars string, length int) (string, error) {
|
||
|
returnString := ""
|
||
|
if (uint32(len(f.data)) - f.position) < uint32(length) {
|
||
|
return returnString, errors.New("Not enough bytes to create a string")
|
||
|
}
|
||
|
for i := 0; i < length; i++ {
|
||
|
charIndex, err := f.GetInt()
|
||
|
if err != nil {
|
||
|
return returnString, err
|
||
|
}
|
||
|
charToAdd := string(possibleChars[charIndex%len(possibleChars)])
|
||
|
returnString = fmt.Sprintf(returnString + charToAdd)
|
||
|
}
|
||
|
return returnString, nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetRune() ([]rune, error) {
|
||
|
stringToConvert, err := f.GetString()
|
||
|
if err != nil {
|
||
|
return []rune("nil"), err
|
||
|
}
|
||
|
return []rune(stringToConvert), nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetFloat32() (float32, error) {
|
||
|
u32, err := f.GetNBytes(4)
|
||
|
if err != nil {
|
||
|
return float32(0.0), err
|
||
|
}
|
||
|
littleEndian, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
return float32(0.0), err
|
||
|
}
|
||
|
if littleEndian {
|
||
|
u32LE := binary.LittleEndian.Uint32(u32)
|
||
|
return math.Float32frombits(u32LE), nil
|
||
|
}
|
||
|
u32BE := binary.BigEndian.Uint32(u32)
|
||
|
return math.Float32frombits(u32BE), nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) GetFloat64() (float64, error) {
|
||
|
u64, err := f.GetNBytes(8)
|
||
|
if err != nil {
|
||
|
return float64(0.0), err
|
||
|
}
|
||
|
littleEndian, err := f.GetBool()
|
||
|
if err != nil {
|
||
|
return float64(0.0), err
|
||
|
}
|
||
|
if littleEndian {
|
||
|
u64LE := binary.LittleEndian.Uint64(u64)
|
||
|
return math.Float64frombits(u64LE), nil
|
||
|
}
|
||
|
u64BE := binary.BigEndian.Uint64(u64)
|
||
|
return math.Float64frombits(u64BE), nil
|
||
|
}
|
||
|
|
||
|
func (f *ConsumeFuzzer) CreateSlice(targetSlice interface{}) error {
|
||
|
err := f.GenerateStruct(targetSlice)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|