Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/test/common/network/listener_filter_buffer_fuzz_test.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/common/assert.h"
2
#include "source/common/common/logger.h"
3
#include "source/common/network/listener_filter_buffer_impl.h"
4
5
#include "test/common/network/listener_filter_buffer_fuzz.pb.h"
6
#include "test/fuzz/fuzz_runner.h"
7
#include "test/fuzz/utility.h"
8
#include "test/mocks/event/mocks.h"
9
#include "test/mocks/network/io_handle.h"
10
11
#include "gtest/gtest.h"
12
13
using testing::_;
14
using testing::ByMove;
15
using testing::Return;
16
using testing::SaveArg;
17
18
namespace Envoy {
19
namespace Network {
20
namespace {
21
22
// The max size of the listener filter buffer.
23
constexpr uint32_t max_buffer_size = 16 * 1024;
24
// The max size of available data on the socket. It can be large than
25
// buffer size, but we won't peek those extra data.
26
constexpr uint32_t max_readable_size = max_buffer_size + 1024;
27
28
class ListenerFilterBufferFuzzer {
29
public:
30
121
  void fuzz(const test::common::network::ListenerFilterBufferFuzzTestCase& input) {
31
    // Ensure the buffer is not exceed the limit we set.
32
121
    auto max_bytes_read = input.max_bytes_read() % max_buffer_size;
33
    // There won't be any case the max size of buffer is 0.
34
121
    if (max_bytes_read == 0) {
35
8
      return;
36
8
    }
37
38
113
    EXPECT_CALL(io_handle_,
39
113
                createFileEvent_(_, _, Event::PlatformDefaultTriggerType,
40
113
                                 Event::FileReadyType::Read | Event::FileReadyType::Closed))
41
113
        .WillOnce(SaveArg<1>(&file_event_callback_));
42
43
    // Use the on_data callback to verify the data.
44
832
    auto on_data_cb = [&](ListenerFilterBuffer& buffer) {
45
832
      auto raw_slice = buffer.rawSlice();
46
832
      std::string data(reinterpret_cast<const char*>(raw_slice.mem_), raw_slice.len_);
47
      // The available data may be more than the buffer size, also, the buffer size
48
      // can be reduced by drain.
49
832
      FUZZ_ASSERT(data == available_data_.substr(0, max_bytes_read - drained_size_));
50
832
    };
51
113
    auto listener_buffer = std::make_unique<ListenerFilterBufferImpl>(
52
113
        io_handle_, dispatcher_, [&](bool) {}, on_data_cb, max_bytes_read);
53
54
1.32k
    for (auto i = 0; i < input.actions().size(); i++) {
55
1.21k
      const char insert_value = 'a' + i % 26;
56
57
1.21k
      switch (input.actions(i).action_selector_case()) {
58
603
      case test::common::network::Action::kReadable: {
59
        // Generate the available data, and ensure it is under the max_readable_size.
60
603
        auto append_data_size =
61
603
            input.actions(i).readable() % (max_readable_size - available_data_.size());
62
        // If the available is 0, then emulate an `EAGAIN`.
63
603
        if (append_data_size == 0) {
64
378
          EXPECT_CALL(io_handle_, recv)
65
378
              .WillOnce(Return(
66
378
                  ByMove(Api::IoCallUint64Result(0, IoSocketError::getIoSocketEagainError()))));
67
378
        } else {
68
225
          available_data_.insert(available_data_.end(), append_data_size, insert_value);
69
225
          EXPECT_CALL(io_handle_, recv).WillOnce([&](void* buffer, size_t length, int flags) {
70
225
            EXPECT_EQ(MSG_PEEK, flags);
71
225
            auto copy_size = std::min(length, available_data_.size());
72
225
            ::memcpy(buffer, available_data_.data(), copy_size);
73
225
            return Api::IoCallUint64Result(copy_size, Api::IoError::none());
74
225
          });
75
225
          drained_size_ = 0;
76
225
        }
77
        // Trigger the peek by event.
78
603
        file_event_callback_(Event::FileReadyType::Read);
79
603
        break;
80
0
      }
81
607
      case test::common::network::Action::kDrain: {
82
        // The drain method only support drain size less than the buffer size.
83
607
        auto drain_size = std::min(input.actions(i).drain(), listener_buffer->rawSlice().len_);
84
607
        if (drain_size != 0) {
85
227
          EXPECT_CALL(io_handle_, recv).WillOnce([&](void* buffer, size_t length, int flags) {
86
227
            EXPECT_EQ(0, flags);
87
227
            EXPECT_EQ(drain_size, length);
88
227
            ::memcpy(buffer, available_data_.data(), drain_size);
89
227
            available_data_ = available_data_.substr(drain_size);
90
227
            return Api::IoCallUint64Result(drain_size, Api::IoError::none());
91
227
          });
92
227
        }
93
607
        drained_size_ += drain_size;
94
607
        listener_buffer->drain(drain_size);
95
        // Reuse the on_data callback to validate the buffer data.
96
607
        on_data_cb(*listener_buffer);
97
607
        break;
98
0
      }
99
0
      case test::common::network::Action::kResetCapacity: {
100
0
        auto capacity_size = input.actions(i).drain() % max_buffer_size;
101
0
        if (capacity_size == 0) {
102
0
          break;
103
0
        }
104
0
        listener_buffer->resetCapacity(capacity_size);
105
0
        EXPECT_EQ(capacity_size, listener_buffer->capacity());
106
0
        max_bytes_read = capacity_size;
107
0
        drained_size_ = 0;
108
0
        available_data_.clear();
109
0
        EXPECT_EQ(listener_buffer->rawSlice().len_, 0);
110
0
        break;
111
0
      }
112
6
      default:
113
6
        break;
114
1.21k
      }
115
1.21k
    }
116
113
  }
117
118
private:
119
  Network::MockIoHandle io_handle_;
120
  Event::MockDispatcher dispatcher_;
121
  Event::FileReadyCb file_event_callback_;
122
  std::string available_data_;
123
  // The size drained by the test. This is used to calculate the current buffer size.
124
  uint64_t drained_size_{0};
125
};
126
127
121
DEFINE_PROTO_FUZZER(const test::common::network::ListenerFilterBufferFuzzTestCase& input) {
128
121
  auto fuzzer = ListenerFilterBufferFuzzer();
129
121
  fuzzer.fuzz(input);
130
121
}
131
132
} // namespace
133
} // namespace Network
134
} // namespace Envoy