1 /* Copyright (c) 2014-2017, 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
12 #include "circuitlist.h"
13 #include "compat_libevent.h"
14 #include "connection.h"
18 #include "test_helpers.h"
20 /* small replacement mock for circuit_mark_for_close_ to avoid doing all
21 * the other bookkeeping that comes with marking circuits. */
23 circuit_mark_for_close_dummy_(circuit_t
*circ
, int reason
, int line
,
27 if (circ
->marked_for_close
) {
28 TT_FAIL(("Circuit already marked for close at %s:%d, but we are marking "
30 circ
->marked_for_close_file
, (int)circ
->marked_for_close
,
34 circ
->marked_for_close
= line
;
35 circ
->marked_for_close_file
= file
;
39 dummy_or_circuit_new(int n_p_cells
, int n_n_cells
)
41 or_circuit_t
*circ
= or_circuit_new(0, NULL
);
45 for (i
=0; i
< n_p_cells
; ++i
) {
46 crypto_rand((void*)&cell
, sizeof(cell
));
47 cell_queue_append_packed_copy(TO_CIRCUIT(circ
), &circ
->p_chan_cells
,
51 for (i
=0; i
< n_n_cells
; ++i
) {
52 crypto_rand((void*)&cell
, sizeof(cell
));
53 cell_queue_append_packed_copy(TO_CIRCUIT(circ
),
54 &TO_CIRCUIT(circ
)->n_chan_cells
,
58 TO_CIRCUIT(circ
)->purpose
= CIRCUIT_PURPOSE_OR
;
59 return TO_CIRCUIT(circ
);
63 add_bytes_to_buf(buf_t
*buf
, size_t n_bytes
)
68 size_t this_add
= n_bytes
> sizeof(b
) ? sizeof(b
) : n_bytes
;
69 crypto_rand(b
, this_add
);
70 buf_add(buf
, b
, this_add
);
75 static edge_connection_t
*
76 dummy_edge_conn_new(circuit_t
*circ
,
77 int type
, size_t in_bytes
, size_t out_bytes
)
79 edge_connection_t
*conn
;
80 buf_t
*inbuf
, *outbuf
;
82 if (type
== CONN_TYPE_EXIT
)
83 conn
= edge_connection_new(type
, AF_INET
);
85 conn
= ENTRY_TO_EDGE_CONN(entry_connection_new(type
, AF_INET
));
87 inbuf
= TO_CONN(conn
)->inbuf
;
88 outbuf
= TO_CONN(conn
)->outbuf
;
90 /* We add these bytes directly to the buffers, to avoid all the
91 * edge connection read/write machinery. */
92 add_bytes_to_buf(inbuf
, in_bytes
);
93 add_bytes_to_buf(outbuf
, out_bytes
);
95 conn
->on_circuit
= circ
;
96 if (type
== CONN_TYPE_EXIT
) {
97 or_circuit_t
*oc
= TO_OR_CIRCUIT(circ
);
98 conn
->next_stream
= oc
->n_streams
;
101 origin_circuit_t
*oc
= TO_ORIGIN_CIRCUIT(circ
);
102 conn
->next_stream
= oc
->p_streams
;
103 oc
->p_streams
= conn
;
109 /** Run unit tests for buffers.c */
111 test_oom_circbuf(void *arg
)
113 or_options_t
*options
= get_options_mutable();
114 circuit_t
*c1
= NULL
, *c2
= NULL
, *c3
= NULL
, *c4
= NULL
;
115 uint64_t now_ns
= 1389631048 * (uint64_t)1000000000;
116 const uint64_t start_ns
= now_ns
;
120 monotime_enable_test_mocking();
121 MOCK(circuit_mark_for_close_
, circuit_mark_for_close_dummy_
);
123 /* Far too low for real life. */
124 options
->MaxMemInQueues
= 256*packed_cell_mem_cost();
125 options
->CellStatistics
= 0;
127 tt_int_op(cell_queues_check_size(), OP_EQ
, 0); /* We don't start out OOM. */
128 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
, 0);
129 tt_int_op(buf_get_total_allocation(), OP_EQ
, 0);
131 /* Now we're going to fake up some circuits and get them added to the global
133 monotime_coarse_set_mock_time_nsec(now_ns
);
134 c1
= dummy_origin_circuit_new(30);
136 now_ns
+= 10 * 1000000;
137 monotime_coarse_set_mock_time_nsec(now_ns
);
138 c2
= dummy_or_circuit_new(20, 20);
140 tt_int_op(packed_cell_mem_cost(), OP_EQ
,
141 sizeof(packed_cell_t
));
142 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
143 packed_cell_mem_cost() * 70);
144 tt_int_op(cell_queues_check_size(), OP_EQ
, 0); /* We are still not OOM */
146 now_ns
+= 10 * 1000000;
147 monotime_coarse_set_mock_time_nsec(now_ns
);
148 c3
= dummy_or_circuit_new(100, 85);
149 tt_int_op(cell_queues_check_size(), OP_EQ
, 0); /* We are still not OOM */
150 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
151 packed_cell_mem_cost() * 255);
153 now_ns
+= 10 * 1000000;
154 monotime_coarse_set_mock_time_nsec(now_ns
);
155 /* Adding this cell will trigger our OOM handler. */
156 c4
= dummy_or_circuit_new(2, 0);
158 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
159 packed_cell_mem_cost() * 257);
161 tt_int_op(cell_queues_check_size(), OP_EQ
, 1); /* We are now OOM */
163 tt_assert(c1
->marked_for_close
);
164 tt_assert(! c2
->marked_for_close
);
165 tt_assert(! c3
->marked_for_close
);
166 tt_assert(! c4
->marked_for_close
);
168 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
169 packed_cell_mem_cost() * (257 - 30));
173 monotime_coarse_set_mock_time_nsec(start_ns
); /* go back in time */
174 c1
= dummy_or_circuit_new(90, 0);
176 now_ns
+= 10 * 1000000;
177 monotime_coarse_set_mock_time_nsec(now_ns
);
179 tt_int_op(cell_queues_check_size(), OP_EQ
, 1); /* We are now OOM */
181 tt_assert(c1
->marked_for_close
);
182 tt_assert(! c2
->marked_for_close
);
183 tt_assert(! c3
->marked_for_close
);
184 tt_assert(! c4
->marked_for_close
);
186 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
187 packed_cell_mem_cost() * (257 - 30));
195 UNMOCK(circuit_mark_for_close_
);
196 monotime_disable_test_mocking();
199 /** Run unit tests for buffers.c */
201 test_oom_streambuf(void *arg
)
203 or_options_t
*options
= get_options_mutable();
204 circuit_t
*c1
= NULL
, *c2
= NULL
, *c3
= NULL
, *c4
= NULL
, *c5
= NULL
;
207 smartlist_t
*edgeconns
= smartlist_new();
208 const uint64_t start_ns
= 1389641159 * (uint64_t)1000000000;
209 uint64_t now_ns
= start_ns
;
212 monotime_enable_test_mocking();
214 MOCK(circuit_mark_for_close_
, circuit_mark_for_close_dummy_
);
216 /* Far too low for real life. */
217 options
->MaxMemInQueues
= 81*packed_cell_mem_cost() + 4096 * 34;
218 options
->CellStatistics
= 0;
220 tt_int_op(cell_queues_check_size(), OP_EQ
, 0); /* We don't start out OOM. */
221 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
, 0);
222 tt_int_op(buf_get_total_allocation(), OP_EQ
, 0);
224 monotime_coarse_set_mock_time_nsec(start_ns
);
226 /* Start all circuits with a bit of data queued in cells */
228 /* go halfway into the second. */
229 monotime_coarse_set_mock_time_nsec(start_ns
+ 500 * 1000000);
230 c1
= dummy_or_circuit_new(10,10);
232 monotime_coarse_set_mock_time_nsec(start_ns
+ 510 * 1000000);
233 c2
= dummy_origin_circuit_new(20);
234 monotime_coarse_set_mock_time_nsec(start_ns
+ 520 * 1000000);
235 c3
= dummy_or_circuit_new(20,20);
236 monotime_coarse_set_mock_time_nsec(start_ns
+ 530 * 1000000);
237 c4
= dummy_or_circuit_new(0,0);
238 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
239 packed_cell_mem_cost() * 80);
241 now_ns
= start_ns
+ 600 * 1000000;
242 monotime_coarse_set_mock_time_nsec(now_ns
);
244 /* Add some connections to c1...c4. */
245 for (i
= 0; i
< 4; ++i
) {
246 edge_connection_t
*ec
;
247 /* link it to a circuit */
248 now_ns
+= 10 * 1000000;
249 monotime_coarse_set_mock_time_nsec(now_ns
);
250 ec
= dummy_edge_conn_new(c1
, CONN_TYPE_EXIT
, 1000, 1000);
252 smartlist_add(edgeconns
, ec
);
253 now_ns
+= 10 * 1000000;
254 monotime_coarse_set_mock_time_nsec(now_ns
);
255 ec
= dummy_edge_conn_new(c2
, CONN_TYPE_AP
, 1000, 1000);
257 smartlist_add(edgeconns
, ec
);
258 now_ns
+= 10 * 1000000;
259 monotime_coarse_set_mock_time_nsec(now_ns
);
260 ec
= dummy_edge_conn_new(c4
, CONN_TYPE_EXIT
, 1000, 1000); /* Yes, 4 twice*/
262 smartlist_add(edgeconns
, ec
);
263 now_ns
+= 10 * 1000000;
264 monotime_coarse_set_mock_time_nsec(now_ns
);
265 ec
= dummy_edge_conn_new(c4
, CONN_TYPE_EXIT
, 1000, 1000);
266 smartlist_add(edgeconns
, ec
);
270 now_ns
-= now_ns
% 1000000000;
271 now_ns
+= 1000000000;
272 monotime_coarse_set_mock_time_nsec(now_ns
);
273 tvms
= (uint32_t) monotime_coarse_absolute_msec();
275 tt_int_op(circuit_max_queued_cell_age(c1
, tvms
), OP_EQ
, 500);
276 tt_int_op(circuit_max_queued_cell_age(c2
, tvms
), OP_EQ
, 490);
277 tt_int_op(circuit_max_queued_cell_age(c3
, tvms
), OP_EQ
, 480);
278 tt_int_op(circuit_max_queued_cell_age(c4
, tvms
), OP_EQ
, 0);
280 tt_int_op(circuit_max_queued_data_age(c1
, tvms
), OP_EQ
, 390);
281 tt_int_op(circuit_max_queued_data_age(c2
, tvms
), OP_EQ
, 380);
282 tt_int_op(circuit_max_queued_data_age(c3
, tvms
), OP_EQ
, 0);
283 tt_int_op(circuit_max_queued_data_age(c4
, tvms
), OP_EQ
, 370);
285 tt_int_op(circuit_max_queued_item_age(c1
, tvms
), OP_EQ
, 500);
286 tt_int_op(circuit_max_queued_item_age(c2
, tvms
), OP_EQ
, 490);
287 tt_int_op(circuit_max_queued_item_age(c3
, tvms
), OP_EQ
, 480);
288 tt_int_op(circuit_max_queued_item_age(c4
, tvms
), OP_EQ
, 370);
290 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
291 packed_cell_mem_cost() * 80);
292 tt_int_op(buf_get_total_allocation(), OP_EQ
, 4096*16*2);
294 /* Now give c4 a very old buffer of modest size */
296 edge_connection_t
*ec
;
297 now_ns
-= 1000000000;
298 monotime_coarse_set_mock_time_nsec(now_ns
);
299 ec
= dummy_edge_conn_new(c4
, CONN_TYPE_EXIT
, 1000, 1000);
301 smartlist_add(edgeconns
, ec
);
303 tt_int_op(buf_get_total_allocation(), OP_EQ
, 4096*17*2);
304 tt_int_op(circuit_max_queued_item_age(c4
, tvms
), OP_EQ
, 1000);
306 tt_int_op(cell_queues_check_size(), OP_EQ
, 0);
308 /* And run over the limit. */
309 now_ns
+= 800*1000000;
310 monotime_coarse_set_mock_time_nsec(now_ns
);
311 c5
= dummy_or_circuit_new(0,5);
313 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
314 packed_cell_mem_cost() * 85);
315 tt_int_op(buf_get_total_allocation(), OP_EQ
, 4096*17*2);
317 tt_int_op(cell_queues_check_size(), OP_EQ
, 1); /* We are now OOM */
319 /* C4 should have died. */
320 tt_assert(! c1
->marked_for_close
);
321 tt_assert(! c2
->marked_for_close
);
322 tt_assert(! c3
->marked_for_close
);
323 tt_assert(c4
->marked_for_close
);
324 tt_assert(! c5
->marked_for_close
);
326 tt_int_op(cell_queues_get_total_allocation(), OP_EQ
,
327 packed_cell_mem_cost() * 85);
328 tt_int_op(buf_get_total_allocation(), OP_EQ
, 4096*8*2);
337 SMARTLIST_FOREACH(edgeconns
, edge_connection_t
*, ec
,
338 connection_free_(TO_CONN(ec
)));
339 smartlist_free(edgeconns
);
341 UNMOCK(circuit_mark_for_close_
);
342 monotime_disable_test_mocking();
345 struct testcase_t oom_tests
[] = {
346 { "circbuf", test_oom_circbuf
, TT_FORK
, NULL
, NULL
},
347 { "streambuf", test_oom_streambuf
, TT_FORK
, NULL
, NULL
},