|
| 1 | +## Go protobuf mutator |
| 2 | + |
| 3 | +This is a go-protobuf-mutator library for random value mutations. This is a Go equivalent of [libprotobuf-mutator](https://github.com/google/libprotobuf-mutator), which is implemented in C++. |
| 4 | + |
| 5 | +### Supported Types |
| 6 | +- ProtoMessage |
| 7 | +- Bool |
| 8 | +- Float64 |
| 9 | +- Float32 |
| 10 | +- Uint32 |
| 11 | +- Uint64 |
| 12 | +- Int32 |
| 13 | +- Int64 |
| 14 | +- String |
| 15 | +- Bytes |
| 16 | + |
| 17 | +### Dependencies for libfuzzer |
| 18 | +- Go 1.21+ |
| 19 | +- Protocol Buffers compiler (`protoc`) |
| 20 | +- libprotobuf-dev |
| 21 | +- clang (for libFuzzer integration) |
| 22 | +- libprotobuf-mutator (v1.1) |
| 23 | + |
| 24 | +### Generating Protocol Buffer Files |
| 25 | + |
| 26 | +Generate Go code from your `.proto` files using the `protoc` compiler: |
| 27 | + |
| 28 | +```sh |
| 29 | +protoc --go_out=. --go-grpc_out=. testdata/example.proto |
| 30 | +``` |
| 31 | + |
| 32 | +This will generate both the standard protocol buffer code (`.pb.go`) and gRPC service definitions (`.grpc.pb.go`) if your proto file contains service definitions. |
| 33 | + |
| 34 | +**Resources:** |
| 35 | +- [Protocol Buffers Official Documentation](https://protobuf.dev/) |
| 36 | +- [Go Protocol Buffers Library](https://pkg.go.dev/google.golang.org/protobuf) |
| 37 | + |
| 38 | +### Basic Usage |
| 39 | +```go |
| 40 | +message := NewProtoMessage() |
| 41 | +mutator := mutator.New(int64(seed), maxSize) |
| 42 | +if err := mutator.MutateProto(message); err != nil { |
| 43 | + fmt.Printf("Failed to mutate message: %+v", err) |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +### Implementation Examples |
| 48 | + |
| 49 | +For complete reference implementations: |
| 50 | + |
| 51 | +| Feature | Location | Documentation | |
| 52 | +|-----------------------|-----------------------------------|-------------------------------------| |
| 53 | +| libFuzzer Integration | [`example/cmd/libfuzzer.go`](./example/cmd/libfuzzer.go) | [README](./example/README.md) | |
| 54 | +| Coverage Tracking | [`example/cmd/coverage.go`](./example/cmd/coverage.go) | Code comments | |
| 55 | + |
| 56 | +**Pro Tip**: The libFuzzer example includes: |
| 57 | +- Custom mutator setup |
| 58 | +- Seed corpus generation |
| 59 | +- Crash reproduction workflow |
| 60 | +- Performance benchmarking |
| 61 | + |
| 62 | +### Integration with libFuzzer |
| 63 | +Add `--tag libfuzzer` to build commands when using libFuzzer. |
| 64 | + |
| 65 | +#### Example Code |
| 66 | +```go |
| 67 | +// #include <stdint.h> |
| 68 | +import "C" |
| 69 | + |
| 70 | +//export LLVMFuzzerTestOneInput |
| 71 | +func LLVMFuzzerTestOneInput(data *C.char, size C.size_t) C.int { |
| 72 | + gdata := unsafe.Slice((*byte)(unsafe.Pointer(data)), size) |
| 73 | + message := NewProtoMessage(gdata) |
| 74 | + Fuzz(message) |
| 75 | + return 0 |
| 76 | +} |
| 77 | + |
| 78 | +//export LLVMFuzzerCustomMutator |
| 79 | +func LLVMFuzzerCustomMutator(data *C.char, size C.size_t, maxSize C.size_t, seed C.uint) C.size_t { |
| 80 | + gdata := unsafe.Slice((*byte)(unsafe.Pointer(data)), size) |
| 81 | + message := NewProtoMessage(gdata) |
| 82 | + |
| 83 | + mutator := mutator.New(int64(seed), int(maxSize-size)) |
| 84 | + if err := mutator.MutateProto(message); err != nil { |
| 85 | + return 0 |
| 86 | + } |
| 87 | + |
| 88 | + gdata = unsafe.Slice((*byte)(unsafe.Pointer(data)), maxSize) |
| 89 | + newSize := StoreMessage(gdata, message) |
| 90 | + return C.size_t(newSize) |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +#### Build Commands |
| 95 | +```sh |
| 96 | +go build -tags libfuzzer -buildmode c-archive -o fuzz.a ./cmd/fuzzing |
| 97 | +clang++ -o fuzz fuzz.a -fsanitize=fuzzer |
| 98 | +./fuzz ./corpus |
| 99 | +``` |
0 commit comments