/src/bufferevent_fuzzer.cc
Line | Count | Source |
1 | | /* Copyright 2026 Google LLC |
2 | | Licensed under the Apache License, Version 2.0 (the "License"); |
3 | | you may not use this file except in compliance with the License. |
4 | | You may obtain a copy of the License at |
5 | | http://www.apache.org/licenses/LICENSE-2.0 |
6 | | Unless required by applicable law or agreed to in writing, software |
7 | | distributed under the License is distributed on an "AS IS" BASIS, |
8 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
9 | | See the License for the specific language governing permissions and |
10 | | limitations under the License. |
11 | | */ |
12 | | |
13 | | #include <assert.h> |
14 | | #include <stddef.h> |
15 | | #include <stdint.h> |
16 | | #include <stdlib.h> |
17 | | #include <string> |
18 | | #include <sys/socket.h> |
19 | | |
20 | | #include <fuzzer/FuzzedDataProvider.h> |
21 | | |
22 | | extern "C" { |
23 | | #include "libevent/include/event2/buffer.h" |
24 | | #include "libevent/include/event2/bufferevent.h" |
25 | | #include "libevent/include/event2/event.h" |
26 | | } |
27 | | |
28 | 0 | static void read_cb(struct bufferevent *bev, void *ctx) { |
29 | 0 | struct evbuffer *input = bufferevent_get_input(bev); |
30 | 0 | evbuffer_drain(input, evbuffer_get_length(input)); |
31 | 0 | } |
32 | | |
33 | 0 | static void event_cb(struct bufferevent *bev, short what, void *ctx) { |
34 | 0 | } |
35 | | |
36 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
37 | | if (size < 16) return 0; |
38 | | |
39 | | FuzzedDataProvider data_provider(data, size); |
40 | | |
41 | | std::string s1 = data_provider.ConsumeRandomLengthString(); |
42 | | std::string s2 = data_provider.ConsumeRandomLengthString(); |
43 | | uint32_t val1 = data_provider.ConsumeIntegral<uint32_t>(); |
44 | | uint32_t val2 = data_provider.ConsumeIntegral<uint32_t>(); |
45 | | uint32_t val3 = data_provider.ConsumeIntegral<uint32_t>(); |
46 | | uint32_t val4 = data_provider.ConsumeIntegral<uint32_t>(); |
47 | | |
48 | | int use_pair = val1 % 2; |
49 | | int read_write = val2 % 2; |
50 | | int use_filter = val4 % 2; |
51 | | |
52 | | int options1 = val2 % 16; |
53 | | int options2 = val3 % 16; |
54 | | |
55 | | struct bufferevent *bev1 = NULL, *bev2 = NULL, *bev3 = NULL, *bev4 = NULL, |
56 | | *pair[2]; |
57 | | struct event_base *base = NULL; |
58 | | struct evbuffer *evbuf = NULL; |
59 | | struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; |
60 | | struct bufferevent_rate_limit_group *bev_rate_group = NULL; |
61 | | char buf[128]; |
62 | | |
63 | | /*create a buffer event*/ |
64 | | struct event_config *cfg = event_config_new(); |
65 | | if (val1 % 3 == 1) { |
66 | | event_config_avoid_method(cfg, "epoll"); |
67 | | } else if (val1 % 3 == 2) { |
68 | | event_config_avoid_method(cfg, "epoll"); |
69 | | event_config_avoid_method(cfg, "poll"); |
70 | | } |
71 | | base = event_base_new_with_config(cfg); |
72 | | event_config_free(cfg); |
73 | | if (!base) return 0; |
74 | | |
75 | | if (use_pair == 0) { |
76 | | if (bufferevent_pair_new(base, options1, pair) == -1) { |
77 | | event_base_free(base); |
78 | | return 0; |
79 | | } |
80 | | bev1 = pair[0]; |
81 | | bev2 = pair[1]; |
82 | | } else { |
83 | | bev1 = bufferevent_socket_new(base, -1, options1); |
84 | | bev2 = bufferevent_socket_new(base, -1, options2); |
85 | | } |
86 | | |
87 | | /*bufferevent_filter_new*/ |
88 | | if (use_filter == 0 && bev1 && bev2) { |
89 | | /*we cannot use BEV_OPT_CLOSE_ON_FREE when freeing bufferevents*/ |
90 | | bev3 = bufferevent_filter_new( |
91 | | bev1, NULL, NULL, options1 & (~BEV_OPT_CLOSE_ON_FREE), NULL, NULL); |
92 | | bev4 = bufferevent_filter_new( |
93 | | bev2, NULL, NULL, options2 & (~BEV_OPT_CLOSE_ON_FREE), NULL, NULL); |
94 | | |
95 | | if (bev3) { |
96 | | // bev1 is now "under" bev3 |
97 | | } else { |
98 | | bufferevent_free(bev1); |
99 | | bev1 = NULL; |
100 | | } |
101 | | if (bev4) { |
102 | | // bev2 is now "under" bev4 |
103 | | } else { |
104 | | bufferevent_free(bev2); |
105 | | bev2 = NULL; |
106 | | } |
107 | | } else { |
108 | | bev3 = bev1; |
109 | | bev4 = bev2; |
110 | | } |
111 | | |
112 | | if (!bev3 || !bev4) { |
113 | | goto cleanup; |
114 | | } |
115 | | |
116 | | if (bufferevent_priority_set(bev3, val2 % 8) == 0) { |
117 | | bufferevent_get_priority(bev3); |
118 | | } |
119 | | |
120 | | /*set rate limits*/ |
121 | | bufferevent_set_rate_limit(bev3, NULL); |
122 | | struct timeval cfg_tick; |
123 | | cfg_tick.tv_sec = val1 % 10; |
124 | | cfg_tick.tv_usec = val2 % 1000000; |
125 | | conn_bucket_cfg = ev_token_bucket_cfg_new(val1 % 1024 + 1, val2 % 2048 + 1, |
126 | | val3 % 1024 + 1, val4 % 2048 + 1, &cfg_tick); |
127 | | if (conn_bucket_cfg) { |
128 | | bev_rate_group = bufferevent_rate_limit_group_new(base, conn_bucket_cfg); |
129 | | if (bev_rate_group) { |
130 | | bufferevent_add_to_rate_limit_group(bev4, bev_rate_group); |
131 | | } |
132 | | } |
133 | | |
134 | | /*write and read from buffer events*/ |
135 | | bufferevent_setcb(bev3, read_cb, NULL, event_cb, NULL); |
136 | | bufferevent_setcb(bev4, read_cb, NULL, event_cb, NULL); |
137 | | bufferevent_enable(bev3, EV_READ | EV_WRITE); |
138 | | bufferevent_enable(bev4, EV_READ | EV_WRITE); |
139 | | |
140 | | bufferevent_write(bev3, s1.c_str(), s1.size()); |
141 | | bufferevent_write(bev4, s2.c_str(), s2.size()); |
142 | | |
143 | | /* Run loop briefly */ |
144 | | event_base_loop(base, EVLOOP_NONBLOCK); |
145 | | |
146 | | bufferevent_write_buffer(bev3, bufferevent_get_input(bev4)); |
147 | | |
148 | | evbuf = evbuffer_new(); |
149 | | if (evbuf) { |
150 | | bufferevent_read_buffer(bev3, evbuf); |
151 | | evbuffer_free(evbuf); |
152 | | } |
153 | | bufferevent_read(bev3, buf, sizeof(buf)); |
154 | | |
155 | | if (bev_rate_group) { |
156 | | bufferevent_remove_from_rate_limit_group(bev4); |
157 | | } |
158 | | |
159 | | /*watermarks*/ |
160 | | if (read_write == 0) { |
161 | | bufferevent_setwatermark(bev4, EV_READ, val1 % 1024, val2 % 2048); |
162 | | } else { |
163 | | bufferevent_setwatermark(bev4, EV_WRITE, val1 % 1024, val2 % 2048); |
164 | | } |
165 | | |
166 | | /*clean up*/ |
167 | | cleanup: |
168 | | if (bev3) bufferevent_free(bev3); |
169 | | if (bev4) bufferevent_free(bev4); |
170 | | /* the filters are created without BEV_OPT_CLOSE_ON_FREE, so freeing them |
171 | | does not free the underlying bufferevents */ |
172 | | if (bev1 && bev3 != bev1) bufferevent_free(bev1); |
173 | | if (bev2 && bev4 != bev2) bufferevent_free(bev2); |
174 | | if (conn_bucket_cfg) ev_token_bucket_cfg_free(conn_bucket_cfg); |
175 | | if (bev_rate_group) bufferevent_rate_limit_group_free(bev_rate_group); |
176 | | if (base) event_base_free(base); |
177 | | |
178 | | return 0; |
179 | | } |