-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Objectives
- Provide a common interface to fuzz all HTTP filters.
- Make it easy for filter authors to create their own filter fuzz target.
- Create a healthy fuzzer from the beginning.
- These should be optimized for speed, and ideally they should be stateless at first.
- Sizeable corpus data (see Inputs and Corpus)
Target
Starting with just Decoder Filters, the fuzz target should execute each of the decode methods, decodeHeaders, decodeData, decodeTrailers and decodeMetadata. The inputs to these methods should be fuzzed. The common interface should implement these methods. For example:
class DecoderFuzzFilter {
public:
DecoderFuzzFilter();
void fuzz(const fuzz::HttpData& request) {
filter_->decodeHeaders(request.headers(), false);
filter_->decodeData(request.data(), false);
filter_->decodeTrailers(request.trailers());
filter_->decodeMetadata(request.metadata());
}
NiceMock<Http::MockStreamDecoderFilterCallbacks> callbacks_;
std::unique_ptr<Http::StreamDecoderFilter> filter_;
}
Writers of individual filter fuzzers would be responsible for extending this base class, writing a constructor that initializes the filter_, and then writing the fuzz target like:
DEFINE_PROTO_FUZZER(const test::extensions::filters::http::buffer::BufferFuzzTestCase& input) {
BufferFuzzFilter filter(input.config());
filter.fuzz(input.request());
}
Inputs and Corpus
Input will consist of:
- Filter configuration data.
- HTTP request/response data (headers, data, trailers, and metadata)
message ExampleFilterFuzzTestCase {
envoy.config.filter.http.example.v2.ExampleFilterConfig config = 1;
HttpData request = 2;
}
HttpData is the untrusted data that the filters encode and decode. This data could be used in every filter fuzzer. For example, a HeaderMap with missing Host header should be tested against all filters to ensure that they don't hit a null pointer dereference. If the common HTTP filter fuzz directory /test/extensions/filters/http/common/fuzz/http_data contained these common request protobufs, and each filter contained it's own default_configuration, we could use a bazel genrule to concatenate each common request file with the default config to produce valid corpus entries.
Future Goals
Here are some future goals, and I'm most interested to see how to design the framework to adapt to these goals easily.
- Support fuzzing a filter chain.
- Support stateful fuzzing of the filters.
- Support filter specific validations.
- As a first step, the fuzzers should simply running
decodeandencodemethods of the filters to catch crashes. If the behavior is easy enough to predict or model with an abstract filter, filter classes can extend the base class to define their ownvalidate(input)methods to expect behavior. (TODO: add this to the POC for buffer_filter_test)
- As a first step, the fuzzers should simply running
See #9400 for a POC, but there are many TODOs in this.
Open Questions
- How should
end_streambe handled? They could be handled with bools in the input, or the design could take a more stateful turn and we could instead fuzz a valid sequence ofHeaders,DataandTrailers.