Coverage Report

Created: 2023-11-12 09:30

/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