Merge remote-tracking branch 'origin/master' into sf/base-type-impl

# Conflicts:
#	generator.go
This commit is contained in:
Evgeny Bukreev 2024-05-07 15:40:36 +03:00
commit 3c5b43322e
Failed to extract signature
3 changed files with 199 additions and 0 deletions

27
clusterfuzz/Dockerfile Normal file
View file

@ -0,0 +1,27 @@
FROM ubuntu:20.04
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update && \
apt-get upgrade -y && \
apt-get autoremove -y && \
apt-get install -y \
git \
python \
lsb-release \
locales \
sudo
RUN git clone https://github.com/google/clusterfuzz && \
cd clusterfuzz && \
git pull && \
git checkout tags/v2.6.0 && \
local/install_deps.bash
RUN pip --no-cache-dir install pipenv==2022.8.5
WORKDIR /clusterfuzz
CMD ["pipenv", "run", "python", "butler.py", "run_server", "--bootstrap"]
EXPOSE 9000

View file

@ -0,0 +1,57 @@
# Инструкция по использованию [clusterfuzz](https://github.com/google/clusterfuzz)
Основывается на:
* https://google.github.io/clusterfuzz/
* https://google.github.io/oss-fuzz/getting-started/new-project-guide/go-lang/
* https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/compile_native_go_fuzzer
## Поднятие локального инстанса
Работает на следующих ОС:
* Ubuntu (16.04, 17.10, 18.04, 18.10, 20.04)
* Debian 8 (jessie) or later
* Recent versions of macOS with homebrew (experimental)
* Note: Only x86 architectures are currently supported
```bash
git clone https://github.com/google/clusterfuzz
cd clusterfuzz
git pull
git checkout tags/v2.6.0
local/install_deps.bash
pipenv shell
python butler.py run_server --bootstrap
```
Флаг --bootstrap в последней команде необходим только при первом запуске локального сервера.
В выводе команды будет строка наподобие этой: `[INFO] Listening at: http://0.0.0.0:9000`.
По указанному адресу можно зайти на web интерфейс.
Далее необходимо поднять т.н. бота. Находясь в `pipenv shell` выполнить:
```bash
python butler.py run_bot --name my-bot ./my-bot
```
Для просмотра логов бота:
```bash
cd ./my-bot/clusterfuzz/bot/logs
tail -f bot.log
```
## Фаззинг проекта на go
```bash
git clone https://git.frostfs.info/TrueCloudLab/policy-engine.git
go get git.frostfs.info/TrueCloudLab/policy-engine
go install github.com/AdamKorcz/go-118-fuzz-build@latest
go get github.com/AdamKorcz/go-118-fuzz-build/testing
go-118-fuzz-build -tags gofuzz -o fuzz_unmarshall.a -func FuzzUnmarshal ~/policy-engine/pkg/chain
clang++ -fsanitize=address,fuzzer fuzz_unmarshall.a -o ~/policy-engine/out/fuzz_unmarshall
zip fuzzer.zip fuzz_unmarshall
```
Полученный архив следует загрузить через интерфейс web интерфейс clusterfuzz.
Важно, что фаззинг go использует под капотом libFuzzer, поэтому в названии job для фаззинга это должно быть указано.
Можно также использовать встроенный шаблон для libFuzzer.

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
"unsafe"
) )
type Generator struct { type Generator struct {
@ -119,6 +120,78 @@ func (g *Generator) fillAny(any reflect.Value) error {
any.Index(i).Set(arrayValue.Elem()) any.Index(i).Set(arrayValue.Elem())
} }
case reflect.Bool:
newBool, err := g.GenerateBool()
if err != nil {
return err
}
any.SetBool(newBool)
case reflect.Uint8:
newUInt8, err := g.GenerateUInt8()
if err != nil {
return err
}
any.SetUint(uint64(newUInt8))
case reflect.Uint16:
newUInt16, err := g.GenerateUInt16()
if err != nil {
return err
}
any.SetUint(uint64(newUInt16))
case reflect.Uint, reflect.Uint32:
newUInt32, err := g.GenerateUInt32()
if err != nil {
return err
}
any.SetUint(uint64(newUInt32))
case reflect.Uint64, reflect.Uintptr:
newUInt64, err := g.GenerateUInt64()
if err != nil {
return err
}
any.SetUint(newUInt64)
case reflect.Chan:
size, err := g.GenerateInt()
if err != nil {
return err
}
channel := reflect.MakeChan(any.Type(), size)
for i := 0; i < size; i++ {
elem := reflect.New(channel.Type().Elem()).Elem()
err = g.fillAny(elem)
if err != nil {
return err
}
channel.Send(elem)
}
any.Set(channel)
case reflect.Slice:
size, err := g.GenerateInt()
if err != nil {
return err
}
slice := reflect.MakeSlice(any.Type(), size, size)
for i := 0; i < size; i++ {
err := g.fillAny(slice.Index(i))
if err != nil {
return err
}
}
any.Set(slice)
case reflect.UnsafePointer:
addr, err := g.GenerateUInt64()
if err != nil {
return err
}
any.SetPointer(unsafe.Pointer(uintptr(addr)))
default: default:
panic("unhandled default case") panic("unhandled default case")
} }
@ -216,5 +289,47 @@ func (g *Generator) GenerateFloat64() (float64, error) {
result := math.Float64frombits(bits) result := math.Float64frombits(bits)
return result, nil
}
func (g *Generator) GenerateBool() (bool, error) {
if g.position >= g.dataSize {
return false, errors.New("the data bytes are over")
}
result := g.data[g.position]%2 == 0
g.position++
return result, nil
}
func (g *Generator) GenerateUInt8() (uint8, error) {
if g.position >= g.dataSize {
return 0, errors.New("the data bytes are over")
}
result := g.data[g.position]
g.position++
return result, nil
}
func (g *Generator) GenerateUInt16() (uint16, error) {
if g.position+1 >= g.dataSize {
return 0, errors.New("the data bytes are over")
}
result := uint16(0)
for i := 0; i < 2; i++ {
result = result<<8 | uint16(g.data[g.position])
g.position++
}
return result, nil
}
func (g *Generator) GenerateUInt64() (uint64, error) {
if g.position+7 >= g.dataSize {
return 0, errors.New("the data bytes are over")
}
result := uint64(0)
for i := 0; i < 8; i++ {
result = result<<8 | uint64(g.data[g.position])
g.position++
}
return result, nil return result, nil
} }