Add beacon state unmarshal fuzzer, afl support (#6625)

* Add AFL third_party libraries

* add beacon state fuzzing, add afl fuzz bundle

* rm fuzzing engine

* fix and lint

* Check for array out of bounds when calculating proposer delta

* failing test

* fix

* Checkpoint progress

* Add requirement that inclusion distance is not zero, add regression test

* No need for HTR since that is covered in process slots

* Removing some fuzzit logic, old fuzz tests

* Add ssz encoder test and fix

* Fuzzing checkpoint, adding fuzzing to the p2p layer

* ignore some libfuzzer files

* Full testing of p2p processing of blocks, with some mocked stuff

* use tmpdir and always process blocks

* use checkptr

* Update ethereumapis

* go mod tidy

* benchmarks for ferran's fast ssz hash tree root

* Update fastssz

* fmt

* gaz

* goimports

* Fix

* fix ethereumapis

* fix again

* kafka

* fix gen file

* fix compute signing root

* gofmt

* checkpoint progress

* progress

* checkpoint

* updates

* updates

* merge fix

* WIP

* merge

* fix build

* fix merge related issues

* cleanup

* revert unrelated

* lint

* lint

* lint

* manual tags for fuzz

* Commentary on upload script

* some import fixes, but not all

* fix //fuzz:fuzz_tests

* rm unused test

* update generated ssz

* Set // +build libfuzzer

* remove debug code

* A bit of refactoring ot explain why there is a committee_disabled file

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Preston Van Loon 2020-09-14 13:42:08 -05:00 committed by GitHub
parent e477df321c
commit cebb62997d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 11761 additions and 883 deletions

View File

@ -69,18 +69,12 @@ build:fuzz --copt=-fno-omit-frame-pointer
build:fuzz --define=FUZZING_ENGINE=libfuzzer
build:fuzz --copt=-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
build:fuzz --linkopt -Wl,--no-as-needed
build:fuzz --define=gc_goopts=-d=libfuzzer
build:fuzz --define=gc_goopts=-d=libfuzzer,checkptr
build:fuzz --run_under=//tools:fuzz_wrapper
build:fuzz --compilation_mode=opt
test:fuzz --local_test_jobs="HOST_CPUS*.5"
test:fuzzit --config=fuzz
test:fuzzit --test_env=FUZZIT_API_KEY
test:fuzzit --test_env=PRYSM_BUILD_IMAGE=gcr.io/prysmaticlabs/prysm-fuzzit:v0.11.0
test:fuzzit --test_timeout=1200
test:fuzzit --run_under=//tools:fuzzit_wrapper
# Build binary with cgo symbolizer for debugging / profiling.
build:cgo_symbolizer --config=llvm
build:cgo_symbolizer --copt=-g

4
.gitignore vendored
View File

@ -29,3 +29,7 @@ password.txt
# Dist files
dist
# libfuzzer
oom-*
crash-*

View File

@ -20,7 +20,10 @@ go_library(
"service.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/cache/depositcache:go_default_library",

View File

@ -6,14 +6,21 @@ go_library(
srcs = [
"attestation_data.go",
"checkpoint_state.go",
"committee.go",
"committees.go",
"common.go",
"doc.go",
"hot_state_cache.go",
"skip_slot_cache.go",
"state_summary.go",
"subnet_ids.go",
],
] + select({
"//fuzz:fuzzing_enabled": [
"committee_disabled.go",
],
"//conditions:default": [
"committee.go",
],
}),
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache",
visibility = [
"//beacon-chain:__subpackages__",

View File

@ -1,3 +1,5 @@
// -build libfuzzer
package cache
import (
@ -12,10 +14,6 @@ import (
)
var (
// ErrNotCommittee will be returned when a cache object is not a pointer to
// a Committee struct.
ErrNotCommittee = errors.New("object is not a committee struct")
// maxCommitteesCacheSize defines the max number of shuffled committees on per randao basis can cache.
// Due to reorgs and long finality, it's good to keep the old cache around for quickly switch over.
maxCommitteesCacheSize = uint64(32)
@ -32,15 +30,6 @@ var (
})
)
// Committees defines the shuffled committees seed.
type Committees struct {
CommitteeCount uint64
Seed [32]byte
ShuffledIndices []uint64
SortedIndices []uint64
ProposerIndices []uint64
}
// CommitteeCache is a struct with 1 queue for looking up shuffled indices list by seed.
type CommitteeCache struct {
CommitteeCache *cache.FIFO
@ -216,6 +205,12 @@ func (c *CommitteeCache) ProposerIndices(seed [32]byte) ([]uint64, error) {
return item.ProposerIndices, nil
}
// HasEntry returns true if the committee cache has a value.
func (c *CommitteeCache) HasEntry(seed string) bool {
_, ok, err := c.CommitteeCache.GetByKey(seed)
return err == nil && ok
}
func startEndIndices(c *Committees, index uint64) (uint64, uint64) {
validatorCount := uint64(len(c.ShuffledIndices))
start := sliceutil.SplitOffset(validatorCount, c.CommitteeCount, index)

View File

@ -0,0 +1,50 @@
// +build libfuzzer
// This file is used in fuzzer builds to bypass global committee caches.
package cache
// FakeCommitteeCache is a struct with 1 queue for looking up shuffled indices list by seed.
type FakeCommitteeCache struct {
}
// NewCommitteesCache creates a new committee cache for storing/accessing shuffled indices of a committee.
func NewCommitteesCache() *FakeCommitteeCache {
return &FakeCommitteeCache{}
}
// Committee fetches the shuffled indices by slot and committee index. Every list of indices
// represent one committee. Returns true if the list exists with slot and committee index. Otherwise returns false, nil.
func (c *FakeCommitteeCache) Committee(slot uint64, seed [32]byte, index uint64) ([]uint64, error) {
return nil, nil
}
// AddCommitteeShuffledList adds Committee shuffled list object to the cache. T
// his method also trims the least recently list if the cache size has ready the max cache size limit.
func (c *FakeCommitteeCache) AddCommitteeShuffledList(committees *Committees) error {
return nil
}
// AddProposerIndicesList updates the committee shuffled list with proposer indices.
func (c *FakeCommitteeCache) AddProposerIndicesList(seed [32]byte, indices []uint64) error {
return nil
}
// ActiveIndices returns the active indices of a given seed stored in cache.
func (c *FakeCommitteeCache) ActiveIndices(seed [32]byte) ([]uint64, error) {
return nil, nil
}
// ActiveIndicesCount returns the active indices count of a given seed stored in cache.
func (c *FakeCommitteeCache) ActiveIndicesCount(seed [32]byte) (int, error) {
return 0, nil
}
// ProposerIndices returns the proposer indices of a given seed.
func (c *FakeCommitteeCache) ProposerIndices(seed [32]byte) ([]uint64, error) {
return nil, nil
}
// HasEntry returns true if the committee cache has a value.
func (c *FakeCommitteeCache) HasEntry(string) bool {
return false
}

16
beacon-chain/cache/committees.go vendored Normal file
View File

@ -0,0 +1,16 @@
package cache
import "errors"
// ErrNotCommittee will be returned when a cache object is not a pointer to
// a Committee struct.
var ErrNotCommittee = errors.New("object is not a committee struct")
// Committees defines the shuffled committees seed.
type Committees struct {
CommitteeCount uint64
Seed [32]byte
ShuffledIndices []uint64
SortedIndices []uint64
ProposerIndices []uint64
}

View File

@ -18,12 +18,13 @@ go_library(
visibility = [
"//beacon-chain:__subpackages__",
"//endtoend/evaluators:__pkg__",
"//fuzz:__pkg__",
"//shared/attestationutil:__pkg__",
"//shared/benchutil/benchmark_files:__subpackages__",
"//shared/depositutil:__pkg__",
"//shared/interop:__pkg__",
"//shared/keystore:__pkg__",
"//shared/depositutil:__pkg__",
"//shared/p2putils:__pkg__",
"//shared/attestationutil:__pkg__",
"//shared/testutil:__pkg__",
"//slasher:__subpackages__",
"//tools:__subpackages__",

View File

@ -312,7 +312,8 @@ func UpdateCommitteeCache(state *stateTrie.BeaconState, epoch uint64) error {
if err != nil {
return err
}
if _, exists, err := committeeCache.CommitteeCache.GetByKey(string(seed[:])); err == nil && exists {
if committeeCache.HasEntry(string(seed[:])) {
return nil
}

View File

@ -23,6 +23,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db",
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
"//tools:__subpackages__",
],
deps = [

View File

@ -14,7 +14,10 @@ go_library(
"types.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//shared/params:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@ -12,7 +12,10 @@ go_library(
"service.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/operations/attestations/kv:go_default_library",
"//beacon-chain/state:go_default_library",

View File

@ -11,7 +11,10 @@ go_library(
"types.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",

View File

@ -8,7 +8,10 @@ go_library(
"service.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/state:go_default_library",

View File

@ -12,6 +12,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder",
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//shared/params:go_default_library",

View File

@ -4,13 +4,17 @@ go_library(
name = "go_default_library",
testonly = True,
srcs = [
"fuzz_p2p.go",
"mock_broadcaster.go",
"mock_peermanager.go",
"mock_peersprovider.go",
"p2p.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/p2p/encoder:go_default_library",
"//beacon-chain/p2p/peers:go_default_library",

View File

@ -0,0 +1,163 @@
package testing
import (
"context"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/gogo/protobuf/proto"
"github.com/libp2p/go-libp2p-core/control"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/multiformats/go-multiaddr"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// FakeP2P stack
type FakeP2P struct {
}
// NewFuzzTestP2P - Create a new fake p2p stack.
func NewFuzzTestP2P() *FakeP2P {
return &FakeP2P{}
}
// Encoding -- fake.
func (p *FakeP2P) Encoding() encoder.NetworkEncoding {
return &encoder.SszNetworkEncoder{}
}
// AddConnectionHandler -- fake.
func (p *FakeP2P) AddConnectionHandler(f func(ctx context.Context, id peer.ID) error) {
}
// AddDisconnectionHandler -- fake.
func (p *FakeP2P) AddDisconnectionHandler(f func(ctx context.Context, id peer.ID) error) {
}
// AddPingMethod -- fake.
func (p *FakeP2P) AddPingMethod(reqFunc func(ctx context.Context, id peer.ID) error) {
}
// PeerID -- fake.
func (p *FakeP2P) PeerID() peer.ID {
return peer.ID("fake")
}
// ENR returns the enr of the local peer.
func (p *FakeP2P) ENR() *enr.Record {
return new(enr.Record)
}
// FindPeersWithSubnet mocks the p2p func.
func (p *FakeP2P) FindPeersWithSubnet(ctx context.Context, index uint64) (bool, error) {
return false, nil
}
// RefreshENR mocks the p2p func.
func (p *FakeP2P) RefreshENR() {
return
}
// LeaveTopic -- fake.
func (p *FakeP2P) LeaveTopic(topic string) error {
return nil
}
// Metadata -- fake.
func (p *FakeP2P) Metadata() *pb.MetaData {
return nil
}
// Peers -- fake.
func (p *FakeP2P) Peers() *peers.Status {
return nil
}
// PublishToTopic -- fake.
func (p *FakeP2P) PublishToTopic(ctx context.Context, topic string, data []byte, opts ...pubsub.PubOpt) error {
return nil
}
// Send -- fake.
func (p *FakeP2P) Send(ctx context.Context, msg interface{}, topic string, pid peer.ID) (network.Stream, error) {
return nil, nil
}
// PubSub -- fake.
func (p *FakeP2P) PubSub() *pubsub.PubSub {
return nil
}
// MetadataSeq -- fake.
func (p *FakeP2P) MetadataSeq() uint64 {
return 0
}
// SetStreamHandler -- fake.
func (p *FakeP2P) SetStreamHandler(topic string, handler network.StreamHandler) {
}
// SubscribeToTopic -- fake.
func (p *FakeP2P) SubscribeToTopic(topic string, opts ...pubsub.SubOpt) (*pubsub.Subscription, error) {
return nil, nil
}
// JoinTopic -- fake.
func (p *FakeP2P) JoinTopic(topic string, opts ...pubsub.TopicOpt) (*pubsub.Topic, error) {
return nil, nil
}
// Host -- fake.
func (p *FakeP2P) Host() host.Host {
return nil
}
// Disconnect -- fake.
func (p *FakeP2P) Disconnect(pid peer.ID) error {
return nil
}
// Broadcast -- fake.
func (p *FakeP2P) Broadcast(ctx context.Context, msg proto.Message) error {
return nil
}
// BroadcastAttestation -- fake.
func (p *FakeP2P) BroadcastAttestation(ctx context.Context, subnet uint64, att *ethpb.Attestation) error {
return nil
}
// InterceptPeerDial -- fake.
func (p *FakeP2P) InterceptPeerDial(peer.ID) (allow bool) {
return true
}
// InterceptAddrDial -- fake.
func (p *FakeP2P) InterceptAddrDial(peer.ID, multiaddr.Multiaddr) (allow bool) {
return true
}
// InterceptAccept -- fake.
func (p *FakeP2P) InterceptAccept(n network.ConnMultiaddrs) (allow bool) {
return true
}
// InterceptSecured -- fake.
func (p *FakeP2P) InterceptSecured(network.Direction, peer.ID, network.ConnMultiaddrs) (allow bool) {
return true
}
// InterceptUpgraded -- fake.
func (p *FakeP2P) InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) {
return true, 0
}

View File

@ -15,7 +15,10 @@ go_library(
"setter.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen",
visibility = ["//beacon-chain:__subpackages__"],
visibility = [
"//beacon-chain:__subpackages__",
"//fuzz:__pkg__",
],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/helpers:go_default_library",

View File

@ -8,6 +8,7 @@ go_library(
"decode_pubsub.go",
"doc.go",
"error.go",
"fuzz_exports.go", # keep
"log.go",
"metrics.go",
"pending_attestations_queue.go",

View File

@ -0,0 +1,54 @@
// +build libfuzzer
package sync
import (
"context"
"github.com/gogo/protobuf/proto"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
)
// NewRegularSyncFuzz service without registering handlers.
func NewRegularSyncFuzz(cfg *Config) *Service {
rLimiter := newRateLimiter(cfg.P2P)
ctx, cancel := context.WithCancel(context.Background())
r := &Service{
ctx: ctx,
cancel: cancel,
db: cfg.DB,
p2p: cfg.P2P,
attPool: cfg.AttPool,
exitPool: cfg.ExitPool,
slashingPool: cfg.SlashingPool,
chain: cfg.Chain,
initialSync: cfg.InitialSync,
attestationNotifier: cfg.AttestationNotifier,
slotToPendingBlocks: make(map[uint64][]*ethpb.SignedBeaconBlock),
seenPendingBlocks: make(map[[32]byte]bool),
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
stateNotifier: cfg.StateNotifier,
blockNotifier: cfg.BlockNotifier,
stateSummaryCache: cfg.StateSummaryCache,
stateGen: cfg.StateGen,
rateLimiter: rLimiter,
}
return r
}
// FuzzValidateBeaconBlockPubSub exports private method validateBeaconBlockPubSub for fuzz testing.
func (s *Service) FuzzValidateBeaconBlockPubSub(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
return s.validateBeaconBlockPubSub(ctx, pid, msg)
}
// FuzzBeaconBlockSubscriber exports private method beaconBlockSubscriber for fuzz testing.
func (s *Service) FuzzBeaconBlockSubscriber(ctx context.Context, msg proto.Message) error {
return s.beaconBlockSubscriber(ctx, msg)
}
func (s *Service) InitCaches() error {
return s.initCaches()
}

View File

@ -2,9 +2,15 @@ load("@prysm//tools/go:def.bzl", "go_library")
load("//tools/go:fuzz.bzl", "go_fuzz_test")
load("@com_github_prysmaticlabs_ethereumapis//tools:ssz.bzl", "SSZ_DEPS", "ssz_gen_marshal")
load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push")
load("@io_bazel_rules_go//go:def.bzl", "go_test")
# gazelle:ignore generated.ssz.go
config_setting(
name = "fuzzing_enabled",
values = {"define": "gotags=libfuzzer"},
)
ssz_gen_marshal(
name = "ssz_generated_files",
srcs = ["inputs.go"],
@ -13,36 +19,10 @@ ssz_gen_marshal(
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
objs = [
"InputBlockHeader",
"InputAttesterSlashingWrapper",
"InputAttestationWrapper",
"InputDepositWrapper",
"InputVoluntaryExitWrapper",
"InputProposerSlashingWrapper",
"InputBlockWithPrestate",
],
)
container_image(
name = "prysm_fuzzit",
base = "@fuzzit_base//image",
directory = "/beacon_states",
env = {
"BEACONSTATES_PATH": "/beacon_states",
},
files = [
"@sigp_beacon_fuzz_corpora//:current_mainnet_beaconstate",
],
)
container_push(
name = "push_image",
format = "Docker",
image = ":prysm_fuzzit",
registry = "gcr.io",
repository = "prysmaticlabs/prysm-fuzzit",
tag = "v0.11.0",
)
IMPORT_PATH = "github.com/prysmaticlabs/prysm/fuzz"
COMMON_DEPS = [
@ -64,49 +44,12 @@ test_suite(
name = "fuzz_tests",
tags = ["manual"],
tests = [
":attestation_fuzz_test_with_libfuzzer",
":attester_slashing_fuzz_test_with_libfuzzer",
":block_fuzz_test_with_libfuzzer",
":block_header_fuzz_test_with_libfuzzer",
":deposit_fuzz_test_with_libfuzzer",
":proposer_slashing_fuzz_test_with_libfuzzer",
":rpc_status_fuzz_test_with_libfuzzer",
":voluntary_exit_fuzz_test_with_libfuzzer",
":state_fuzz_test_with_libfuzzer",
],
)
go_fuzz_test(
name = "attestation_fuzz_test",
srcs = [
"attestation_fuzz.go",
] + COMMON_SRCS,
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_attestation",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/attestation",
func = "BeaconFuzzAttestation",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "attester_slashing_fuzz_test",
srcs = [
"attester_slashing_fuzz.go",
] + COMMON_SRCS,
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_attester_slashing",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/attester_slashing",
func = "BeaconFuzzAttesterSlashing",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "block_fuzz_test",
srcs = [
@ -116,59 +59,31 @@ go_fuzz_test(
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/block_header",
func = "BeaconFuzzBlock",
importpath = IMPORT_PATH,
max_len = 30000000,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "block_header_fuzz_test",
srcs = [
"block_header_fuzz.go",
] + COMMON_SRCS,
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_block_header",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/block_header",
func = "BeaconFuzzBlockHeader",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "deposit_fuzz_test",
srcs = [
"deposit_fuzz.go",
] + COMMON_SRCS,
corpus = "deposit_corpus",
corpus_path = "fuzz/deposit_corpus",
func = "BeaconFuzzDeposit",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "proposer_slashing_fuzz_test",
srcs = [
"proposer_slashing_fuzz.go",
] + COMMON_SRCS,
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_proposer_slashing",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/proposer_slashing",
func = "BeaconFuzzProposerSlashing",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//shared/params:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/sync:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/forkchoice/protoarray:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
"@com_github_libp2p_go_libp2p_pubsub//:go_default_library",
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//shared/rand:go_default_library",
] + COMMON_DEPS,
)
@ -200,17 +115,35 @@ go_fuzz_test(
)
go_fuzz_test(
name = "voluntary_exit_fuzz_test",
name = "ssz_encoder_attestations_test",
srcs = [
"voluntary_exit_fuzz.go",
"ssz_encoder_attestations_fuzz.go",
] + COMMON_SRCS,
corpus = "voluntary_exit_corpus",
corpus_path = "fuzz/voluntary_exit_corpus",
func = "BeaconFuzzVoluntaryExit",
corpus = "@sigp_beacon_fuzz_corpora//:current_mainnet_block_header",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/block_header",
func = "SszEncoderAttestationFuzz",
importpath = IMPORT_PATH,
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//fuzz/testing:go_default_library",
"//beacon-chain/p2p/encoder:go_default_library",
"//shared/params:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
] + COMMON_DEPS,
)
go_fuzz_test(
name = "state_fuzz_test",
srcs = [
"state_fuzz.go",
] + COMMON_SRCS,
corpus = "@sigp_beacon_fuzz_corpora//:0_11_0_mainnet_beaconstate",
corpus_path = "external/sigp_beacon_fuzz_corpora/0-11-0/mainnet/beaconstate",
func = "BeaconStateFuzz",
importpath = IMPORT_PATH,
max_len = 30000000,
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//shared/params:go_default_library",
] + COMMON_DEPS,
)
@ -219,41 +152,64 @@ go_library(
name = "go_default_library",
testonly = 1,
srcs = [
"attestation_fuzz.go",
"attester_slashing_fuzz.go",
"block_fuzz.go",
"block_header_fuzz.go",
"common.go",
"deposit_fuzz.go",
"inputs.go",
"rpc_status_fuzz.go",
"voluntary_exit_fuzz.go",
"ssz_encoder_attestations_fuzz.go",
"state_fuzz.go",
":ssz_generated_files", # keep
],
importpath = "github.com/prysmaticlabs/prysm/fuzz",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/forkchoice/protoarray:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/p2p/encoder:go_default_library",
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/sync:go_default_library",
"//beacon-chain/sync/initial-sync/testing:go_default_library",
"//fuzz/testing:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/params:go_default_library",
"//shared/bytesutil:go_default_library",
"@com_github_libp2p_go_libp2p//:go_default_library",
"//shared/rand:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_libp2p_go_libp2p_core//host:go_default_library",
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
"@com_github_libp2p_go_libp2p_pubsub//:go_default_library",
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
"@com_github_libp2p_go_libp2p//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
] + SSZ_DEPS, # keep
)
go_library(
name = "inputs",
srcs = [
"inputs.go",
":ssz_generated_files", # keep
],
importpath = "github.com/prysmaticlabs/prysm/fuzz",
visibility = ["//visibility:public"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
] + SSZ_DEPS, # keep
)

View File

@ -23,7 +23,7 @@ deserialize to input objects.
_Example: Block header input data_
```go
type InputBlockHeader struct {
type InputBlockWithPrestate struct {
StateID uint16
Block *ethpb.BeaconBlock
}

View File

@ -1,33 +0,0 @@
package fuzz
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzAttestation implements libfuzzer and beacon fuzz interface.
func BeaconFuzzAttestation(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputAttestationWrapper{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
post, err := blocks.ProcessAttestationNoVerifySignature(context.Background(), st, input.Attestation)
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -1,38 +0,0 @@
package fuzz
import (
"context"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzAttesterSlashing implements libfuzzer and beacon fuzz interface.
func BeaconFuzzAttesterSlashing(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputAttesterSlashingWrapper{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
block := &ethpb.SignedBeaconBlock{
Block: &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{AttesterSlashings: []*ethpb.AttesterSlashing{input.AttesterSlashing}},
},
}
post, err := blocks.ProcessAttesterSlashings(context.Background(), st, block)
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -1,33 +1,195 @@
// +build libfuzzer
package fuzz
import (
"bytes"
"context"
"encoding/hex"
"io/ioutil"
"os"
"path"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
p2pt "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/sirupsen/logrus"
)
// BeaconFuzzBlock using the corpora from sigp/beacon-fuzz.
func BeaconFuzzBlock(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputBlockHeader{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
const topic = p2p.BlockSubnetTopicFormat
var db1 db.Database
var ssc *cache.StateSummaryCache
var dbPath = path.Join(os.TempDir(), "fuzz_beacondb", randomHex(6))
func randomHex(n int) string {
bytes := make([]byte, n)
if _, err := rand.NewGenerator().Read(bytes); err != nil {
panic(err)
}
return hex.EncodeToString(bytes)
}
func init() {
featureconfig.Init(&featureconfig.Flags{SkipBLSVerify: true})
logrus.SetLevel(logrus.PanicLevel)
logrus.SetOutput(ioutil.Discard)
ssc = cache.NewStateSummaryCache()
var err error
db1, err = db.NewDB(dbPath, ssc)
if err != nil {
panic(err)
}
}
func setupDB() {
if err := db1.ClearDB(); err != nil {
_ = err
}
ctx := context.Background()
s := testutil.NewBeaconState()
b := testutil.NewBeaconBlock()
if err := db1.SaveBlock(ctx, b); err != nil {
panic(err)
}
br, err := ssz.HashTreeRoot(b)
if err != nil {
panic(err)
}
if err := db1.SaveState(ctx, s, br); err != nil {
panic(err)
}
if err := db1.SaveGenesisBlockRoot(ctx, br); err != nil {
panic(err)
}
}
type fakeChecker struct{}
func (fakeChecker) Syncing() bool {
return false
}
func (fakeChecker) Status() error {
return nil
}
func (fakeChecker) Resync() error {
return nil
}
// BeaconFuzzBlock runs full processing of beacon block against a given state.
func BeaconFuzzBlock(b []byte) {
params.UseMainnetConfig()
input := &InputBlockWithPrestate{}
if err := input.UnmarshalSSZ(b); err != nil {
return
}
st, err := stateTrie.InitializeFromProtoUnsafe(input.State)
if err != nil {
return
}
setupDB()
p2p := p2pt.NewFuzzTestP2P()
sgen := stategen.New(db1, ssc)
sn := &testing.MockStateNotifier{}
bn := &testing.MockBlockNotifier{}
an := &testing.MockOperationNotifier{}
ap := attestations.NewPool()
ep := voluntaryexits.NewPool()
sp := slashings.NewPool()
ops, err := attestations.NewService(context.Background(), &attestations.Config{Pool: ap})
if err != nil {
panic(err)
}
chain, err := blockchain.NewService(context.Background(), &blockchain.Config{
ChainStartFetcher: nil,
BeaconDB: db1,
DepositCache: nil,
AttPool: ap,
ExitPool: ep,
SlashingPool: sp,
P2p: p2p,
StateNotifier: sn,
ForkChoiceStore: protoarray.New(0, 0, [32]byte{}),
OpsService: ops,
StateGen: sgen,
})
if err != nil {
panic(err)
}
chain.Start()
s := sync.NewRegularSyncFuzz(&sync.Config{
DB: db1,
P2P: p2p,
Chain: chain,
InitialSync: fakeChecker{},
StateNotifier: sn,
BlockNotifier: bn,
AttestationNotifier: an,
AttPool: ap,
ExitPool: ep,
SlashingPool: sp,
StateSummaryCache: ssc,
StateGen: sgen,
})
if err := s.InitCaches(); err != nil {
panic(err)
}
buf := new(bytes.Buffer)
_, err = p2p.Encoding().EncodeGossip(buf, input.Block)
if err != nil {
panic(err)
}
ctx := context.Background()
pid := peer.ID("fuzz")
msg := &pubsub.Message{
Message: &pubsub_pb.Message{
Data: buf.Bytes(),
TopicIDs: []string{topic},
},
}
if res := s.FuzzValidateBeaconBlockPubSub(ctx, pid, msg); res != pubsub.ValidationAccept {
return
}
if err := s.FuzzBeaconBlockSubscriber(ctx, msg); err != nil {
_ = err
}
if _, err := state.ProcessBlock(ctx, st, input.Block); err != nil {
_ = err
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
post, err := state.ProcessBlock(context.Background(), st, &ethpb.SignedBeaconBlock{Block: input.Block})
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -1,30 +0,0 @@
package fuzz
import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzBlockHeader using the corpora from sigp/beacon-fuzz.
func BeaconFuzzBlockHeader(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputBlockHeader{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
post, err := blocks.ProcessBlockHeaderNoVerify(st, input.Block)
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -1,30 +0,0 @@
package fuzz
import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzDeposit implements libfuzzer and beacon fuzz interface.
func BeaconFuzzDeposit(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputDepositWrapper{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
post, err := blocks.ProcessDeposit(st, input.Deposit, true)
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -4,28 +4,38 @@ package fuzz
import (
ssz "github.com/ferranbt/fastssz"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// MarshalSSZ ssz marshals the InputBlockHeader object
func (i *InputBlockHeader) MarshalSSZ() ([]byte, error) {
// MarshalSSZ ssz marshals the InputBlockWithPrestate object
func (i *InputBlockWithPrestate) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputBlockHeader object to a target array
func (i *InputBlockHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) {
// MarshalSSZTo ssz marshals the InputBlockWithPrestate object to a target array
func (i *InputBlockWithPrestate) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
offset := int(12)
offset := int(8)
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Offset (0) 'State'
dst = ssz.WriteOffset(dst, offset)
if i.State == nil {
i.State = new(pb.BeaconState)
}
offset += i.State.SizeSSZ()
// Offset (1) 'Block'
dst = ssz.WriteOffset(dst, offset)
if i.Block == nil {
i.Block = new(ethpb.BeaconBlock)
i.Block = new(ethpb.SignedBeaconBlock)
}
offset += i.Block.SizeSSZ()
// Field (0) 'State'
if dst, err = i.State.MarshalSSZTo(dst); err != nil {
return
}
// Field (1) 'Block'
if dst, err = i.Block.MarshalSSZTo(dst); err != nil {
return
@ -34,30 +44,43 @@ func (i *InputBlockHeader) MarshalSSZTo(buf []byte) (dst []byte, err error) {
return
}
// UnmarshalSSZ ssz unmarshals the InputBlockHeader object
func (i *InputBlockHeader) UnmarshalSSZ(buf []byte) error {
// UnmarshalSSZ ssz unmarshals the InputBlockWithPrestate object
func (i *InputBlockWithPrestate) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 12 {
if size < 8 {
return ssz.ErrSize
}
tail := buf
var o1 uint64
var o0, o1 uint64
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Offset (0) 'State'
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
return ssz.ErrOffset
}
// Offset (1) 'Block'
if o1 = ssz.ReadOffset(buf[8:12]); o1 > size {
if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
return ssz.ErrOffset
}
// Field (0) 'State'
{
buf = tail[o0:o1]
if i.State == nil {
i.State = new(pb.BeaconState)
}
if err = i.State.UnmarshalSSZ(buf); err != nil {
return err
}
}
// Field (1) 'Block'
{
buf = tail[o1:]
if i.Block == nil {
i.Block = new(ethpb.BeaconBlock)
i.Block = new(ethpb.SignedBeaconBlock)
}
if err = i.Block.UnmarshalSSZ(buf); err != nil {
return err
@ -66,30 +89,38 @@ func (i *InputBlockHeader) UnmarshalSSZ(buf []byte) error {
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputBlockHeader object
func (i *InputBlockHeader) SizeSSZ() (size int) {
size = 12
// SizeSSZ returns the ssz encoded size in bytes for the InputBlockWithPrestate object
func (i *InputBlockWithPrestate) SizeSSZ() (size int) {
size = 8
// Field (0) 'State'
if i.State == nil {
i.State = new(pb.BeaconState)
}
size += i.State.SizeSSZ()
// Field (1) 'Block'
if i.Block == nil {
i.Block = new(ethpb.BeaconBlock)
i.Block = new(ethpb.SignedBeaconBlock)
}
size += i.Block.SizeSSZ()
return
}
// HashTreeRoot ssz hashes the InputBlockHeader object
func (i *InputBlockHeader) HashTreeRoot() ([32]byte, error) {
// HashTreeRoot ssz hashes the InputBlockWithPrestate object
func (i *InputBlockWithPrestate) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputBlockHeader object with a hasher
func (i *InputBlockHeader) HashTreeRootWith(hh *ssz.Hasher) (err error) {
// HashTreeRootWith ssz hashes the InputBlockWithPrestate object with a hasher
func (i *InputBlockWithPrestate) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (0) 'State'
if err = i.State.HashTreeRootWith(hh); err != nil {
return
}
// Field (1) 'Block'
if err = i.Block.HashTreeRootWith(hh); err != nil {
@ -99,407 +130,3 @@ func (i *InputBlockHeader) HashTreeRootWith(hh *ssz.Hasher) (err error) {
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the InputAttesterSlashingWrapper object
func (i *InputAttesterSlashingWrapper) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputAttesterSlashingWrapper object to a target array
func (i *InputAttesterSlashingWrapper) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
offset := int(12)
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Offset (1) 'AttesterSlashing'
dst = ssz.WriteOffset(dst, offset)
if i.AttesterSlashing == nil {
i.AttesterSlashing = new(ethpb.AttesterSlashing)
}
offset += i.AttesterSlashing.SizeSSZ()
// Field (1) 'AttesterSlashing'
if dst, err = i.AttesterSlashing.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the InputAttesterSlashingWrapper object
func (i *InputAttesterSlashingWrapper) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 12 {
return ssz.ErrSize
}
tail := buf
var o1 uint64
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Offset (1) 'AttesterSlashing'
if o1 = ssz.ReadOffset(buf[8:12]); o1 > size {
return ssz.ErrOffset
}
// Field (1) 'AttesterSlashing'
{
buf = tail[o1:]
if i.AttesterSlashing == nil {
i.AttesterSlashing = new(ethpb.AttesterSlashing)
}
if err = i.AttesterSlashing.UnmarshalSSZ(buf); err != nil {
return err
}
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputAttesterSlashingWrapper object
func (i *InputAttesterSlashingWrapper) SizeSSZ() (size int) {
size = 12
// Field (1) 'AttesterSlashing'
if i.AttesterSlashing == nil {
i.AttesterSlashing = new(ethpb.AttesterSlashing)
}
size += i.AttesterSlashing.SizeSSZ()
return
}
// HashTreeRoot ssz hashes the InputAttesterSlashingWrapper object
func (i *InputAttesterSlashingWrapper) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputAttesterSlashingWrapper object with a hasher
func (i *InputAttesterSlashingWrapper) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (1) 'AttesterSlashing'
if err = i.AttesterSlashing.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the InputAttestationWrapper object
func (i *InputAttestationWrapper) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputAttestationWrapper object to a target array
func (i *InputAttestationWrapper) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
offset := int(12)
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Offset (1) 'Attestation'
dst = ssz.WriteOffset(dst, offset)
if i.Attestation == nil {
i.Attestation = new(ethpb.Attestation)
}
offset += i.Attestation.SizeSSZ()
// Field (1) 'Attestation'
if dst, err = i.Attestation.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the InputAttestationWrapper object
func (i *InputAttestationWrapper) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size < 12 {
return ssz.ErrSize
}
tail := buf
var o1 uint64
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Offset (1) 'Attestation'
if o1 = ssz.ReadOffset(buf[8:12]); o1 > size {
return ssz.ErrOffset
}
// Field (1) 'Attestation'
{
buf = tail[o1:]
if i.Attestation == nil {
i.Attestation = new(ethpb.Attestation)
}
if err = i.Attestation.UnmarshalSSZ(buf); err != nil {
return err
}
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputAttestationWrapper object
func (i *InputAttestationWrapper) SizeSSZ() (size int) {
size = 12
// Field (1) 'Attestation'
if i.Attestation == nil {
i.Attestation = new(ethpb.Attestation)
}
size += i.Attestation.SizeSSZ()
return
}
// HashTreeRoot ssz hashes the InputAttestationWrapper object
func (i *InputAttestationWrapper) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputAttestationWrapper object with a hasher
func (i *InputAttestationWrapper) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (1) 'Attestation'
if err = i.Attestation.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the InputDepositWrapper object
func (i *InputDepositWrapper) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputDepositWrapper object to a target array
func (i *InputDepositWrapper) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Field (1) 'Deposit'
if i.Deposit == nil {
i.Deposit = new(ethpb.Deposit)
}
if dst, err = i.Deposit.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the InputDepositWrapper object
func (i *InputDepositWrapper) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size != 1248 {
return ssz.ErrSize
}
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Field (1) 'Deposit'
if i.Deposit == nil {
i.Deposit = new(ethpb.Deposit)
}
if err = i.Deposit.UnmarshalSSZ(buf[8:1248]); err != nil {
return err
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputDepositWrapper object
func (i *InputDepositWrapper) SizeSSZ() (size int) {
size = 1248
return
}
// HashTreeRoot ssz hashes the InputDepositWrapper object
func (i *InputDepositWrapper) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputDepositWrapper object with a hasher
func (i *InputDepositWrapper) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (1) 'Deposit'
if err = i.Deposit.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the InputVoluntaryExitWrapper object
func (i *InputVoluntaryExitWrapper) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputVoluntaryExitWrapper object to a target array
func (i *InputVoluntaryExitWrapper) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Field (1) 'VoluntaryExit'
if i.VoluntaryExit == nil {
i.VoluntaryExit = new(ethpb.VoluntaryExit)
}
if dst, err = i.VoluntaryExit.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the InputVoluntaryExitWrapper object
func (i *InputVoluntaryExitWrapper) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size != 24 {
return ssz.ErrSize
}
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Field (1) 'VoluntaryExit'
if i.VoluntaryExit == nil {
i.VoluntaryExit = new(ethpb.VoluntaryExit)
}
if err = i.VoluntaryExit.UnmarshalSSZ(buf[8:24]); err != nil {
return err
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputVoluntaryExitWrapper object
func (i *InputVoluntaryExitWrapper) SizeSSZ() (size int) {
size = 24
return
}
// HashTreeRoot ssz hashes the InputVoluntaryExitWrapper object
func (i *InputVoluntaryExitWrapper) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputVoluntaryExitWrapper object with a hasher
func (i *InputVoluntaryExitWrapper) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (1) 'VoluntaryExit'
if err = i.VoluntaryExit.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}
// MarshalSSZ ssz marshals the InputProposerSlashingWrapper object
func (i *InputProposerSlashingWrapper) MarshalSSZ() ([]byte, error) {
return ssz.MarshalSSZ(i)
}
// MarshalSSZTo ssz marshals the InputProposerSlashingWrapper object to a target array
func (i *InputProposerSlashingWrapper) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = buf
// Field (0) 'StateID'
dst = ssz.MarshalUint64(dst, i.StateID)
// Field (1) 'ProposerSlashing'
if i.ProposerSlashing == nil {
i.ProposerSlashing = new(ethpb.ProposerSlashing)
}
if dst, err = i.ProposerSlashing.MarshalSSZTo(dst); err != nil {
return
}
return
}
// UnmarshalSSZ ssz unmarshals the InputProposerSlashingWrapper object
func (i *InputProposerSlashingWrapper) UnmarshalSSZ(buf []byte) error {
var err error
size := uint64(len(buf))
if size != 424 {
return ssz.ErrSize
}
// Field (0) 'StateID'
i.StateID = ssz.UnmarshallUint64(buf[0:8])
// Field (1) 'ProposerSlashing'
if i.ProposerSlashing == nil {
i.ProposerSlashing = new(ethpb.ProposerSlashing)
}
if err = i.ProposerSlashing.UnmarshalSSZ(buf[8:424]); err != nil {
return err
}
return err
}
// SizeSSZ returns the ssz encoded size in bytes for the InputProposerSlashingWrapper object
func (i *InputProposerSlashingWrapper) SizeSSZ() (size int) {
size = 424
return
}
// HashTreeRoot ssz hashes the InputProposerSlashingWrapper object
func (i *InputProposerSlashingWrapper) HashTreeRoot() ([32]byte, error) {
return ssz.HashWithDefaultHasher(i)
}
// HashTreeRootWith ssz hashes the InputProposerSlashingWrapper object with a hasher
func (i *InputProposerSlashingWrapper) HashTreeRootWith(hh *ssz.Hasher) (err error) {
indx := hh.Index()
// Field (0) 'StateID'
hh.PutUint64(i.StateID)
// Field (1) 'ProposerSlashing'
if err = i.ProposerSlashing.HashTreeRootWith(hh); err != nil {
return
}
hh.Merkleize(indx)
return
}

View File

@ -2,40 +2,11 @@ package fuzz
import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// InputBlockHeader for fuzz testing beacon block headers.
type InputBlockHeader struct {
StateID uint64
Block *ethpb.BeaconBlock
}
// InputAttesterSlashingWrapper for fuzz testing attester slashing.
type InputAttesterSlashingWrapper struct {
StateID uint64
AttesterSlashing *ethpb.AttesterSlashing
}
// InputAttestationWrapper for fuzz testing attestations.
type InputAttestationWrapper struct {
StateID uint64
Attestation *ethpb.Attestation
}
// InputDepositWrapper for fuzz testing deposits.
type InputDepositWrapper struct {
StateID uint64
Deposit *ethpb.Deposit
}
// InputVoluntaryExitWrapper for fuzz testing voluntary exits.
type InputVoluntaryExitWrapper struct {
StateID uint64
VoluntaryExit *ethpb.VoluntaryExit
}
// InputProposerSlashingWrapper for fuzz testing proposer slashings.
type InputProposerSlashingWrapper struct {
StateID uint64
ProposerSlashing *ethpb.ProposerSlashing
// InputBlockWithPrestate for fuzz testing beacon blocks.
type InputBlockWithPrestate struct {
State *pb.BeaconState
Block *ethpb.SignedBeaconBlock
}

View File

@ -1,40 +0,0 @@
// +build libfuzzer
package fuzz
import (
"context"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzDeposit implements libfuzzer and beacon fuzz interface.
func BeaconFuzzProposerSlashing(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputProposerSlashingWrapper{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
block := &ethpb.SignedBeaconBlock{
Block: &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{ProposerSlashings: []*ethpb.ProposerSlashing{input.ProposerSlashing}},
},
}
post, err := blocks.ProcessProposerSlashings(context.Background(), st, block)
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -0,0 +1,27 @@
package fuzz
import (
"bytes"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
"github.com/prysmaticlabs/prysm/shared/params"
)
var buf = new(bytes.Buffer)
// SszEncoderAttestationFuzz runs network encode/decode for attestations.
func SszEncoderAttestationFuzz(b []byte) {
params.UseMainnetConfig()
buf.Reset()
input := &ethpb.Attestation{}
e := encoder.SszNetworkEncoder{}
if err := e.DecodeGossip(b, input); err != nil {
_ = err
return
}
if _, err := e.EncodeGossip(buf, input); err != nil {
_ = err
return
}
}

40
fuzz/state_fuzz.go Normal file
View File

@ -0,0 +1,40 @@
package fuzz
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
stateutil "github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
)
func init() {
featureconfig.Init(&featureconfig.Flags{
EnableSSZCache: false,
})
}
// BeaconStateFuzz --
func BeaconStateFuzz(input []byte) {
params.UseMainnetConfig()
st := &pb.BeaconState{}
if err := st.UnmarshalSSZ(input); err != nil {
return
}
s, err := state.InitializeFromProtoUnsafe(st)
if err != nil {
panic(err)
}
nextEpoch := helpers.SlotToEpoch(s.Slot()) + 1
slot, err := helpers.StartSlot(nextEpoch)
if err != nil {
return
}
if _, err := stateutil.ProcessSlots(context.Background(), s, slot); err != nil {
_ = err
return
}
}

View File

@ -1,31 +0,0 @@
package fuzz
import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
prylabs_testing "github.com/prysmaticlabs/prysm/fuzz/testing"
"github.com/prysmaticlabs/prysm/shared/params"
)
// BeaconFuzzVoluntaryExit implements libfuzzer and beacon fuzz interface.
func BeaconFuzzVoluntaryExit(b []byte) ([]byte, bool) {
params.UseMainnetConfig()
input := &InputVoluntaryExitWrapper{}
if err := input.UnmarshalSSZ(b); err != nil {
return fail(err)
}
s, err := prylabs_testing.GetBeaconFuzzState(input.StateID)
if err != nil || s == nil {
return nil, false
}
st, err := stateTrie.InitializeFromProto(s)
if err != nil {
return fail(err)
}
post, err := blocks.ProcessVoluntaryExitsNoVerifySignature(st, &ethpb.BeaconBlockBody{VoluntaryExits: []*ethpb.SignedVoluntaryExit{{Exit: input.VoluntaryExit}}})
if err != nil {
return fail(err)
}
return success(post)
}

View File

@ -114,6 +114,7 @@ go_test(
"//beacon-chain/state/stateutil:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/params:go_default_library",
"//shared/params/spectest:go_default_library",
"//shared/testutil:go_default_library",
"//shared/testutil/assert:go_default_library",

15
scripts/upload_fuzzers.sh Executable file
View File

@ -0,0 +1,15 @@
# Fuzzer bundle uploads
#
# This script builds the appropriate fuzzing bundles and uploads them to the google cloud bucket.
# Clusterfuzz will pick up the new fuzz bundles as fuzzing jobs are run.
# Build targets.
bazel build --config=fuzz \
//fuzz:block_fuzz_test_libfuzzer_bundle \
//fuzz:state_fuzz_test_libfuzzer_bundle \
//fuzz:ssz_encoder_attestations_test_libfuzzer_bundle
# Upload bundles with date timestamps in the filename.
gsutil cp bazel-bin/fuzz/block_fuzz_test_libfuzzer_bundle.zip gs://builds.prysmaticlabs.appspot.com/libfuzzer_asan_blocks/fuzzer-build-$(date +%Y%m%d%H%M).zip
gsutil cp bazel-bin/fuzz/state_fuzz_test_libfuzzer_bundle.zip gs://builds.prysmaticlabs.appspot.com/libfuzzer_asan_state/fuzzer-build-$(date +%Y%m%d%H%M).zip
gsutil cp bazel-bin/fuzz/ssz_encoder_attestations_test_libfuzzer_bundle.zip gs://builds.prysmaticlabs.appspot.com/libfuzzer_asan_ssz_encoder_attestations/fuzzer-build-$(date +%Y%m%d%H%M).zip

View File

@ -12,7 +12,7 @@ import (
// SetConfig sets the global params for spec tests depending on the option chosen.
// Provides reset function allowing to get back to the previous configuration at the end of a test.
func SetConfig(t *testing.T, config string) error {
func SetConfig(t testing.TB, config string) error {
params.SetupTestConfigCleanup(t)
switch config {
case "minimal":

View File

@ -4,7 +4,7 @@ import "testing"
// SetupTestConfigCleanup preserves configurations allowing to modify them within tests without any
// restrictions, everything is restored after the test.
func SetupTestConfigCleanup(t *testing.T) {
func SetupTestConfigCleanup(t testing.TB) {
prevDefaultBeaconConfig := mainnetBeaconConfig.Copy()
prevBeaconConfig := beaconConfig.Copy()
prevNetworkCfg := mainnetNetworkConfig.Copy()

View File

@ -42,7 +42,7 @@ func UnmarshalYaml(y []byte, dest interface{}) error {
// TestFolders sets the proper config and returns the result of ReadDir
// on the passed in eth2-spec-tests directory along with its path.
func TestFolders(t *testing.T, config string, folderPath string) ([]os.FileInfo, string) {
func TestFolders(t testing.TB, config string, folderPath string) ([]os.FileInfo, string) {
testsFolderPath := path.Join("tests", config, "phase0", folderPath)
filepath, err := bazel.Runfile(testsFolderPath)
require.NoError(t, err)

80
third_party/afl/BUILD.bazel vendored Normal file
View File

@ -0,0 +1,80 @@
package(
default_testonly = True,
default_visibility = ["//visibility:public"],
)
# Note: these libraries only compile with llvm.
cc_library(
name = "comm_hdr",
hdrs = [
"alloc-inl.h",
"config.h",
"debug.h",
"hash.h",
"types.h",
],
visibility = ["//visibility:private"],
)
CFLAGS = [
"-O3 -funroll-loops",
"-Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign",
"-DAFL_PATH=\\\"/usr/local/lib/afl\\\"",
"-DDOC_PATH=\\\"/usr/local/share/doc/afl\\\"",
"-DBIN_PATH=\\\"/usr/local/bin\\\"",
]
LDFLAGS = ["-ldl"]
cc_library(
name = "lib-afl-fuzz",
srcs = ["afl-fuzz.c"],
copts = CFLAGS,
linkopts = LDFLAGS,
visibility = ["//visibility:private"],
deps = [":comm_hdr"],
)
cc_library(
name = "lib-afl-showmap",
srcs = ["afl-showmap.c"],
copts = CFLAGS,
linkopts = LDFLAGS,
visibility = ["//visibility:private"],
deps = [":comm_hdr"],
)
cc_library(
name = "afl-llvm-rt",
srcs = ["llvm_mode/afl-llvm-rt.o.c"],
copts = ["-Wno-pointer-sign -O3"],
visibility = ["//visibility:private"],
deps = [":comm_hdr"],
)
cc_library(
name = "fuzzing_engine",
srcs = ["afl_driver.cpp"],
copts = ["-fsanitize=address -O3"],
linkopts = ["-fsanitize=address"],
tags = ["manual"],
deps = [":afl-llvm-rt"],
)
genrule(
name = "libs",
srcs = [
":lib-afl-fuzz",
":lib-afl-showmap",
],
outs = [
"afl-fuzz",
"afl-showmap",
],
cmd = """
cp $(locations :lib-afl-fuzz) .
cp $(locations :lib-afl-showmap) .
mv liblib-afl-fuzz.a $(location afl-fuzz)
mv liblib-afl-showmap.a $(location afl-showmap)
""",
)

BIN
third_party/afl/afl-fuzz vendored Executable file

Binary file not shown.

8099
third_party/afl/afl-fuzz.c vendored Normal file

File diff suppressed because it is too large Load Diff

780
third_party/afl/afl-showmap.c vendored Normal file
View File

@ -0,0 +1,780 @@
/*
american fuzzy lop - map display utility
----------------------------------------
Written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
A very simple tool that runs the targeted binary and displays
the contents of the trace bitmap in a human-readable form. Useful in
scripts to eliminate redundant inputs and perform other checks.
Exit code is 2 if the target program crashes; 1 if it times out or
there is a problem executing it; or 0 if execution is successful.
*/
#define AFL_MAIN
#include "config.h"
#include "types.h"
#include "debug.h"
#include "alloc-inl.h"
#include "hash.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
static s32 child_pid; /* PID of the tested program */
static u8* trace_bits; /* SHM with instrumentation bitmap */
static u8 *out_file, /* Trace output file */
*doc_path, /* Path to docs */
*target_path, /* Path to target binary */
*at_file; /* Substitution string for @@ */
static u32 exec_tmout; /* Exec timeout (ms) */
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
static s32 shm_id; /* ID of the SHM region */
static u8 quiet_mode, /* Hide non-essential messages? */
edges_only, /* Ignore hit counts? */
cmin_mode, /* Generate output in afl-cmin mode? */
binary_mode, /* Write output as a binary map */
keep_cores; /* Allow coredumps? */
static volatile u8
stop_soon, /* Ctrl-C pressed? */
child_timed_out, /* Child timed out? */
child_crashed; /* Child crashed? */
/* Classify tuple counts. Instead of mapping to individual bits, as in
afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */
static const u8 count_class_human[256] = {
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 3,
[4 ... 7] = 4,
[8 ... 15] = 5,
[16 ... 31] = 6,
[32 ... 127] = 7,
[128 ... 255] = 8
};
static const u8 count_class_binary[256] = {
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 4,
[4 ... 7] = 8,
[8 ... 15] = 16,
[16 ... 31] = 32,
[32 ... 127] = 64,
[128 ... 255] = 128
};
static void classify_counts(u8* mem, const u8* map) {
u32 i = MAP_SIZE;
if (edges_only) {
while (i--) {
if (*mem) *mem = 1;
mem++;
}
} else {
while (i--) {
*mem = map[*mem];
mem++;
}
}
}
/* Get rid of shared memory (atexit handler). */
static void remove_shm(void) {
shmctl(shm_id, IPC_RMID, NULL);
}
/* Configure shared memory. */
static void setup_shm(void) {
u8* shm_str;
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
if (shm_id < 0) PFATAL("shmget() failed");
atexit(remove_shm);
shm_str = alloc_printf("%d", shm_id);
setenv(SHM_ENV_VAR, shm_str, 1);
ck_free(shm_str);
trace_bits = shmat(shm_id, NULL, 0);
if (!trace_bits) PFATAL("shmat() failed");
}
/* Write results. */
static u32 write_results(void) {
s32 fd;
u32 i, ret = 0;
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
if (!strncmp(out_file, "/dev/", 5)) {
fd = open(out_file, O_WRONLY, 0600);
if (fd < 0) PFATAL("Unable to open '%s'", out_file);
} else if (!strcmp(out_file, "-")) {
fd = dup(1);
if (fd < 0) PFATAL("Unable to open stdout");
} else {
unlink(out_file); /* Ignore errors */
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
}
if (binary_mode) {
for (i = 0; i < MAP_SIZE; i++)
if (trace_bits[i]) ret++;
ck_write(fd, trace_bits, MAP_SIZE, out_file);
close(fd);
} else {
FILE* f = fdopen(fd, "w");
if (!f) PFATAL("fdopen() failed");
for (i = 0; i < MAP_SIZE; i++) {
if (!trace_bits[i]) continue;
ret++;
if (cmin_mode) {
if (child_timed_out) break;
if (!caa && child_crashed != cco) break;
fprintf(f, "%u%u\n", trace_bits[i], i);
} else fprintf(f, "%06u:%u\n", i, trace_bits[i]);
}
fclose(f);
}
return ret;
}
/* Handle timeout signal. */
static void handle_timeout(int sig) {
child_timed_out = 1;
if (child_pid > 0) kill(child_pid, SIGKILL);
}
/* Execute target application. */
static void run_target(char** argv) {
static struct itimerval it;
int status = 0;
if (!quiet_mode)
SAYF("-- Program output begins --\n" cRST);
MEM_BARRIER();
child_pid = fork();
if (child_pid < 0) PFATAL("fork() failed");
if (!child_pid) {
struct rlimit r;
if (quiet_mode) {
s32 fd = open("/dev/null", O_RDWR);
if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
*(u32*)trace_bits = EXEC_FAIL_SIG;
PFATAL("Descriptor initialization failed");
}
close(fd);
}
if (mem_limit) {
r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
#ifdef RLIMIT_AS
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
#else
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
#endif /* ^RLIMIT_AS */
}
if (!keep_cores) r.rlim_max = r.rlim_cur = 0;
else r.rlim_max = r.rlim_cur = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
setsid();
execv(target_path, argv);
*(u32*)trace_bits = EXEC_FAIL_SIG;
exit(0);
}
/* Configure timeout, wait for child, cancel timeout. */
if (exec_tmout) {
child_timed_out = 0;
it.it_value.tv_sec = (exec_tmout / 1000);
it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
}
setitimer(ITIMER_REAL, &it, NULL);
if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed");
child_pid = 0;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &it, NULL);
MEM_BARRIER();
/* Clean up bitmap, analyze exit condition, etc. */
if (*(u32*)trace_bits == EXEC_FAIL_SIG)
FATAL("Unable to execute '%s'", argv[0]);
classify_counts(trace_bits, binary_mode ?
count_class_binary : count_class_human);
if (!quiet_mode)
SAYF(cRST "-- Program output ends --\n");
if (!child_timed_out && !stop_soon && WIFSIGNALED(status))
child_crashed = 1;
if (!quiet_mode) {
if (child_timed_out)
SAYF(cLRD "\n+++ Program timed off +++\n" cRST);
else if (stop_soon)
SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
else if (child_crashed)
SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status));
}
}
/* Handle Ctrl-C and the like. */
static void handle_stop_sig(int sig) {
stop_soon = 1;
if (child_pid > 0) kill(child_pid, SIGKILL);
}
/* Do basic preparations - persistent fds, filenames, etc. */
static void set_up_environment(void) {
setenv("ASAN_OPTIONS", "abort_on_error=1:"
"detect_leaks=0:"
"symbolize=0:"
"allocator_may_return_null=1", 0);
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
"symbolize=0:"
"abort_on_error=1:"
"allocator_may_return_null=1:"
"msan_track_origins=0", 0);
if (getenv("AFL_PRELOAD")) {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
}
}
/* Setup signal handlers, duh. */
static void setup_signal_handlers(void) {
struct sigaction sa;
sa.sa_handler = NULL;
sa.sa_flags = SA_RESTART;
sa.sa_sigaction = NULL;
sigemptyset(&sa.sa_mask);
/* Various ways of saying "stop". */
sa.sa_handler = handle_stop_sig;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
/* Exec timeout notifications. */
sa.sa_handler = handle_timeout;
sigaction(SIGALRM, &sa, NULL);
}
/* Detect @@ in args. */
static void detect_file_args(char** argv) {
u32 i = 0;
u8* cwd = getcwd(NULL, 0);
if (!cwd) PFATAL("getcwd() failed");
while (argv[i]) {
u8* aa_loc = strstr(argv[i], "@@");
if (aa_loc) {
u8 *aa_subst, *n_arg;
if (!at_file) FATAL("@@ syntax is not supported by this tool.");
/* Be sure that we're always using fully-qualified paths. */
if (at_file[0] == '/') aa_subst = at_file;
else aa_subst = alloc_printf("%s/%s", cwd, at_file);
/* Construct a replacement argv value. */
*aa_loc = 0;
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
argv[i] = n_arg;
*aa_loc = '@';
if (at_file[0] != '/') ck_free(aa_subst);
}
i++;
}
free(cwd); /* not tracked */
}
/* Show banner. */
static void show_banner(void) {
SAYF(cCYA "afl-showmap " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
}
/* Display usage hints. */
static void usage(u8* argv0) {
show_banner();
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
"Required parameters:\n\n"
" -o file - file to write the trace data to\n\n"
"Execution control settings:\n\n"
" -t msec - timeout for each run (none)\n"
" -m megs - memory limit for child process (%u MB)\n"
" -Q - use binary-only instrumentation (QEMU mode)\n\n"
"Other settings:\n\n"
" -q - sink program's output and don't show messages\n"
" -e - show edge coverage only, ignore hit counts\n"
" -c - allow core dumps\n\n"
"This tool displays raw tuple data captured by AFL instrumentation.\n"
"For additional help, consult %s/README.\n\n" cRST,
argv0, MEM_LIMIT, doc_path);
exit(1);
}
/* Find binary. */
static void find_binary(u8* fname) {
u8* env_path = 0;
struct stat st;
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
target_path = ck_strdup(fname);
if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
!(st.st_mode & 0111) || st.st_size < 4)
FATAL("Program '%s' not found or not executable", fname);
} else {
while (env_path) {
u8 *cur_elem, *delim = strchr(env_path, ':');
if (delim) {
cur_elem = ck_alloc(delim - env_path + 1);
memcpy(cur_elem, env_path, delim - env_path);
delim++;
} else cur_elem = ck_strdup(env_path);
env_path = delim;
if (cur_elem[0])
target_path = alloc_printf("%s/%s", cur_elem, fname);
else
target_path = ck_strdup(fname);
ck_free(cur_elem);
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
(st.st_mode & 0111) && st.st_size >= 4) break;
ck_free(target_path);
target_path = 0;
}
if (!target_path) FATAL("Program '%s' not found or not executable", fname);
}
}
/* Fix up argv for QEMU. */
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
u8 *tmp, *cp, *rsl, *own_copy;
/* Workaround for a QEMU stability glitch. */
setenv("QEMU_LOG", "nochain", 1);
memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
new_argv[2] = target_path;
new_argv[1] = "--";
/* Now we need to actually find qemu for argv[0]. */
tmp = getenv("AFL_PATH");
if (tmp) {
cp = alloc_printf("%s/afl-qemu-trace", tmp);
if (access(cp, X_OK))
FATAL("Unable to find '%s'", tmp);
target_path = new_argv[0] = cp;
return new_argv;
}
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
if (rsl) {
*rsl = 0;
cp = alloc_printf("%s/afl-qemu-trace", own_copy);
ck_free(own_copy);
if (!access(cp, X_OK)) {
target_path = new_argv[0] = cp;
return new_argv;
}
} else ck_free(own_copy);
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace";
return new_argv;
}
FATAL("Unable to find 'afl-qemu-trace'.");
}
/* Main entry point */
int main(int argc, char** argv) {
s32 opt;
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
u32 tcnt;
char** use_argv;
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbc")) > 0)
switch (opt) {
case 'o':
if (out_file) FATAL("Multiple -o options not supported");
out_file = optarg;
break;
case 'm': {
u8 suffix = 'M';
if (mem_limit_given) FATAL("Multiple -m options not supported");
mem_limit_given = 1;
if (!strcmp(optarg, "none")) {
mem_limit = 0;
break;
}
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
optarg[0] == '-') FATAL("Bad syntax used for -m");
switch (suffix) {
case 'T': mem_limit *= 1024 * 1024; break;
case 'G': mem_limit *= 1024; break;
case 'k': mem_limit /= 1024; break;
case 'M': break;
default: FATAL("Unsupported suffix or bad syntax for -m");
}
if (mem_limit < 5) FATAL("Dangerously low value of -m");
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
FATAL("Value of -m out of range on 32-bit systems");
}
break;
case 't':
if (timeout_given) FATAL("Multiple -t options not supported");
timeout_given = 1;
if (strcmp(optarg, "none")) {
exec_tmout = atoi(optarg);
if (exec_tmout < 20 || optarg[0] == '-')
FATAL("Dangerously low value of -t");
}
break;
case 'e':
if (edges_only) FATAL("Multiple -e options not supported");
edges_only = 1;
break;
case 'q':
if (quiet_mode) FATAL("Multiple -q options not supported");
quiet_mode = 1;
break;
case 'Z':
/* This is an undocumented option to write data in the syntax expected
by afl-cmin. Nobody else should have any use for this. */
cmin_mode = 1;
quiet_mode = 1;
break;
case 'A':
/* Another afl-cmin specific feature. */
at_file = optarg;
break;
case 'Q':
if (qemu_mode) FATAL("Multiple -Q options not supported");
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
qemu_mode = 1;
break;
case 'b':
/* Secret undocumented mode. Writes output in raw binary format
similar to that dumped by afl-fuzz in <out_dir/queue/fuzz_bitmap. */
binary_mode = 1;
break;
case 'c':
if (keep_cores) FATAL("Multiple -c options not supported");
keep_cores = 1;
break;
default:
usage(argv[0]);
}
if (optind == argc || !out_file) usage(argv[0]);
setup_shm();
setup_signal_handlers();
set_up_environment();
find_binary(argv[optind]);
if (!quiet_mode) {
show_banner();
ACTF("Executing '%s'...\n", target_path);
}
detect_file_args(argv + optind);
if (qemu_mode)
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
else
use_argv = argv + optind;
run_target(use_argv);
tcnt = write_results();
if (!quiet_mode) {
if (!tcnt) FATAL("No instrumentation detected" cRST);
OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file);
}
exit(child_crashed * 2 + child_timed_out);
}

277
third_party/afl/afl_driver.cpp vendored Normal file
View File

@ -0,0 +1,277 @@
//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//===----------------------------------------------------------------------===//
/* This file allows to fuzz libFuzzer-style target functions
(LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
Usage:
################################################################################
cat << EOF > test_fuzzer.cc
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size > 0 && data[0] == 'H')
if (size > 1 && data[1] == 'I')
if (size > 2 && data[2] == '!')
__builtin_trap();
return 0;
}
EOF
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
# Build afl-llvm-rt.o.c from the AFL distribution.
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
# Build this file, link it with afl-llvm-rt.o.o and the target code.
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
# Run AFL:
rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
################################################################################
AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
specified. If the file does not exist, it is created. This is useful for getting
stack traces (when using ASAN for example) or original error messages on hard
to reproduce bugs. Note that any content written to stderr will be written to
this file instead of stderr's usual location.
AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option.
If 1, close stdout at startup. If 2 close stderr; if 3 close both.
*/
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <vector>
// Platform detection. Copied from FuzzerInternal.h
#ifdef __linux__
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __APPLE__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __NetBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 1
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __FreeBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 1
#define LIBFUZZER_OPENBSD 0
#elif __OpenBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 1
#else
#error "Support for your platform has not been implemented"
#endif
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
}
// Notify AFL about persistent mode.
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
extern "C" int __afl_persistent_loop(unsigned int);
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
// Notify AFL about deferred forkserver.
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
extern "C" void __afl_manual_init();
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
// Input buffer.
static const size_t kMaxAflInputSize = 1 << 20;
static uint8_t AflInputBuf[kMaxAflInputSize];
// Use this optionally defined function to output sanitizer messages even if
// user asks to close stderr.
__attribute__((weak)) extern "C" void __sanitizer_set_report_fd(void *);
// Keep track of where stderr content is being written to, so that
// dup_and_close_stderr can use the correct one.
static FILE *output_file = stderr;
// Experimental feature to use afl_driver without AFL's deferred mode.
// Needs to run before __afl_auto_init.
__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) {
if (getenv("AFL_DRIVER_DONT_DEFER")) {
if (unsetenv("__AFL_DEFER_FORKSRV")) {
perror("Failed to unset __AFL_DEFER_FORKSRV");
abort();
}
}
}
// If the user asks us to duplicate stderr, then do it.
static void maybe_duplicate_stderr() {
char *stderr_duplicate_filename =
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
if (!stderr_duplicate_filename)
return;
FILE *stderr_duplicate_stream =
freopen(stderr_duplicate_filename, "a+", stderr);
if (!stderr_duplicate_stream) {
fprintf(
stderr,
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
abort();
}
output_file = stderr_duplicate_stream;
}
// Most of these I/O functions were inspired by/copied from libFuzzer's code.
static void discard_output(int fd) {
FILE *temp = fopen("/dev/null", "w");
if (!temp)
abort();
dup2(fileno(temp), fd);
fclose(temp);
}
static void close_stdout() { discard_output(STDOUT_FILENO); }
// Prevent the targeted code from writing to "stderr" but allow sanitizers and
// this driver to do so.
static void dup_and_close_stderr() {
int output_fileno = fileno(output_file);
int output_fd = dup(output_fileno);
if (output_fd <= 0)
abort();
FILE *new_output_file = fdopen(output_fd, "w");
if (!new_output_file)
abort();
if (!__sanitizer_set_report_fd)
return;
__sanitizer_set_report_fd(reinterpret_cast<void *>(output_fd));
discard_output(output_fileno);
}
static void Printf(const char *Fmt, ...) {
va_list ap;
va_start(ap, Fmt);
vfprintf(output_file, Fmt, ap);
va_end(ap);
fflush(output_file);
}
// Close stdout and/or stderr if user asks for it.
static void maybe_close_fd_mask() {
char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK");
if (!fd_mask_str)
return;
int fd_mask = atoi(fd_mask_str);
if (fd_mask & 2)
dup_and_close_stderr();
if (fd_mask & 1)
close_stdout();
}
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
// with libFuzzer's LLVMFuzzerCustomMutator.
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
return 0;
}
// Execute any files provided as parameters.
static int ExecuteFilesOnyByOne(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
std::ifstream in(argv[i], std::ios::binary);
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg (0, in.beg);
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
// Allocate exactly length bytes so that we reliably catch buffer overflows.
std::vector<char> bytes(length);
in.read(bytes.data(), bytes.size());
assert(in);
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
bytes.size());
std::cout << "Execution successful" << std::endl;
}
return 0;
}
int main(int argc, char **argv) {
Printf(
"======================= INFO =========================\n"
"This binary is built for AFL-fuzz.\n"
"To run the target function on individual input(s) execute this:\n"
" %s < INPUT_FILE\n"
"or\n"
" %s INPUT_FILE1 [INPUT_FILE2 ... ]\n"
"To fuzz with afl-fuzz execute this:\n"
" afl-fuzz [afl-flags] %s [-N]\n"
"afl-fuzz will run N iterations before "
"re-spawning the process (default: 1000)\n"
"======================================================\n",
argv[0], argv[0], argv[0]);
maybe_duplicate_stderr();
maybe_close_fd_mask();
if (LLVMFuzzerInitialize)
LLVMFuzzerInitialize(&argc, &argv);
// Do any other expensive one-time initialization here.
if (!getenv("AFL_DRIVER_DONT_DEFER"))
__afl_manual_init();
int N = 1000;
if (argc == 2 && argv[1][0] == '-')
N = atoi(argv[1] + 1);
else if(argc == 2 && (N = atoi(argv[1])) > 0)
Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
else if (argc > 1)
return ExecuteFilesOnyByOne(argc, argv);
assert(N > 0);
// Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
// on the first execution of LLVMFuzzerTestOneInput is ignored.
uint8_t dummy_input[1] = {0};
LLVMFuzzerTestOneInput(dummy_input, 1);
int num_runs = 0;
while (__afl_persistent_loop(N)) {
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
if (n_read > 0) {
// Copy AflInputBuf into a separate buffer to let asan find buffer
// overflows. Don't use unique_ptr/etc to avoid extra dependencies.
uint8_t *copy = new uint8_t[n_read];
memcpy(copy, AflInputBuf, n_read);
num_runs++;
LLVMFuzzerTestOneInput(copy, n_read);
delete[] copy;
}
}
Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
}

570
third_party/afl/alloc-inl.h vendored Normal file
View File

@ -0,0 +1,570 @@
/*
american fuzzy lop - error-checking, memory-zeroing alloc routines
------------------------------------------------------------------
Written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This allocator is not designed to resist malicious attackers (the canaries
are small and predictable), but provides a robust and portable way to detect
use-after-free, off-by-one writes, stale pointers, and so on.
*/
#ifndef _HAVE_ALLOC_INL_H
#define _HAVE_ALLOC_INL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "types.h"
#include "debug.h"
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
#define alloc_printf(_str...) ({ \
u8* _tmp; \
s32 _len = snprintf(NULL, 0, _str); \
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
_tmp = ck_alloc(_len + 1); \
snprintf((char*)_tmp, _len + 1, _str); \
_tmp; \
})
/* Macro to enforce allocation limits as a last-resort defense against
integer overflows. */
#define ALLOC_CHECK_SIZE(_s) do { \
if ((_s) > MAX_ALLOC) \
ABORT("Bad alloc request: %u bytes", (_s)); \
} while (0)
/* Macro to check malloc() failures and the like. */
#define ALLOC_CHECK_RESULT(_r, _s) do { \
if (!(_r)) \
ABORT("Out of memory: can't allocate %u bytes", (_s)); \
} while (0)
/* Magic tokens used to mark used / freed chunks. */
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
/* Positions of guard tokens in relation to the user-visible pointer. */
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)])
#define ALLOC_OFF_HEAD 8
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
/* Allocator increments for ck_realloc_block(). */
#define ALLOC_BLK_INC 256
/* Sanity-checking macros for pointers. */
#define CHECK_PTR(_p) do { \
if (_p) { \
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
ABORT("Use after free."); \
else ABORT("Corrupted head alloc canary."); \
} \
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
ABORT("Corrupted tail alloc canary."); \
} \
} while (0)
#define CHECK_PTR_EXPR(_p) ({ \
typeof (_p) _tmp = (_p); \
CHECK_PTR(_tmp); \
_tmp; \
})
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
requests. */
static inline void* DFL_ck_alloc_nozero(u32 size) {
void* ret;
if (!size) return NULL;
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return ret;
}
/* Allocate a buffer, returning zeroed memory. */
static inline void* DFL_ck_alloc(u32 size) {
void* mem;
if (!size) return NULL;
mem = DFL_ck_alloc_nozero(size);
return memset(mem, 0, size);
}
/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD
is set, the old memory will be also clobbered with 0xFF. */
static inline void DFL_ck_free(void* mem) {
if (!mem) return;
CHECK_PTR(mem);
#ifdef DEBUG_BUILD
/* Catch pointer issues sooner. */
memset(mem, 0xFF, ALLOC_S(mem));
#endif /* DEBUG_BUILD */
ALLOC_C1(mem) = ALLOC_MAGIC_F;
free(mem - ALLOC_OFF_HEAD);
}
/* Re-allocate a buffer, checking for issues and zeroing any newly-added tail.
With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the
old memory is clobbered with 0xFF. */
static inline void* DFL_ck_realloc(void* orig, u32 size) {
void* ret;
u32 old_size = 0;
if (!size) {
DFL_ck_free(orig);
return NULL;
}
if (orig) {
CHECK_PTR(orig);
#ifndef DEBUG_BUILD
ALLOC_C1(orig) = ALLOC_MAGIC_F;
#endif /* !DEBUG_BUILD */
old_size = ALLOC_S(orig);
orig -= ALLOC_OFF_HEAD;
ALLOC_CHECK_SIZE(old_size);
}
ALLOC_CHECK_SIZE(size);
#ifndef DEBUG_BUILD
ret = realloc(orig, size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
#else
/* Catch pointer issues sooner: force relocation and make sure that the
original buffer is wiped. */
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
if (orig) {
memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size));
memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size);
ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F;
free(orig);
}
#endif /* ^!DEBUG_BUILD */
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
if (size > old_size)
memset(ret + old_size, 0, size - old_size);
return ret;
}
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
repeated small reallocs without complicating the user code). */
static inline void* DFL_ck_realloc_block(void* orig, u32 size) {
#ifndef DEBUG_BUILD
if (orig) {
CHECK_PTR(orig);
if (ALLOC_S(orig) >= size) return orig;
size += ALLOC_BLK_INC;
}
#endif /* !DEBUG_BUILD */
return DFL_ck_realloc(orig, size);
}
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
static inline u8* DFL_ck_strdup(u8* str) {
void* ret;
u32 size;
if (!str) return NULL;
size = strlen((char*)str) + 1;
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return memcpy(ret, str, size);
}
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
or NULL inputs. */
static inline void* DFL_ck_memdup(void* mem, u32 size) {
void* ret;
if (!mem || !size) return NULL;
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return memcpy(ret, mem, size);
}
/* Create a buffer with a block of text, appending a NUL terminator at the end.
Returns NULL for zero-sized or NULL inputs. */
static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
u8* ret;
if (!mem || !size) return NULL;
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL + 1);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
memcpy(ret, mem, size);
ret[size] = 0;
return ret;
}
#ifndef DEBUG_BUILD
/* In non-debug mode, we just do straightforward aliasing of the above functions
to user-visible names such as ck_alloc(). */
#define ck_alloc DFL_ck_alloc
#define ck_alloc_nozero DFL_ck_alloc_nozero
#define ck_realloc DFL_ck_realloc
#define ck_realloc_block DFL_ck_realloc_block
#define ck_strdup DFL_ck_strdup
#define ck_memdup DFL_ck_memdup
#define ck_memdup_str DFL_ck_memdup_str
#define ck_free DFL_ck_free
#define alloc_report()
#else
/* In debugging mode, we also track allocations to detect memory leaks, and the
flow goes through one more layer of indirection. */
/* Alloc tracking data structures: */
#define ALLOC_BUCKETS 4096
struct TRK_obj {
void *ptr;
char *file, *func;
u32 line;
};
#ifdef AFL_MAIN
struct TRK_obj* TRK[ALLOC_BUCKETS];
u32 TRK_cnt[ALLOC_BUCKETS];
# define alloc_report() TRK_report()
#else
extern struct TRK_obj* TRK[ALLOC_BUCKETS];
extern u32 TRK_cnt[ALLOC_BUCKETS];
# define alloc_report()
#endif /* ^AFL_MAIN */
/* Bucket-assigning function for a given pointer: */
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
/* Add a new entry to the list of allocated objects. */
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
u32 line) {
u32 i, bucket;
if (!ptr) return;
bucket = TRKH(ptr);
/* Find a free slot in the list of entries for that bucket. */
for (i = 0; i < TRK_cnt[bucket]; i++)
if (!TRK[bucket][i].ptr) {
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
return;
}
/* No space available - allocate more. */
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
TRK_cnt[bucket]++;
}
/* Remove entry from the list of allocated objects. */
static inline void TRK_free_buf(void* ptr, const char* file, const char* func,
u32 line) {
u32 i, bucket;
if (!ptr) return;
bucket = TRKH(ptr);
/* Find the element on the list... */
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr == ptr) {
TRK[bucket][i].ptr = 0;
return;
}
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)",
func, file, line);
}
/* Do a final report on all non-deallocated objects. */
static inline void TRK_report(void) {
u32 i, bucket;
fflush(0);
for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++)
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr)
WARNF("ALLOC: Memory never freed, created in %s (%s:%u)",
TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line);
}
/* Simple wrappers for non-debugging functions: */
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
u32 line) {
void* ret = DFL_ck_alloc(size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_realloc(orig, size);
TRK_free_buf(orig, file, func, line);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_realloc_block(orig, size);
TRK_free_buf(orig, file, func, line);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func,
u32 line) {
void* ret = DFL_ck_strdup(str);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_memdup(mem, size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_memdup_str(mem, size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void TRK_ck_free(void* ptr, const char* file,
const char* func, u32 line) {
TRK_free_buf(ptr, file, func, line);
DFL_ck_free(ptr);
}
/* Aliasing user-facing names to tracking functions: */
#define ck_alloc(_p1) \
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_alloc_nozero(_p1) \
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_realloc(_p1, _p2) \
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_realloc_block(_p1, _p2) \
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_strdup(_p1) \
TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_memdup(_p1, _p2) \
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_memdup_str(_p1, _p2) \
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_free(_p1) \
TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
#endif /* ^!DEBUG_BUILD */
#endif /* ! _HAVE_ALLOC_INL_H */

350
third_party/afl/config.h vendored Normal file
View File

@ -0,0 +1,350 @@
/*
american fuzzy lop - vaguely configurable bits
----------------------------------------------
Written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
*/
#ifndef _HAVE_CONFIG_H
#define _HAVE_CONFIG_H
#include "types.h"
/* Version string: */
#define VERSION "2.52b"
/******************************************************
* *
* Settings that may be of interest to power users: *
* *
******************************************************/
/* Comment out to disable terminal colors (note that this makes afl-analyze
a lot less nice): */
#define USE_COLOR
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
#define FANCY_BOXES
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
also used for detecting hangs; the actual value is auto-scaled: */
#define EXEC_TIMEOUT 1000
/* Timeout rounding factor when auto-scaling (milliseconds): */
#define EXEC_TM_ROUND 20
/* Default memory limit for child process (MB): */
#ifndef __x86_64__
# define MEM_LIMIT 25
#else
# define MEM_LIMIT 50
#endif /* ^!__x86_64__ */
/* Default memory limit when running in QEMU mode (MB): */
#define MEM_LIMIT_QEMU 200
/* Number of calibration cycles per every new test case (and for test
cases that show variable behavior): */
#define CAL_CYCLES 8
#define CAL_CYCLES_LONG 40
/* Number of subsequent timeouts before abandoning an input file: */
#define TMOUT_LIMIT 250
/* Maximum number of unique hangs or crashes to record: */
#define KEEP_UNIQUE_HANG 500
#define KEEP_UNIQUE_CRASH 5000
/* Baseline number of random tweaks during a single 'havoc' stage: */
#define HAVOC_CYCLES 256
#define HAVOC_CYCLES_INIT 1024
/* Maximum multiplier for the above (should be a power of two, beware
of 32-bit int overflows): */
#define HAVOC_MAX_MULT 16
/* Absolute minimum number of havoc cycles (after all adjustments): */
#define HAVOC_MIN 16
/* Maximum stacking for havoc-stage tweaks. The actual value is calculated
like this:
n = random between 1 and HAVOC_STACK_POW2
stacking = 2^n
In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or
128 stacked tweaks: */
#define HAVOC_STACK_POW2 7
/* Caps on block sizes for cloning and deletion operations. Each of these
ranges has a 33% probability of getting picked, except for the first
two cycles where smaller blocks are favored: */
#define HAVOC_BLK_SMALL 32
#define HAVOC_BLK_MEDIUM 128
#define HAVOC_BLK_LARGE 1500
/* Extra-large blocks, selected very rarely (<5% of the time): */
#define HAVOC_BLK_XL 32768
/* Probabilities of skipping non-favored entries in the queue, expressed as
percentages: */
#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */
#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */
#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */
/* Splicing cycle count: */
#define SPLICE_CYCLES 15
/* Nominal per-splice havoc cycle length: */
#define SPLICE_HAVOC 32
/* Maximum offset for integer addition / subtraction stages: */
#define ARITH_MAX 35
/* Limits for the test case trimmer. The absolute minimum chunk size; and
the starting and ending divisors for chopping up the input file: */
#define TRIM_MIN_BYTES 4
#define TRIM_START_STEPS 16
#define TRIM_END_STEPS 1024
/* Maximum size of input file, in bytes (keep under 100MB): */
#define MAX_FILE (1 * 1024 * 1024)
/* The same, for the test case minimizer: */
#define TMIN_MAX_FILE (10 * 1024 * 1024)
/* Block normalization steps for afl-tmin: */
#define TMIN_SET_MIN_SIZE 4
#define TMIN_SET_STEPS 128
/* Maximum dictionary token size (-x), in bytes: */
#define MAX_DICT_FILE 128
/* Length limits for auto-detected dictionary tokens: */
#define MIN_AUTO_EXTRA 3
#define MAX_AUTO_EXTRA 32
/* Maximum number of user-specified dictionary tokens to use in deterministic
steps; past this point, the "extras/user" step will be still carried out,
but with proportionally lower odds: */
#define MAX_DET_EXTRAS 200
/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing
(first value), and to keep in memory as candidates. The latter should be much
higher than the former. */
#define USE_AUTO_EXTRAS 50
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10)
/* Scaling factor for the effector map used to skip some of the more
expensive deterministic steps. The actual divisor is set to
2^EFF_MAP_SCALE2 bytes: */
#define EFF_MAP_SCALE2 3
/* Minimum input file length at which the effector logic kicks in: */
#define EFF_MIN_LEN 128
/* Maximum effector density past which everything is just fuzzed
unconditionally (%): */
#define EFF_MAX_PERC 90
/* UI refresh frequency (Hz): */
#define UI_TARGET_HZ 5
/* Fuzzer stats file and plot update intervals (sec): */
#define STATS_UPDATE_SEC 60
#define PLOT_UPDATE_SEC 5
/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */
#define AVG_SMOOTHING 16
/* Sync interval (every n havoc cycles): */
#define SYNC_INTERVAL 5
/* Output directory reuse grace period (minutes): */
#define OUTPUT_GRACE 25
/* Uncomment to use simple file names (id_NNNNNN): */
// #define SIMPLE_FILES
/* List of interesting values to use in fuzzing. */
#define INTERESTING_8 \
-128, /* Overflow signed 8-bit when decremented */ \
-1, /* */ \
0, /* */ \
1, /* */ \
16, /* One-off with common buffer size */ \
32, /* One-off with common buffer size */ \
64, /* One-off with common buffer size */ \
100, /* One-off with common buffer size */ \
127 /* Overflow signed 8-bit when incremented */
#define INTERESTING_16 \
-32768, /* Overflow signed 16-bit when decremented */ \
-129, /* Overflow signed 8-bit */ \
128, /* Overflow signed 8-bit */ \
255, /* Overflow unsig 8-bit when incremented */ \
256, /* Overflow unsig 8-bit */ \
512, /* One-off with common buffer size */ \
1000, /* One-off with common buffer size */ \
1024, /* One-off with common buffer size */ \
4096, /* One-off with common buffer size */ \
32767 /* Overflow signed 16-bit when incremented */
#define INTERESTING_32 \
-2147483648LL, /* Overflow signed 32-bit when decremented */ \
-100663046, /* Large negative number (endian-agnostic) */ \
-32769, /* Overflow signed 16-bit */ \
32768, /* Overflow signed 16-bit */ \
65535, /* Overflow unsig 16-bit when incremented */ \
65536, /* Overflow unsig 16 bit */ \
100663045, /* Large positive number (endian-agnostic) */ \
2147483647 /* Overflow signed 32-bit when incremented */
/***********************************************************
* *
* Really exotic stuff you probably don't want to touch: *
* *
***********************************************************/
/* Call count interval between reseeding the libc PRNG from /dev/urandom: */
#define RESEED_RNG 10000
/* Maximum line length passed from GCC to 'as' and used for parsing
configuration files: */
#define MAX_LINE 8192
/* Environment variable used to pass SHM ID to the called program. */
#define SHM_ENV_VAR "__AFL_SHM_ID"
/* Other less interesting, internal-only variables. */
#define CLANG_ENV_VAR "__AFL_CLANG_MODE"
#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK"
#define PERSIST_ENV_VAR "__AFL_PERSISTENT"
#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV"
/* In-code signatures for deferred and persistent mode. */
#define PERSIST_SIG "##SIG_AFL_PERSISTENT##"
#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##"
/* Distinctive bitmap signature used to indicate failed execution: */
#define EXEC_FAIL_SIG 0xfee1dead
/* Distinctive exit code used to indicate MSAN trip condition: */
#define MSAN_ERROR 86
/* Designated file descriptors for forkserver commands (the application will
use FORKSRV_FD and FORKSRV_FD + 1): */
#define FORKSRV_FD 198
/* Fork server init timeout multiplier: we'll wait the user-selected
timeout plus this much for the fork server to spin up. */
#define FORK_WAIT_MULT 10
/* Calibration timeout adjustments, to be a bit more generous when resuming
fuzzing sessions or trying to calibrate already-added internal finds.
The first value is a percentage, the other is in milliseconds: */
#define CAL_TMOUT_PERC 125
#define CAL_TMOUT_ADD 50
/* Number of chances to calibrate a case before giving up: */
#define CAL_CHANCES 3
/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than
2; you probably want to keep it under 18 or so for performance reasons
(adjusting AFL_INST_RATIO when compiling is probably a better way to solve
problems with complex programs). You need to recompile the target binary
after changing this - otherwise, SEGVs may ensue. */
#define MAP_SIZE_POW2 16
#define MAP_SIZE (1 << MAP_SIZE_POW2)
/* Maximum allocator request size (keep well under INT_MAX): */
#define MAX_ALLOC 0x40000000
/* A made-up hashing seed: */
#define HASH_CONST 0xa5b35705
/* Constants for afl-gotcpu to control busy loop timing: */
#define CTEST_TARGET_MS 5000
#define CTEST_CORE_TRG_MS 1000
#define CTEST_BUSY_CYCLES (10 * 1000 * 1000)
/* Uncomment this to use inferior block-coverage-based instrumentation. Note
that you need to recompile the target binary for this to have any effect: */
// #define COVERAGE_ONLY
/* Uncomment this to ignore hit counts and output just one bit per tuple.
As with the previous setting, you will need to recompile the target
binary: */
// #define SKIP_COUNTS
/* Uncomment this to use instrumentation data to record newly discovered paths,
but do not use them as seeds for fuzzing. This is useful for conveniently
measuring coverage that could be attained by a "dumb" fuzzing algorithm: */
// #define IGNORE_FINDS
#endif /* ! _HAVE_CONFIG_H */

251
third_party/afl/debug.h vendored Normal file
View File

@ -0,0 +1,251 @@
/*
american fuzzy lop - debug / error handling macros
--------------------------------------------------
Written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
*/
#ifndef _HAVE_DEBUG_H
#define _HAVE_DEBUG_H
#include <errno.h>
#include "types.h"
#include "config.h"
/*******************
* Terminal colors *
*******************/
#ifdef USE_COLOR
# define cBLK "\x1b[0;30m"
# define cRED "\x1b[0;31m"
# define cGRN "\x1b[0;32m"
# define cBRN "\x1b[0;33m"
# define cBLU "\x1b[0;34m"
# define cMGN "\x1b[0;35m"
# define cCYA "\x1b[0;36m"
# define cLGR "\x1b[0;37m"
# define cGRA "\x1b[1;90m"
# define cLRD "\x1b[1;91m"
# define cLGN "\x1b[1;92m"
# define cYEL "\x1b[1;93m"
# define cLBL "\x1b[1;94m"
# define cPIN "\x1b[1;95m"
# define cLCY "\x1b[1;96m"
# define cBRI "\x1b[1;97m"
# define cRST "\x1b[0m"
# define bgBLK "\x1b[40m"
# define bgRED "\x1b[41m"
# define bgGRN "\x1b[42m"
# define bgBRN "\x1b[43m"
# define bgBLU "\x1b[44m"
# define bgMGN "\x1b[45m"
# define bgCYA "\x1b[46m"
# define bgLGR "\x1b[47m"
# define bgGRA "\x1b[100m"
# define bgLRD "\x1b[101m"
# define bgLGN "\x1b[102m"
# define bgYEL "\x1b[103m"
# define bgLBL "\x1b[104m"
# define bgPIN "\x1b[105m"
# define bgLCY "\x1b[106m"
# define bgBRI "\x1b[107m"
#else
# define cBLK ""
# define cRED ""
# define cGRN ""
# define cBRN ""
# define cBLU ""
# define cMGN ""
# define cCYA ""
# define cLGR ""
# define cGRA ""
# define cLRD ""
# define cLGN ""
# define cYEL ""
# define cLBL ""
# define cPIN ""
# define cLCY ""
# define cBRI ""
# define cRST ""
# define bgBLK ""
# define bgRED ""
# define bgGRN ""
# define bgBRN ""
# define bgBLU ""
# define bgMGN ""
# define bgCYA ""
# define bgLGR ""
# define bgGRA ""
# define bgLRD ""
# define bgLGN ""
# define bgYEL ""
# define bgLBL ""
# define bgPIN ""
# define bgLCY ""
# define bgBRI ""
#endif /* ^USE_COLOR */
/*************************
* Box drawing sequences *
*************************/
#ifdef FANCY_BOXES
# define SET_G1 "\x1b)0" /* Set G1 for box drawing */
# define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
# define bSTART "\x0e" /* Enter G1 drawing mode */
# define bSTOP "\x0f" /* Leave G1 drawing mode */
# define bH "q" /* Horizontal line */
# define bV "x" /* Vertical line */
# define bLT "l" /* Left top corner */
# define bRT "k" /* Right top corner */
# define bLB "m" /* Left bottom corner */
# define bRB "j" /* Right bottom corner */
# define bX "n" /* Cross */
# define bVR "t" /* Vertical, branch right */
# define bVL "u" /* Vertical, branch left */
# define bHT "v" /* Horizontal, branch top */
# define bHB "w" /* Horizontal, branch bottom */
#else
# define SET_G1 ""
# define RESET_G1 ""
# define bSTART ""
# define bSTOP ""
# define bH "-"
# define bV "|"
# define bLT "+"
# define bRT "+"
# define bLB "+"
# define bRB "+"
# define bX "+"
# define bVR "+"
# define bVL "+"
# define bHT "+"
# define bHB "+"
#endif /* ^FANCY_BOXES */
/***********************
* Misc terminal codes *
***********************/
#define TERM_HOME "\x1b[H"
#define TERM_CLEAR TERM_HOME "\x1b[2J"
#define cEOL "\x1b[0K"
#define CURSOR_HIDE "\x1b[?25l"
#define CURSOR_SHOW "\x1b[?25h"
/************************
* Debug & error macros *
************************/
/* Just print stuff to the appropriate stream. */
#ifdef MESSAGES_TO_STDOUT
# define SAYF(x...) printf(x)
#else
# define SAYF(x...) fprintf(stderr, x)
#endif /* ^MESSAGES_TO_STDOUT */
/* Show a prefixed warning. */
#define WARNF(x...) do { \
SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \
SAYF(cRST "\n"); \
} while (0)
/* Show a prefixed "doing something" message. */
#define ACTF(x...) do { \
SAYF(cLBL "[*] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
/* Show a prefixed "success" message. */
#define OKF(x...) do { \
SAYF(cLGN "[+] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
/* Show a prefixed fatal error message (not used in afl). */
#define BADF(x...) do { \
SAYF(cLRD "\n[-] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
/* Die with a verbose non-OS fatal error message. */
#define FATAL(x...) do { \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
cBRI x); \
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \
__FUNCTION__, __FILE__, __LINE__); \
exit(1); \
} while (0)
/* Die by calling abort() to provide a core dump. */
#define ABORT(x...) do { \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
cBRI x); \
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \
__FUNCTION__, __FILE__, __LINE__); \
abort(); \
} while (0)
/* Die while also including the output of perror(). */
#define PFATAL(x...) do { \
fflush(stdout); \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \
cBRI x); \
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \
__FUNCTION__, __FILE__, __LINE__); \
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
exit(1); \
} while (0)
/* Die with FAULT() or PFAULT() depending on the value of res (used to
interpret different failure modes for read(), write(), etc). */
#define RPFATAL(res, x...) do { \
if (res < 0) PFATAL(x); else FATAL(x); \
} while (0)
/* Error-checking versions of read() and write() that call RPFATAL() as
appropriate. */
#define ck_write(fd, buf, len, fn) do { \
u32 _len = (len); \
s32 _res = write(fd, buf, _len); \
if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \
} while (0)
#define ck_read(fd, buf, len, fn) do { \
u32 _len = (len); \
s32 _res = read(fd, buf, _len); \
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
} while (0)
#endif /* ! _HAVE_DEBUG_H */

104
third_party/afl/hash.h vendored Normal file
View File

@ -0,0 +1,104 @@
/*
american fuzzy lop - hashing function
-------------------------------------
The hash32() function is a variant of MurmurHash3, a good
non-cryptosafe hashing function developed by Austin Appleby.
For simplicity, this variant does *NOT* accept buffer lengths
that are not divisible by 8 bytes. The 32-bit version is otherwise
similar to the original; the 64-bit one is a custom hack with
mostly-unproven properties.
Austin's original code is public domain.
Other code written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
*/
#ifndef _HAVE_HASH_H
#define _HAVE_HASH_H
#include "types.h"
#ifdef __x86_64__
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
static inline u32 hash32(const void* key, u32 len, u32 seed) {
const u64* data = (u64*)key;
u64 h1 = seed ^ len;
len >>= 3;
while (len--) {
u64 k1 = *data++;
k1 *= 0x87c37b91114253d5ULL;
k1 = ROL64(k1, 31);
k1 *= 0x4cf5ad432745937fULL;
h1 ^= k1;
h1 = ROL64(h1, 27);
h1 = h1 * 5 + 0x52dce729;
}
h1 ^= h1 >> 33;
h1 *= 0xff51afd7ed558ccdULL;
h1 ^= h1 >> 33;
h1 *= 0xc4ceb9fe1a85ec53ULL;
h1 ^= h1 >> 33;
return h1;
}
#else
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
static inline u32 hash32(const void* key, u32 len, u32 seed) {
const u32* data = (u32*)key;
u32 h1 = seed ^ len;
len >>= 2;
while (len--) {
u32 k1 = *data++;
k1 *= 0xcc9e2d51;
k1 = ROL32(k1, 15);
k1 *= 0x1b873593;
h1 ^= k1;
h1 = ROL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1;
}
#endif /* ^__x86_64__ */
#endif /* !_HAVE_HASH_H */

View File

@ -0,0 +1,306 @@
/*
american fuzzy lop - LLVM instrumentation bootstrap
---------------------------------------------------
Written by Laszlo Szekeres <lszekeres@google.com> and
Michal Zalewski <lcamtuf@google.com>
LLVM integration design comes from Laszlo Szekeres.
Copyright 2015, 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This code is the rewrite of afl-as.h's main_payload.
*/
#include "../config.h"
#include "../types.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/types.h>
/* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode.
Basically, we need to make sure that the forkserver is initialized after
the LLVM-generated runtime initialization pass, not before. */
#ifdef USE_TRACE_PC
# define CONST_PRIO 5
#else
# define CONST_PRIO 0
#endif /* ^USE_TRACE_PC */
/* Globals needed by the injected instrumentation. The __afl_area_initial region
is used for instrumentation output before __afl_map_shm() has a chance to run.
It will end up as .comm, so it shouldn't be too wasteful. */
u8 __afl_area_initial[MAP_SIZE];
u8* __afl_area_ptr = __afl_area_initial;
__thread u32 __afl_prev_loc;
/* Running in persistent mode? */
static u8 is_persistent;
/* SHM setup. */
static void __afl_map_shm(void) {
u8 *id_str = getenv(SHM_ENV_VAR);
/* If we're running under AFL, attach to the appropriate region, replacing the
early-stage __afl_area_initial region that is needed to allow some really
hacky .init code to work correctly in projects such as OpenSSL. */
if (id_str) {
u32 shm_id = atoi(id_str);
__afl_area_ptr = shmat(shm_id, NULL, 0);
/* Whooooops. */
if (__afl_area_ptr == (void *)-1) _exit(1);
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us. */
__afl_area_ptr[0] = 1;
}
}
/* Fork server logic. */
static void __afl_start_forkserver(void) {
static u8 tmp[4];
s32 child_pid;
u8 child_stopped = 0;
/* Phone home and tell the parent that we're OK. If parent isn't there,
assume we're not running in forkserver mode and just execute program. */
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
while (1) {
u32 was_killed;
int status;
/* Wait for parent by reading from the pipe. Abort if read fails. */
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
/* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old
process. */
if (child_stopped && was_killed) {
child_stopped = 0;
if (waitpid(child_pid, &status, 0) < 0) _exit(1);
}
if (!child_stopped) {
/* Once woken up, create a clone of our process. */
child_pid = fork();
if (child_pid < 0) _exit(1);
/* In child process: close fds, resume execution. */
if (!child_pid) {
close(FORKSRV_FD);
close(FORKSRV_FD + 1);
return;
}
} else {
/* Special handling for persistent mode: if the child is alive but
currently stopped, simply restart it with SIGCONT. */
kill(child_pid, SIGCONT);
child_stopped = 0;
}
/* In parent process: write PID to pipe, then wait for child. */
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) _exit(1);
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0)
_exit(1);
/* In persistent mode, the child stops itself with SIGSTOP to indicate
a successful run. In this case, we want to wake it up without forking
again. */
if (WIFSTOPPED(status)) child_stopped = 1;
/* Relay wait status to pipe, then loop back. */
if (write(FORKSRV_FD + 1, &status, 4) != 4) _exit(1);
}
}
/* A simplified persistent mode handler, used as explained in README.llvm. */
int __afl_persistent_loop(unsigned int max_cnt) {
static u8 first_pass = 1;
static u32 cycle_cnt;
if (first_pass) {
/* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
On subsequent calls, the parent will take care of that, but on the first
iteration, it's our job to erase any trace of whatever happened
before the loop. */
if (is_persistent) {
memset(__afl_area_ptr, 0, MAP_SIZE);
__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;
}
cycle_cnt = max_cnt;
first_pass = 0;
return 1;
}
if (is_persistent) {
if (--cycle_cnt) {
raise(SIGSTOP);
__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;
return 1;
} else {
/* When exiting __AFL_LOOP(), make sure that the subsequent code that
follows the loop is not traced. We do that by pivoting back to the
dummy output region. */
__afl_area_ptr = __afl_area_initial;
}
}
return 0;
}
/* This one can be called from user code when deferred forkserver mode
is enabled. */
void __afl_manual_init(void) {
static u8 init_done;
if (!init_done) {
__afl_map_shm();
__afl_start_forkserver();
init_done = 1;
}
}
/* Proper initialization routine. */
__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
is_persistent = !!getenv(PERSIST_ENV_VAR);
if (getenv(DEFER_ENV_VAR)) return;
__afl_manual_init();
}
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
It remains non-operational in the traditional, plugin-backed LLVM mode.
For more info about 'trace-pc-guard', see README.llvm.
The first function (__sanitizer_cov_trace_pc_guard) is called back on every
edge (as opposed to every basic block). */
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
__afl_area_ptr[*guard]++;
}
/* Init callback. Populates instrumentation IDs. Note that we're using
ID of 0 as a special value to indicate non-instrumented bits. That may
still touch the bitmap, but in a fairly harmless way. */
void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
u32 inst_ratio = 100;
u8* x;
if (start == stop || *start) return;
x = getenv("AFL_INST_RATIO");
if (x) inst_ratio = atoi(x);
if (!inst_ratio || inst_ratio > 100) {
fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
abort();
}
/* Make sure that the first element in the range is always set - we use that
to avoid duplicate calls (which can happen as an artifact of the underlying
implementation in LLVM). */
*(start++) = R(MAP_SIZE - 1) + 1;
while (start < stop) {
if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;
else *start = 0;
start++;
}
}

86
third_party/afl/types.h vendored Normal file
View File

@ -0,0 +1,86 @@
/*
american fuzzy lop - type definitions and minor macros
------------------------------------------------------
Written and maintained by Michal Zalewski <lcamtuf@google.com>
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
*/
#ifndef _HAVE_TYPES_H
#define _HAVE_TYPES_H
#include <stdint.h>
#include <stdlib.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
/*
Ugh. There is an unintended compiler / glibc #include glitch caused by
combining the u64 type an %llu in format strings, necessitating a workaround.
In essence, the compiler is always looking for 'unsigned long long' for %llu.
On 32-bit systems, the u64 type (aliased to uint64_t) is expanded to
'unsigned long long' in <bits/types.h>, so everything checks out.
But on 64-bit systems, it is #ifdef'ed in the same file as 'unsigned long'.
Now, it only happens in circumstances where the type happens to have the
expected bit width, *but* the compiler does not know that... and complains
about 'unsigned long' being unsafe to pass to %llu.
*/
#ifdef __x86_64__
typedef unsigned long long u64;
#else
typedef uint64_t u64;
#endif /* ^__x86_64__ */
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
#ifndef MIN
# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a))
# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
#endif /* !MIN */
#define SWAP16(_x) ({ \
u16 _ret = (_x); \
(u16)((_ret << 8) | (_ret >> 8)); \
})
#define SWAP32(_x) ({ \
u32 _ret = (_x); \
(u32)((_ret << 24) | (_ret >> 24) | \
((_ret << 8) & 0x00FF0000) | \
((_ret >> 8) & 0x0000FF00)); \
})
#ifdef AFL_LLVM_PASS
# define AFL_R(x) (random() % (x))
#else
# define R(x) (random() % (x))
#endif /* ^AFL_LLVM_PASS */
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
#define MEM_BARRIER() \
__asm__ volatile("" ::: "memory")
#define likely(_x) __builtin_expect(!!(_x), 1)
#define unlikely(_x) __builtin_expect(!!(_x), 0)
#endif /* ! _HAVE_TYPES_H */

View File

@ -11,12 +11,6 @@ alias(
visibility = ["//visibility:public"],
)
sh_binary(
name = "fuzzit_wrapper",
srcs = ["fuzzit_wrapper.sh"],
data = ["@fuzzit_linux//:fuzzit"],
)
sh_binary(
name = "fuzz_wrapper",
srcs = ["fuzz_wrapper.sh"],

View File

@ -1,19 +0,0 @@
#!/bin/bash
set -e
# run fuzzing regression or upload to Fuzzit for long running fuzzing job depending on whether FUZZIT_API_KEY is set
FUZZIT="${TEST_SRCDIR}/fuzzit_linux/fuzzit"
FUZZER_BINARY=$1
FUZZIT_TARGET_NAME="$(basename $1 | sed -e s/_fuzz_test_with_libfuzzer$// -e s/_/-/g)"
if [[ ! -z "${FUZZIT_API_KEY}" ]]; then
"${FUZZIT}" create target --skip-if-exists --public-corpus prysmaticlabs-gh/"${FUZZIT_TARGET_NAME}"
# Run fuzzing first so this is not affected by local-regression timeout
"${FUZZIT}" create job --skip-if-not-exists --host "${PRYSM_BUILD_IMAGE}" --type fuzzing prysmaticlabs-gh/"${FUZZIT_TARGET_NAME}" "${FUZZER_BINARY}"
fi
"${FUZZIT}" create job --skip-if-not-exists --host "${PRYSM_BUILD_IMAGE}" --type local-regression prysmaticlabs-gh/"${FUZZIT_TARGET_NAME}" "${FUZZER_BINARY}"

View File

@ -8,7 +8,7 @@ def go_library(name, **kwargs):
go_goopts = kwargs["gc_goopts"]
gc_goopts += select({
"@prysm//tools/go:libfuzz_enabled": ["-d=libfuzzer"],
"@prysm//tools/go:libfuzz_enabled": ["-d=libfuzzer,checkptr"],
"//conditions:default": [],
})

View File

@ -38,7 +38,7 @@ func main() {
def _gen_fuzz_main_impl(ctx):
if ctx.var.get("gotags") != "libfuzzer":
fail("gotags must be set to libfuzzer. Use --config=fuzz or --config=fuzzit.")
if ctx.var.get("gc_goopts") != "-d=libfuzzer":
if "libfuzzer" not in ctx.var.get("gc_goopts"):
fail("gc_goopts must be set to -d=libfuzzer. Use --config=fuzz or --config=fuzzit.")
pkg = ctx.attr.target_pkg
@ -57,6 +57,40 @@ gen_fuzz_main = rule(
},
)
fuzzer_options_tpl = """[libfuzzer]
max_len=%d
"""
def _generate_libfuzzer_config(ctx):
output_file_name = ctx.label.name + ".options"
output = fuzzer_options_tpl % (
ctx.attr.max_len,
)
output_file = ctx.actions.declare_file(output_file_name)
ctx.actions.write(output_file, output)
return [DefaultInfo(files = depset([output_file]))]
gen_libfuzzer_config = rule(
implementation = _generate_libfuzzer_config,
attrs = {
"max_len": attr.int(default = 0),
},
)
def _upload_to_gcp_impl(ctx):
return [
DefaultInfo(),
]
upload_to_gcp = rule(
implementation = _upload_to_gcp_impl,
attrs = {
"gcp_bucket": attr.string(mandatory = True),
"libfuzzer_bundle": attr.label(mandatory = True),
"afl_bundle": attr.label(mandatory = True),
},
)
def go_fuzz_test(
name,
corpus,
@ -64,7 +98,8 @@ def go_fuzz_test(
importpath,
func = "Fuzz",
repository = "",
input_size = 0,
max_len = 0,
gcp_bucket = "gs://builds.prysmaticlabs.appspot.com",
size = "medium",
tags = [],
**kwargs):
@ -85,6 +120,10 @@ def go_fuzz_test(
testonly = 1,
visibility = ["//visibility:private"],
)
gen_libfuzzer_config(
name = name + "_options",
max_len = max_len,
)
go_binary(
name = name + "_binary",
srcs = [name + "_libfuzz_main"],
@ -117,13 +156,44 @@ def go_fuzz_test(
corpus_name = corpus
additional_args = []
if input_size > 0:
additional_args += ["-max_len=%s" % input_size]
if max_len > 0:
additional_args += ["-max_len=%s" % max_len]
native.cc_test(
name = name + "_with_afl",
linkopts = [
"-fsanitize=address",
"-fsanitize-coverage=trace-pc-guard",
],
linkstatic = 1,
testonly = 1,
srcs = [":" + name],
deps = [
"@herumi_bls_eth_go_binary//:lib",
"//third_party/afl:fuzzing_engine",
],
tags = ["manual", "fuzzer"] + tags,
)
native.genrule(
name = name + "_afl_bundle",
outs = [name + "_afl_bundle.zip"],
srcs = [
"//third_party/afl:libs",
":" + name + "_with_afl",
],
cmd = "cp $(location :" + name + "_with_afl) fuzzer; $(location @bazel_tools//tools/zip:zipper) cf $@ $(locations //third_party/afl:libs) fuzzer",
tools = [
"@bazel_tools//tools/zip:zipper",
],
testonly = 1,
tags = ["manual"] + tags,
)
native.cc_test(
name = name + "_with_libfuzzer",
linkopts = ["-fsanitize=fuzzer,address"],
copts = ["-fsantize=fuzzer,address"],
copts = ["-fsanitize=fuzzer,address"],
linkstatic = 1,
testonly = 1,
srcs = [":" + name],
@ -138,3 +208,26 @@ def go_fuzz_test(
data = [corpus_name],
timeout = "eternal",
)
native.genrule(
name = name + "_libfuzzer_bundle",
outs = [name + "_libfuzzer_bundle.zip"],
srcs = [
":" + name + "_with_libfuzzer",
":" + name + "_options",
],
cmd = "cp $(location :" + name + "_with_libfuzzer) fuzzer; " +
"cp $(location :" + name + "_options) fuzzer.options; " +
"$(location @bazel_tools//tools/zip:zipper) cf $@ fuzzer fuzzer.options",
tools = ["@bazel_tools//tools/zip:zipper"],
testonly = 1,
tags = ["manual"] + tags,
)
upload_to_gcp(
name = name + "_uploader",
gcp_bucket = gcp_bucket,
afl_bundle = ":" + name + "_afl_bundle",
libfuzzer_bundle = ":" + name + "_libfuzzer_bundle",
tags = ["manual"] + tags,
)