1 /* Copyright (c) 2014-2020, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /* Unit tests for OOM handling logic */
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. */
29 circuit_mark_for_close_dummy_(circuit_t
*circ
, int reason
, int line
,
33 if (circ
->marked_for_close
) {
34 TT_FAIL(("Circuit already marked for close at %s:%d, but we are marking "
36 circ
->marked_for_close_file
, (int)circ
->marked_for_close
,
40 circ
->marked_for_close
= line
;
41 circ
->marked_for_close_file
= file
;
45 dummy_or_circuit_new(int n_p_cells
, int n_n_cells
)
47 or_circuit_t
*circ
= or_circuit_new(0, NULL
);
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
,
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
,
64 TO_CIRCUIT(circ
)->purpose
= CIRCUIT_PURPOSE_OR
;
65 return TO_CIRCUIT(circ
);
69 add_bytes_to_buf(buf_t
*buf
, size_t 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
);
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
);
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
;
107 origin_circuit_t
*oc
= TO_ORIGIN_CIRCUIT(circ
);
108 conn
->next_stream
= oc
->p_streams
;
109 oc
->p_streams
= conn
;
115 /** Run unit tests for buffers.c */
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
;
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
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));
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));
201 UNMOCK(circuit_mark_for_close_
);
202 monotime_disable_test_mocking();
205 /** Run unit tests for buffers.c */
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
;
213 smartlist_t
*edgeconns
= smartlist_new();
214 const uint64_t start_ns
= 1389641159 * (uint64_t)1000000000;
215 uint64_t now_ns
= start_ns
;
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);
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);
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*/
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
);
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); \
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);
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);
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
},