Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / test / test_oom.c
blob7cab0933f741f2bd8ec4f633e859a6dbb2f4a48b
1 /* Copyright (c) 2014-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /* Unit tests for OOM handling logic */
6 #define RELAY_PRIVATE
7 #define BUFFERS_PRIVATE
8 #define CIRCUITLIST_PRIVATE
9 #define CONNECTION_PRIVATE
10 #include "core/or/or.h"
11 #include "lib/buf/buffers.h"
12 #include "core/or/circuitlist.h"
13 #include "lib/evloop/compat_libevent.h"
14 #include "core/mainloop/connection.h"
15 #include "app/config/config.h"
16 #include "lib/crypt_ops/crypto_rand.h"
17 #include "core/or/relay.h"
18 #include "test/test.h"
19 #include "test/test_helpers.h"
21 #include "core/or/cell_st.h"
22 #include "core/or/entry_connection_st.h"
23 #include "core/or/or_circuit_st.h"
24 #include "core/or/origin_circuit_st.h"
26 /* small replacement mock for circuit_mark_for_close_ to avoid doing all
27 * the other bookkeeping that comes with marking circuits. */
28 static void
29 circuit_mark_for_close_dummy_(circuit_t *circ, int reason, int line,
30 const char *file)
32 (void) reason;
33 if (circ->marked_for_close) {
34 TT_FAIL(("Circuit already marked for close at %s:%d, but we are marking "
35 "it again at %s:%d",
36 circ->marked_for_close_file, (int)circ->marked_for_close,
37 file, line));
40 circ->marked_for_close = line;
41 circ->marked_for_close_file = file;
44 static circuit_t *
45 dummy_or_circuit_new(int n_p_cells, int n_n_cells)
47 or_circuit_t *circ = or_circuit_new(0, NULL);
48 int i;
49 cell_t cell;
51 for (i=0; i < n_p_cells; ++i) {
52 crypto_rand((void*)&cell, sizeof(cell));
53 cell_queue_append_packed_copy(TO_CIRCUIT(circ), &circ->p_chan_cells,
54 0, &cell, 1, 0);
57 for (i=0; i < n_n_cells; ++i) {
58 crypto_rand((void*)&cell, sizeof(cell));
59 cell_queue_append_packed_copy(TO_CIRCUIT(circ),
60 &TO_CIRCUIT(circ)->n_chan_cells,
61 1, &cell, 1, 0);
64 TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_OR;
65 return TO_CIRCUIT(circ);
68 static void
69 add_bytes_to_buf(buf_t *buf, size_t n_bytes)
71 char b[3000];
73 while (n_bytes) {
74 size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes;
75 crypto_rand(b, this_add);
76 buf_add(buf, b, this_add);
77 n_bytes -= this_add;
81 static edge_connection_t *
82 dummy_edge_conn_new(circuit_t *circ,
83 int type, size_t in_bytes, size_t out_bytes)
85 edge_connection_t *conn;
86 buf_t *inbuf, *outbuf;
88 if (type == CONN_TYPE_EXIT)
89 conn = edge_connection_new(type, AF_INET);
90 else
91 conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET));
93 inbuf = TO_CONN(conn)->inbuf;
94 outbuf = TO_CONN(conn)->outbuf;
96 /* We add these bytes directly to the buffers, to avoid all the
97 * edge connection read/write machinery. */
98 add_bytes_to_buf(inbuf, in_bytes);
99 add_bytes_to_buf(outbuf, out_bytes);
101 conn->on_circuit = circ;
102 if (type == CONN_TYPE_EXIT) {
103 or_circuit_t *oc = TO_OR_CIRCUIT(circ);
104 conn->next_stream = oc->n_streams;
105 oc->n_streams = conn;
106 } else {
107 origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
108 conn->next_stream = oc->p_streams;
109 oc->p_streams = conn;
112 return conn;
115 /** Run unit tests for buffers.c */
116 static void
117 test_oom_circbuf(void *arg)
119 or_options_t *options = get_options_mutable();
120 circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL;
121 uint64_t now_ns = 1389631048 * (uint64_t)1000000000;
122 const uint64_t start_ns = now_ns;
124 (void) arg;
126 monotime_enable_test_mocking();
127 MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
129 /* Far too low for real life. */
130 options->MaxMemInQueues = 256*packed_cell_mem_cost();
131 options->CellStatistics = 0;
133 tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */
134 tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0);
135 tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
137 /* Now we're going to fake up some circuits and get them added to the global
138 circuit list. */
139 monotime_coarse_set_mock_time_nsec(now_ns);
140 c1 = dummy_origin_circuit_new(30);
142 now_ns += 10 * 1000000;
143 monotime_coarse_set_mock_time_nsec(now_ns);
144 c2 = dummy_or_circuit_new(20, 20);
146 tt_int_op(packed_cell_mem_cost(), OP_EQ,
147 sizeof(packed_cell_t));
148 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
149 packed_cell_mem_cost() * 70);
150 tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
152 now_ns += 10 * 1000000;
153 monotime_coarse_set_mock_time_nsec(now_ns);
154 c3 = dummy_or_circuit_new(100, 85);
155 tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
156 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
157 packed_cell_mem_cost() * 255);
159 now_ns += 10 * 1000000;
160 monotime_coarse_set_mock_time_nsec(now_ns);
161 /* Adding this cell will trigger our OOM handler. */
162 c4 = dummy_or_circuit_new(2, 0);
164 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
165 packed_cell_mem_cost() * 257);
167 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
169 tt_assert(c1->marked_for_close);
170 tt_assert(! c2->marked_for_close);
171 tt_assert(! c3->marked_for_close);
172 tt_assert(! c4->marked_for_close);
174 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
175 packed_cell_mem_cost() * (257 - 30));
177 circuit_free(c1);
179 monotime_coarse_set_mock_time_nsec(start_ns); /* go back in time */
180 c1 = dummy_or_circuit_new(90, 0);
182 now_ns += 10 * 1000000;
183 monotime_coarse_set_mock_time_nsec(now_ns);
185 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
187 tt_assert(c1->marked_for_close);
188 tt_assert(! c2->marked_for_close);
189 tt_assert(! c3->marked_for_close);
190 tt_assert(! c4->marked_for_close);
192 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
193 packed_cell_mem_cost() * (257 - 30));
195 done:
196 circuit_free(c1);
197 circuit_free(c2);
198 circuit_free(c3);
199 circuit_free(c4);
201 UNMOCK(circuit_mark_for_close_);
202 monotime_disable_test_mocking();
205 /** Run unit tests for buffers.c */
206 static void
207 test_oom_streambuf(void *arg)
209 or_options_t *options = get_options_mutable();
210 circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL;
211 uint32_t tvts;
212 int i;
213 smartlist_t *edgeconns = smartlist_new();
214 const uint64_t start_ns = 1389641159 * (uint64_t)1000000000;
215 uint64_t now_ns = start_ns;
217 (void) arg;
218 monotime_enable_test_mocking();
220 MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
222 /* Far too low for real life. */
223 options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34;
224 options->CellStatistics = 0;
226 tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */
227 tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0);
228 tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
230 monotime_coarse_set_mock_time_nsec(start_ns);
232 /* Start all circuits with a bit of data queued in cells */
234 /* go halfway into the second. */
235 monotime_coarse_set_mock_time_nsec(start_ns + 500 * 1000000);
236 c1 = dummy_or_circuit_new(10,10);
238 monotime_coarse_set_mock_time_nsec(start_ns + 510 * 1000000);
239 c2 = dummy_origin_circuit_new(20);
240 monotime_coarse_set_mock_time_nsec(start_ns + 520 * 1000000);
241 c3 = dummy_or_circuit_new(20,20);
242 monotime_coarse_set_mock_time_nsec(start_ns + 530 * 1000000);
243 c4 = dummy_or_circuit_new(0,0);
244 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
245 packed_cell_mem_cost() * 80);
247 now_ns = start_ns + 600 * 1000000;
248 monotime_coarse_set_mock_time_nsec(now_ns);
250 /* Add some connections to c1...c4. */
251 for (i = 0; i < 4; ++i) {
252 edge_connection_t *ec;
253 /* link it to a circuit */
254 now_ns += 10 * 1000000;
255 monotime_coarse_set_mock_time_nsec(now_ns);
256 ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000);
257 tt_assert(ec);
258 smartlist_add(edgeconns, ec);
259 now_ns += 10 * 1000000;
260 monotime_coarse_set_mock_time_nsec(now_ns);
261 ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000);
262 tt_assert(ec);
263 smartlist_add(edgeconns, ec);
264 now_ns += 10 * 1000000;
265 monotime_coarse_set_mock_time_nsec(now_ns);
266 ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/
267 tt_assert(ec);
268 smartlist_add(edgeconns, ec);
269 now_ns += 10 * 1000000;
270 monotime_coarse_set_mock_time_nsec(now_ns);
271 ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
272 smartlist_add(edgeconns, ec);
273 tt_assert(ec);
276 now_ns -= now_ns % 1000000000;
277 now_ns += 1000000000;
278 monotime_coarse_set_mock_time_nsec(now_ns);
279 tvts = monotime_coarse_get_stamp();
281 #define ts_is_approx(ts, val) do { \
282 uint32_t x_ = (uint32_t) monotime_coarse_stamp_units_to_approx_msec(ts); \
283 tt_int_op(x_, OP_GE, val - 5); \
284 tt_int_op(x_, OP_LE, val + 5); \
285 } while (0)
287 ts_is_approx(circuit_max_queued_cell_age(c1, tvts), 500);
288 ts_is_approx(circuit_max_queued_cell_age(c2, tvts), 490);
289 ts_is_approx(circuit_max_queued_cell_age(c3, tvts), 480);
290 ts_is_approx(circuit_max_queued_cell_age(c4, tvts), 0);
292 ts_is_approx(circuit_max_queued_data_age(c1, tvts), 390);
293 ts_is_approx(circuit_max_queued_data_age(c2, tvts), 380);
294 ts_is_approx(circuit_max_queued_data_age(c3, tvts), 0);
295 ts_is_approx(circuit_max_queued_data_age(c4, tvts), 370);
297 ts_is_approx(circuit_max_queued_item_age(c1, tvts), 500);
298 ts_is_approx(circuit_max_queued_item_age(c2, tvts), 490);
299 ts_is_approx(circuit_max_queued_item_age(c3, tvts), 480);
300 ts_is_approx(circuit_max_queued_item_age(c4, tvts), 370);
302 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
303 packed_cell_mem_cost() * 80);
304 tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*16*2);
306 /* Now give c4 a very old buffer of modest size */
308 edge_connection_t *ec;
309 now_ns -= 1000000000;
310 monotime_coarse_set_mock_time_nsec(now_ns);
311 ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
312 tt_assert(ec);
313 smartlist_add(edgeconns, ec);
315 tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2);
316 ts_is_approx(circuit_max_queued_item_age(c4, tvts), 1000);
318 tt_int_op(cell_queues_check_size(), OP_EQ, 0);
320 /* And run over the limit. */
321 now_ns += 800*1000000;
322 monotime_coarse_set_mock_time_nsec(now_ns);
323 c5 = dummy_or_circuit_new(0,5);
325 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
326 packed_cell_mem_cost() * 85);
327 tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2);
329 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
331 /* C4 should have died. */
332 tt_assert(! c1->marked_for_close);
333 tt_assert(! c2->marked_for_close);
334 tt_assert(! c3->marked_for_close);
335 tt_assert(c4->marked_for_close);
336 tt_assert(! c5->marked_for_close);
338 tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
339 packed_cell_mem_cost() * 85);
340 tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*8*2);
342 done:
343 circuit_free(c1);
344 circuit_free(c2);
345 circuit_free(c3);
346 circuit_free(c4);
347 circuit_free(c5);
349 SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec,
350 connection_free_minimal(TO_CONN(ec)));
351 smartlist_free(edgeconns);
353 UNMOCK(circuit_mark_for_close_);
354 monotime_disable_test_mocking();
357 struct testcase_t oom_tests[] = {
358 { "circbuf", test_oom_circbuf, TT_FORK, NULL, NULL },
359 { "streambuf", test_oom_streambuf, TT_FORK, NULL, NULL },
360 END_OF_TESTCASES