/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 | 497 | static void read_cb(struct bufferevent *bev, void *ctx) { |
29 | 497 | struct evbuffer *input = bufferevent_get_input(bev); |
30 | 497 | evbuffer_drain(input, evbuffer_get_length(input)); |
31 | 497 | } |
32 | | |
33 | 0 | static void event_cb(struct bufferevent *bev, short what, void *ctx) { |
34 | 0 | } |
35 | | |
36 | 610 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
37 | 610 | if (size < 16) return 0; |
38 | | |
39 | 604 | FuzzedDataProvider data_provider(data, size); |
40 | | |
41 | 604 | std::string s1 = data_provider.ConsumeRandomLengthString(); |
42 | 604 | std::string s2 = data_provider.ConsumeRandomLengthString(); |
43 | 604 | uint32_t val1 = data_provider.ConsumeIntegral<uint32_t>(); |
44 | 604 | uint32_t val2 = data_provider.ConsumeIntegral<uint32_t>(); |
45 | 604 | uint32_t val3 = data_provider.ConsumeIntegral<uint32_t>(); |
46 | 604 | uint32_t val4 = data_provider.ConsumeIntegral<uint32_t>(); |
47 | | |
48 | 604 | int use_pair = val1 % 2; |
49 | 604 | int read_write = val2 % 2; |
50 | 604 | int use_filter = val4 % 2; |
51 | | |
52 | 604 | int options1 = val2 % 16; |
53 | 604 | int options2 = val3 % 16; |
54 | | |
55 | 604 | struct bufferevent *bev1 = NULL, *bev2 = NULL, *bev3 = NULL, *bev4 = NULL, |
56 | 604 | *pair[2]; |
57 | 604 | struct event_base *base = NULL; |
58 | 604 | struct evbuffer *evbuf = NULL; |
59 | 604 | struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; |
60 | 604 | struct bufferevent_rate_limit_group *bev_rate_group = NULL; |
61 | 604 | char buf[128]; |
62 | | |
63 | | /*create a buffer event*/ |
64 | 604 | struct event_config *cfg = event_config_new(); |
65 | 604 | if (val1 % 3 == 1) { |
66 | 104 | event_config_avoid_method(cfg, "epoll"); |
67 | 500 | } else if (val1 % 3 == 2) { |
68 | 109 | event_config_avoid_method(cfg, "epoll"); |
69 | 109 | event_config_avoid_method(cfg, "poll"); |
70 | 109 | } |
71 | 604 | base = event_base_new_with_config(cfg); |
72 | 604 | event_config_free(cfg); |
73 | 604 | if (!base) return 0; |
74 | | |
75 | 604 | if (use_pair == 0) { |
76 | 509 | if (bufferevent_pair_new(base, options1, pair) == -1) { |
77 | 13 | event_base_free(base); |
78 | 13 | return 0; |
79 | 13 | } |
80 | 496 | bev1 = pair[0]; |
81 | 496 | bev2 = pair[1]; |
82 | 496 | } else { |
83 | 95 | bev1 = bufferevent_socket_new(base, -1, options1); |
84 | 95 | bev2 = bufferevent_socket_new(base, -1, options2); |
85 | 95 | } |
86 | | |
87 | | /*bufferevent_filter_new*/ |
88 | 591 | if (use_filter == 0 && bev1 && bev2) { |
89 | | /*we cannot use BEV_OPT_CLOSE_ON_FREE when freeing bufferevents*/ |
90 | 441 | bev3 = bufferevent_filter_new( |
91 | 441 | bev1, NULL, NULL, options1 & (~BEV_OPT_CLOSE_ON_FREE), NULL, NULL); |
92 | 441 | bev4 = bufferevent_filter_new( |
93 | 441 | bev2, NULL, NULL, options2 & (~BEV_OPT_CLOSE_ON_FREE), NULL, NULL); |
94 | | |
95 | 441 | if (bev3) { |
96 | | // bev1 is now "under" bev3 |
97 | 434 | } else { |
98 | 7 | bufferevent_free(bev1); |
99 | 7 | bev1 = NULL; |
100 | 7 | } |
101 | 441 | if (bev4) { |
102 | | // bev2 is now "under" bev4 |
103 | 434 | } else { |
104 | 7 | bufferevent_free(bev2); |
105 | 7 | bev2 = NULL; |
106 | 7 | } |
107 | 441 | } else { |
108 | 150 | bev3 = bev1; |
109 | 150 | bev4 = bev2; |
110 | 150 | } |
111 | | |
112 | 591 | if (!bev3 || !bev4) { |
113 | 31 | goto cleanup; |
114 | 31 | } |
115 | | |
116 | 560 | if (bufferevent_priority_set(bev3, val2 % 8) == 0) { |
117 | 7 | bufferevent_get_priority(bev3); |
118 | 7 | } |
119 | | |
120 | | /*set rate limits*/ |
121 | 560 | bufferevent_set_rate_limit(bev3, NULL); |
122 | 560 | struct timeval cfg_tick; |
123 | 560 | cfg_tick.tv_sec = val1 % 10; |
124 | 560 | cfg_tick.tv_usec = val2 % 1000000; |
125 | 560 | conn_bucket_cfg = ev_token_bucket_cfg_new(val1 % 1024 + 1, val2 % 2048 + 1, |
126 | 560 | val3 % 1024 + 1, val4 % 2048 + 1, &cfg_tick); |
127 | 560 | if (conn_bucket_cfg) { |
128 | 202 | bev_rate_group = bufferevent_rate_limit_group_new(base, conn_bucket_cfg); |
129 | 202 | if (bev_rate_group) { |
130 | 202 | bufferevent_add_to_rate_limit_group(bev4, bev_rate_group); |
131 | 202 | } |
132 | 202 | } |
133 | | |
134 | | /*write and read from buffer events*/ |
135 | 560 | bufferevent_setcb(bev3, read_cb, NULL, event_cb, NULL); |
136 | 560 | bufferevent_setcb(bev4, read_cb, NULL, event_cb, NULL); |
137 | 560 | bufferevent_enable(bev3, EV_READ | EV_WRITE); |
138 | 560 | bufferevent_enable(bev4, EV_READ | EV_WRITE); |
139 | | |
140 | 560 | bufferevent_write(bev3, s1.c_str(), s1.size()); |
141 | 560 | bufferevent_write(bev4, s2.c_str(), s2.size()); |
142 | | |
143 | | /* Run loop briefly */ |
144 | 560 | event_base_loop(base, EVLOOP_NONBLOCK); |
145 | | |
146 | 560 | bufferevent_write_buffer(bev3, bufferevent_get_input(bev4)); |
147 | | |
148 | 560 | evbuf = evbuffer_new(); |
149 | 560 | if (evbuf) { |
150 | 560 | bufferevent_read_buffer(bev3, evbuf); |
151 | 560 | evbuffer_free(evbuf); |
152 | 560 | } |
153 | 560 | bufferevent_read(bev3, buf, sizeof(buf)); |
154 | | |
155 | 560 | if (bev_rate_group) { |
156 | 202 | bufferevent_remove_from_rate_limit_group(bev4); |
157 | 202 | } |
158 | | |
159 | | /*watermarks*/ |
160 | 560 | if (read_write == 0) { |
161 | 498 | bufferevent_setwatermark(bev4, EV_READ, val1 % 1024, val2 % 2048); |
162 | 498 | } else { |
163 | 62 | bufferevent_setwatermark(bev4, EV_WRITE, val1 % 1024, val2 % 2048); |
164 | 62 | } |
165 | | |
166 | | /*clean up*/ |
167 | 591 | cleanup: |
168 | 591 | if (bev3) bufferevent_free(bev3); |
169 | 591 | if (bev4) bufferevent_free(bev4); |
170 | 591 | if (conn_bucket_cfg) ev_token_bucket_cfg_free(conn_bucket_cfg); |
171 | 591 | if (bev_rate_group) bufferevent_rate_limit_group_free(bev_rate_group); |
172 | 591 | if (base) event_base_free(base); |
173 | | |
174 | 591 | return 0; |
175 | 560 | } |