-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move more fuzzers over to native fuzzers
- Loading branch information
1 parent
7306d75
commit 062af0b
Showing
18 changed files
with
387 additions
and
739 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package gateway | ||
|
||
import ( | ||
"testing" | ||
|
||
"istio.io/istio/pkg/fuzz" | ||
) | ||
|
||
func FuzzConvertResources(f *testing.F) { | ||
fuzz.BaseCases(f) | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
fg := fuzz.New(t, data) | ||
r := fuzz.Struct[KubernetesResources](fg) | ||
convertResources(r) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
pilot/pkg/networking/core/v1alpha3/envoyfilter/fuzz_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package envoyfilter | ||
|
||
import ( | ||
"testing" | ||
|
||
cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" | ||
|
||
meshconfig "istio.io/api/mesh/v1alpha1" | ||
networking "istio.io/api/networking/v1alpha3" | ||
"istio.io/istio/pilot/pkg/model" | ||
"istio.io/istio/pilot/pkg/serviceregistry/memory" | ||
"istio.io/istio/pkg/config/host" | ||
"istio.io/istio/pkg/fuzz" | ||
) | ||
|
||
func FuzzApplyClusterMerge(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, patchCount int, hostname string, data []byte) { | ||
fg := fuzz.New(t, data) | ||
patches := fuzz.Slice[*networking.EnvoyFilter_EnvoyConfigObjectPatch](fg, patchCount%30) | ||
proxy := fuzz.Struct[*model.Proxy](fg) | ||
mesh := fuzz.Struct[*meshconfig.MeshConfig](fg) | ||
c := fuzz.Struct[*cluster.Cluster](fg) | ||
|
||
serviceDiscovery := memory.NewServiceDiscovery() | ||
env := newTestEnvironment(serviceDiscovery, mesh, buildEnvoyFilterConfigStore(patches)) | ||
push := model.NewPushContext() | ||
push.InitContext(env, nil, nil) | ||
efw := push.EnvoyFilters(proxy) | ||
ApplyClusterMerge(networking.EnvoyFilter_GATEWAY, efw, c, []host.Name{host.Name(hostname)}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package v1alpha3 | ||
|
||
import ( | ||
"testing" | ||
|
||
route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" | ||
|
||
"istio.io/istio/pilot/pkg/model" | ||
"istio.io/istio/pkg/fuzz" | ||
) | ||
|
||
func FuzzBuildGatewayListeners(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, patchCount int, hostname string, data []byte) { | ||
fg := fuzz.New(t, data) | ||
proxy := fuzz.Struct[*model.Proxy](fg) | ||
to := fuzz.Struct[TestOptions](fg) | ||
lb := fuzz.Struct[*ListenerBuilder](fg) | ||
cg := NewConfigGenTest(t, to) | ||
lb.node = cg.SetupProxy(proxy) | ||
lb.push = cg.PushContext() | ||
cg.ConfigGen.buildGatewayListeners(lb) | ||
}) | ||
} | ||
|
||
func FuzzBuildSidecarOutboundHTTPRouteConfig(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, patchCount int, hostname string, data []byte) { | ||
fg := fuzz.New(t, data) | ||
proxy := fuzz.Struct[*model.Proxy](fg) | ||
to := fuzz.Struct[TestOptions](fg) | ||
cg := NewConfigGenTest(t, to) | ||
req := fuzz.Struct[*model.PushRequest](fg) | ||
req.Push = cg.PushContext() | ||
vHostCache := make(map[int][]*route.VirtualHost) | ||
cg.ConfigGen.buildSidecarOutboundHTTPRouteConfig(cg.SetupProxy(proxy), req, "80", vHostCache, nil, nil) | ||
}) | ||
} | ||
|
||
func FuzzBuildSidecarOutboundListeners(f *testing.F) { | ||
f.Fuzz(func(t *testing.T, patchCount int, hostname string, data []byte) { | ||
fg := fuzz.New(t, data) | ||
proxy := fuzz.Struct[*model.Proxy](fg) | ||
to := fuzz.Struct[TestOptions](fg) | ||
cg := NewConfigGenTest(t, to) | ||
req := fuzz.Struct[*model.PushRequest](fg) | ||
req.Push = cg.PushContext() | ||
NewListenerBuilder(proxy, cg.env.PushContext).buildSidecarOutboundListeners(cg.SetupProxy(proxy), cg.env.PushContext) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package builder | ||
|
||
import ( | ||
"testing" | ||
|
||
"istio.io/istio/pilot/pkg/model" | ||
"istio.io/istio/pilot/pkg/security/trustdomain" | ||
"istio.io/istio/pkg/fuzz" | ||
) | ||
|
||
func FuzzBuildHTTP(f *testing.F) { | ||
fuzz.BaseCases(f) | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
fg := fuzz.New(t, data) | ||
bundle := fuzz.Struct[trustdomain.Bundle](fg) | ||
push := fuzz.Struct[*model.PushContext](fg, validatePush) | ||
node := fuzz.Struct[*model.Proxy](fg) | ||
policies := push.AuthzPolicies.ListAuthorizationPolicies(node.ConfigNamespace, node.Metadata.Labels) | ||
option := fuzz.Struct[Option](fg) | ||
New(bundle, push, policies, option).BuildHTTP() | ||
}) | ||
} | ||
|
||
func FuzzBuildTCP(f *testing.F) { | ||
fuzz.BaseCases(f) | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
fg := fuzz.New(t, data) | ||
bundle := fuzz.Struct[trustdomain.Bundle](fg) | ||
push := fuzz.Struct[*model.PushContext](fg, validatePush) | ||
node := fuzz.Struct[*model.Proxy](fg) | ||
policies := push.AuthzPolicies.ListAuthorizationPolicies(node.ConfigNamespace, node.Metadata.Labels) | ||
option := fuzz.Struct[Option](fg) | ||
New(bundle, push, policies, option).BuildTCP() | ||
}) | ||
} | ||
|
||
func validatePush(in *model.PushContext) bool { | ||
if in == nil { | ||
return false | ||
} | ||
if in.AuthzPolicies == nil { | ||
return false | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package controller | ||
|
||
import ( | ||
"testing" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
|
||
"istio.io/istio/pilot/pkg/model" | ||
"istio.io/istio/pkg/fuzz" | ||
"istio.io/istio/pkg/network" | ||
) | ||
|
||
func FuzzKubeController(f *testing.F) { | ||
fuzz.BaseCases(f) | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
fg := fuzz.New(t, data) | ||
networkID := network.ID("fakeNetwork") | ||
fco := fuzz.Struct[FakeControllerOptions](fg) | ||
fco.SkipRun = true | ||
controller, _ := NewFakeControllerWithOptions(t, fco) | ||
controller.network = networkID | ||
|
||
p := fuzz.Struct[*corev1.Pod](fg) | ||
controller.pods.onEvent(p, model.EventAdd) | ||
s := fuzz.Struct[*corev1.Service](fg) | ||
controller.onServiceEvent(s, model.EventAdd) | ||
e := fuzz.Struct[*corev1.Endpoints](fg) | ||
controller.endpoints.onEvent(e, model.EventAdd) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Fuzzing Istio Code | ||
|
||
The Istio (Go) code base is fuzzed using native Go fuzzing. | ||
For general docs on how to fuzz in Go, see [Getting started with fuzzing](https://go.dev/doc/tutorial/fuzz). | ||
|
||
## Writing a test | ||
|
||
Generally, writing a fuzz test for Istio is the same as any other Go program. | ||
However, because most of our fuzzing is based on complex structs rather than the primitives Go supports natively, | ||
the `pkg/fuzz` package contains a number of helpers to fuzz. | ||
|
||
Here is an example: | ||
|
||
```go | ||
// Define a new fuzzer. Must have Fuzz prefix | ||
func FuzzBuildHTTP(f *testing.F) { | ||
fuzz.BaseCases(f) // Insert basic cases so a few trivial cases run in presubmit | ||
f.Fuzz(func(t *testing.T, data []byte) { | ||
fg := fuzz.New(t, data) | ||
// Setup a few structs for testing | ||
bundle := fuzz.Struct[trustdomain.Bundle](fg) | ||
// This one has a custom validator | ||
push := fuzz.Struct[*model.PushContext](fg, validatePush) | ||
// *model.Proxy, and other types, implement the fuzz.Validator interface and already validate some basics. | ||
node := fuzz.Struct[*model.Proxy](fg) | ||
option := fuzz.Struct[Option](fg) | ||
|
||
// Run our actual test code. In this case, we are just checking nothing crashes. | ||
// In other tests, explicit assertions may be helpful. | ||
policies := push.AuthzPolicies.ListAuthorizationPolicies(node.ConfigNamespace, node.Metadata.Labels) | ||
New(bundle, push, policies, option).BuildHTTP() | ||
}) | ||
} | ||
``` | ||
|
||
## Running tests | ||
|
||
Fuzz tests can be run using standard Go tooling: | ||
|
||
```shell | ||
go test ./path/to/pkg -v -run=^$ -fuzz=Fuzz | ||
``` | ||
|
||
## CI testing | ||
|
||
Go fuzzers are run as part of standard unit tests against known test cases (from `f.Add` (which `fuzz.BaseCases` calls), or `testdata`). | ||
For continuous fuzzing, [`OSS-Fuzz`](https://github.com/google/oss-fuzz) continually builds and runs the fuzzers and reports any failures. | ||
These results are private to the Istio Product Security WG until disclosed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package fuzz | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"testing" | ||
|
||
fuzzheaders "github.com/AdaLogics/go-fuzz-headers" | ||
) | ||
|
||
// Helper is a helper struct for fuzzing | ||
type Helper struct { | ||
cf *fuzzheaders.ConsumeFuzzer | ||
t *testing.T | ||
} | ||
|
||
type Validator interface { | ||
// FuzzValidate returns true if the current struct is valid for fuzzing. | ||
FuzzValidate() bool | ||
} | ||
|
||
// New creates a new fuzz.Helper, capable of generating more complex types | ||
func New(t *testing.T, data []byte) Helper { | ||
return Helper{cf: fuzzheaders.NewConsumer(data), t: t} | ||
} | ||
|
||
// Struct generates a Struct. Validation patterns can be passed in - if any return false, the fuzz case is skipped. | ||
// Additionally, if the T implements Validator, it will implicitly be used. | ||
func Struct[T any](h Helper, validators ...func(T) bool) T { | ||
d := new(T) | ||
if err := h.cf.GenerateStruct(d); err != nil { | ||
h.t.Skip(err.Error()) | ||
} | ||
r := *d | ||
validate(h, validators, r) | ||
return r | ||
} | ||
|
||
// Slice generates a slice of Structs | ||
func Slice[T any](h Helper, count int, validators ...func(T) bool) []T { | ||
if count < 0 { | ||
// Make it easier to just pass fuzzer generated counts, typically with %max applied | ||
count *= -1 | ||
} | ||
res := make([]T, 0, count) | ||
for i := 0; i < count; i++ { | ||
d := new(T) | ||
if err := h.cf.GenerateStruct(d); err != nil { | ||
h.t.Skip(err.Error()) | ||
} | ||
r := *d | ||
validate(h, validators, r) | ||
res = append(res, r) | ||
} | ||
return res | ||
} | ||
|
||
func validate[T any](h Helper, validators []func(T) bool, r T) { | ||
if fz, ok := any(r).(Validator); ok { | ||
if !fz.FuzzValidate() { | ||
h.t.Skip(fmt.Sprintf("struct didn't pass validator")) | ||
} | ||
} | ||
for i, v := range validators { | ||
if !v(r) { | ||
h.t.Skipf(fmt.Sprintf("struct didn't pass validator %d", i)) | ||
} | ||
} | ||
} | ||
|
||
// BaseCases inserts a few trivial test cases to do a very brief sanity check of a test that relies on []byte inputs | ||
func BaseCases(f *testing.F) { | ||
for _, c := range [][]byte{ | ||
{}, | ||
[]byte("."), | ||
bytes.Repeat([]byte("."), 1000), | ||
} { | ||
f.Add(c) | ||
} | ||
} |
Oops, something went wrong.