Skip to content
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

Merged
merged 13 commits into from
Dec 21, 2020

Conversation

asraa
Copy link
Contributor

@asraa asraa commented Sep 29, 2020

Signed-off-by: Asra Ali asraa@google.com

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

Signed-off-by: Asra Ali <asraa@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
@asraa asraa requested a review from zasweq September 29, 2020 20:12
@asraa
Copy link
Contributor Author

asraa commented Sep 29, 2020

/cc @antoniovicente @yanavlasov

Copy link
Member

@htuch htuch left a 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.
Copy link
Member

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?

Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor Author

@asraa asraa Oct 2, 2020

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);
Copy link
Member

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).

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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."

@asraa
Copy link
Contributor Author

asraa commented Sep 29, 2020

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.

@htuch
Copy link
Member

htuch commented Sep 29, 2020

@asraa up to you, happy to review in either state, just want to throw it out there.

asraa added 2 commits October 2, 2020 16:06
Signed-off-by: Asra Ali <asraa@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
htuch
htuch previously approved these changes Oct 2, 2020
Copy link
Member

@htuch htuch left a 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);
Copy link
Member

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 <asraa@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
htuch
htuch previously approved these changes Oct 7, 2020
Signed-off-by: Asra Ali <asraa@google.com>
asraa added 2 commits October 14, 2020 09:22
Signed-off-by: Asra Ali <asraa@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
@github-actions
Copy link

github-actions bot commented Dec 9, 2020

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!

@github-actions github-actions bot added the stale stalebot believes this issue/PR has not been touched recently label Dec 9, 2020
@htuch
Copy link
Member

htuch commented Dec 9, 2020

@asraa looks like this one was approved but there were some final CI check issues blocking merge.

@github-actions github-actions bot removed the stale stalebot believes this issue/PR has not been touched recently label Dec 10, 2020
@mattklein123 mattklein123 assigned jmarantz and unassigned htuch Dec 18, 2020
@jmarantz
Copy link
Contributor

ping when ready for review

/wait

Signed-off-by: Asra Ali <asraa@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
Copy link
Contributor

@jmarantz jmarantz left a 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.

test/common/http/http2/hpack_fuzz_test.cc Outdated Show resolved Hide resolved
test/common/http/http2/hpack_fuzz_test.cc Outdated Show resolved Hide resolved
test/common/http/http2/hpack_fuzz_test.cc Outdated Show resolved Hide resolved
test/common/http/http2/hpack_fuzz_test.cc Outdated Show resolved Hide resolved
Signed-off-by: Asra Ali <asraa@google.com>
jmarantz
jmarantz previously approved these changes Dec 21, 2020
// 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};
Copy link
Contributor

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.

Copy link
Contributor Author

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 <asraa@google.com>
@mattklein123 mattklein123 merged commit e90d674 into envoyproxy:master Dec 21, 2020
mpuncel added a commit to mpuncel/envoy that referenced this pull request Dec 22, 2020
* 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 <mpuncel@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants