-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[fuzz] add fuzz tests for hpack encoding and decoding #13315
Conversation
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. What does the performance looks like? Maybe file a bug in nghttp2 repo for them to create a similar fuzzer?
ENVOY_LOG_MISC(trace, "Encoding headers {}", headers); | ||
const absl::optional<Buffer::OwnedImpl> payload = encodeHeaders(input_nv); | ||
if (!payload.has_value() || payload.value().getRawSlices().size() == 0) { | ||
// An empty header map produces no payload, skip decoding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the empty payload is due to some nghttp2 failure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If nghttp2 throws a negative error code, payload will be a nullopt, which will return. I think this boils down to whether the team wants scope of fuzzer to be to simply check if encode/decode always work by sending headers through both and checking for equality, or if the team wants to explore header values that could cause nghttp2 to throw errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think catching unexpected errors from nghttp2 would be a nice bonus, since we have a lot of ASSERTs in places around the returns from nghttp2 calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Empty payload is now handled earlier, since in our codebase and here we pass .data()
, which, if nullptr because of empty header map, will crash nghttp2 at runtime. That's kinda bad, but I went through the codebase and there's nowhere we supply headers via data()
without at least one header. We should add a statement enforcing this though, maybe.
Since empty payload is not worrisome in encode, I catch errors with an ASSERT(result >= 0) now.
DEFINE_PROTO_FUZZER(const test::common::http::http2::HpackTestCase& input) { | ||
// Validate headers. | ||
try { | ||
TestUtility::validate(input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe leave a TODO to make this even faster by skipping LPM and working with direct byte array representing lists of headers (let's say separated by any characters that aren't valid HTTP header vals).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done. using vectors rather than HeaderMap sped it up to about 800, adding verification via qsort and compare brought it back down to 700.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually suggesting to skip using proto entirely, rather than skipping the HeaderMap, but up to you, I think it's fine to merge as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, acked. The TODO about that is at the top of the file "// TODO(asraa): Speed up by using raw byte input and seperators rather than protobuf input."
Good question, performance started at around 650-700, and tapered down to 450. The obvious way to optimize would be to remove the header map construction and just stick to comparisons between protos or the nv arrays. originally i just had the comparison go test::fuzz::Headers input to test::fuzz::Headers decoded headers. But it has the overhead of protos or array sorting I will do this, it's worth getting it to 1000. |
@asraa up to you, happy to review in either state, just want to throw it out there. |
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
DEFINE_PROTO_FUZZER(const test::common::http::http2::HpackTestCase& input) { | ||
// Validate headers. | ||
try { | ||
TestUtility::validate(input); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually suggesting to skip using proto entirely, rather than skipping the HeaderMap, but up to you, I think it's fine to merge as is.
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
This pull request has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
@asraa looks like this one was approved but there were some final CI check issues blocking merge. |
ping when ready for review /wait |
Signed-off-by: Asra Ali <[email protected]>
Signed-off-by: Asra Ali <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks fine modulo some nits.
Signed-off-by: Asra Ali <[email protected]>
// TODO(asraa): Consider adding flags in fuzzed input. | ||
nva[i++] = {const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(header.key().data())), | ||
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(header.value().data())), | ||
header.key().size(), header.value().size(), /*flags = */ 0}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we've not been using this pattern of arg-documentation in Envoy. However I like the previous state of having flags as a variable that's passed in, as it's self-documenting. The flags var could be const though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be all set (only diff)
Signed-off-by: Asra Ali <[email protected]>
* master: (30 commits) Deflaked: Guarddog_impl_test (envoyproxy#14475) [fuzz] add fuzz tests for hpack encoding and decoding (envoyproxy#13315) [filters] Prevent a filter from sending local reply and continue (envoyproxy#14416) oauth2: improving coverage (envoyproxy#14479) owners: Change dio email address (envoyproxy#14498) macos build: Fix ninja install (envoyproxy#14495) http: use OptRef helper to reduce some boilerplate (envoyproxy#14361) doc: update test/integration/README.md (envoyproxy#14485) server: wait workers to start before draining parent. (envoyproxy#14319) api: relax inline_string length limitation in DataSource (envoyproxy#14461) oauth: properly stop filter chain when a response was sent (envoyproxy#14476) listener: deprecate use_proxy_proto (envoyproxy#14406) deps: update cel and remove a patch (envoyproxy#14473) preconnect: rename: (envoyproxy#14474) coverage: ratcheting limits (envoyproxy#14472) grpc mux: fix sending node again after stream is reset (envoyproxy#14080) [test] Replace printers_include with printers_lib. (envoyproxy#14442) tcp: nodelay in the new pool (envoyproxy#14453) test: replace mock_methodn macros with mock_method (envoyproxy#14450) tcp: extending tcp integration test (envoyproxy#14451) ... Signed-off-by: Michael Puncel <[email protected]>
Signed-off-by: Asra Ali [email protected]
Commit Message: Adds tests for HPACK encoding and decoding with nghttp2. The fuzz test takes a set of headers, encodes them, decodes, and checks the results is equal to the input.
Performance: 700-800 exec/sec
Risk Level: Low
Testing: Ran with libfuzzer