/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 |