/proc/self/cwd/test/common/grpc/codec_fuzz_test.cc
Line | Count | Source |
1 | | #include "source/common/buffer/buffer_impl.h" |
2 | | #include "source/common/grpc/codec.h" |
3 | | |
4 | | #include "test/fuzz/fuzz_runner.h" |
5 | | #include "test/fuzz/utility.h" |
6 | | #include "test/proto/helloworld.pb.h" |
7 | | |
8 | | namespace Envoy { |
9 | | namespace Grpc { |
10 | | namespace Fuzz { |
11 | | |
12 | | // Fuzz the Grpc::decode() implementation, validating that decode(encode(x)) == |
13 | | // x for all x, regardless of how the encoded buffer is partitioned. Models |
14 | | // frame boundary conditions and also trailing random crud, which effectively |
15 | | // models line noise input to the decoder as well. |
16 | 590 | DEFINE_FUZZER(const uint8_t* buf, size_t len) { |
17 | 590 | FuzzedDataProvider provider(buf, len); |
18 | | |
19 | | // We probably won't learn a ton more after a few frames worth. |
20 | 590 | const uint32_t num_encode_frames = provider.ConsumeIntegralInRange(0, 3); |
21 | | // Model a buffer containing the wire input to the decoder. |
22 | 590 | Buffer::OwnedImpl wire_buffer; |
23 | | // We populate these proto requests and then encode them into wire_buffer. |
24 | 590 | std::vector<std::unique_ptr<helloworld::HelloRequest>> requests; |
25 | | // Bounding the size of each request somewhat for sanity sake. We are trading |
26 | | // off being able to see what happens at really large sizes, e.g. 16MB, but |
27 | | // that will just be too slow, lots of memcpy. |
28 | 590 | const size_t MaxRequestNameSize = 96 * 1024; |
29 | 1.70k | for (uint32_t i = 0; i < num_encode_frames; ++i) { |
30 | 1.11k | requests.emplace_back(new helloworld::HelloRequest()); |
31 | 1.11k | requests.back()->set_name_bytes(provider.ConsumeRandomLengthString(MaxRequestNameSize)); |
32 | | // Encode the proto to bytes. |
33 | 1.11k | const std::string request_buffer = requests.back()->SerializeAsString(); |
34 | | // Encode the gRPC header. |
35 | 1.11k | std::array<uint8_t, 5> header; |
36 | 1.11k | Encoder encoder; |
37 | 1.11k | encoder.newFrame(GRPC_FH_DEFAULT, request_buffer.size(), header); |
38 | | // Add header and byte representation of request to wire. |
39 | 1.11k | wire_buffer.add(header.data(), 5); |
40 | 1.11k | wire_buffer.add(request_buffer.data(), request_buffer.size()); |
41 | 1.11k | } |
42 | | |
43 | | // Add random crud at the end to see if we can make the decoder unhappy in |
44 | | // non-standard ways. |
45 | 590 | { |
46 | 590 | const std::string crud = provider.ConsumeRandomLengthString(MaxRequestNameSize); |
47 | 590 | wire_buffer.add(crud.data(), crud.size()); |
48 | 590 | } |
49 | | |
50 | 590 | Decoder decoder; |
51 | 590 | std::vector<Frame> frames; |
52 | | // We now decode the wire contents, piecemeal. |
53 | 8.97k | while (wire_buffer.length() > 0) { |
54 | | // We'll try and pick a partition for the remaining content, so that we |
55 | | // enable the fuzzer to explore different ways to cut it. |
56 | 8.38k | const uint64_t decode_length = |
57 | 8.38k | provider.remaining_bytes() == 0 |
58 | 8.38k | ? wire_buffer.length() |
59 | 8.38k | : provider.ConsumeIntegralInRange<uint64_t>(0, wire_buffer.length()); |
60 | 8.38k | Buffer::OwnedImpl decode_buffer; |
61 | 8.38k | decode_buffer.move(wire_buffer, decode_length); |
62 | 8.38k | const bool decode_result = decoder.decode(decode_buffer, frames); |
63 | | // If we have recovered the original frames, we're decoding garbage. It |
64 | | // might end up being a valid frame, but there is no predictability, so just |
65 | | // drain and move on. If we haven't recovered the original frames, we |
66 | | // shouldn't have any errors and should be consuming all of decode_buffer. |
67 | 8.38k | if (frames.size() >= num_encode_frames) { |
68 | 3.49k | decode_buffer.drain(decode_buffer.length()); |
69 | 4.88k | } else { |
70 | 4.88k | FUZZ_ASSERT(decode_result); |
71 | 4.88k | FUZZ_ASSERT(decode_buffer.length() == 0); |
72 | 4.88k | } |
73 | 8.38k | } |
74 | | |
75 | | // Verify that the original requests are correctly decoded. |
76 | 590 | FUZZ_ASSERT(frames.size() >= num_encode_frames); |
77 | 1.70k | for (uint32_t i = 0; i < num_encode_frames; ++i) { |
78 | 1.11k | helloworld::HelloRequest decoded_request; |
79 | 1.11k | FUZZ_ASSERT(decoded_request.ParseFromArray( |
80 | 1.11k | frames[i].data_->linearize(frames[i].data_->length()), frames[i].data_->length())); |
81 | 1.11k | FUZZ_ASSERT(decoded_request.name_bytes() == requests[i]->name_bytes()); |
82 | 1.11k | } |
83 | 590 | } |
84 | | |
85 | | } // namespace Fuzz |
86 | | } // namespace Grpc |
87 | | } // namespace Envoy |