test_oom.c (11428B)
1 /* Copyright (c) 2014-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /* Unit tests for OOM handling logic */ 5 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" 20 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" 25 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) 31 { 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)); 38 } 39 40 circ->marked_for_close = line; 41 circ->marked_for_close_file = file; 42 } 43 44 static circuit_t * 45 dummy_or_circuit_new(int n_p_cells, int n_n_cells) 46 { 47 or_circuit_t *circ = or_circuit_new(0, NULL); 48 int i; 49 cell_t cell; 50 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); 55 } 56 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); 62 } 63 64 TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_OR; 65 return TO_CIRCUIT(circ); 66 } 67 68 static void 69 add_bytes_to_buf(buf_t *buf, size_t n_bytes) 70 { 71 char b[3000]; 72 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; 78 } 79 } 80 81 static edge_connection_t * 82 dummy_edge_conn_new(circuit_t *circ, 83 int type, size_t in_bytes, size_t out_bytes) 84 { 85 edge_connection_t *conn; 86 buf_t *inbuf, *outbuf; 87 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)); 92 93 inbuf = TO_CONN(conn)->inbuf; 94 outbuf = TO_CONN(conn)->outbuf; 95 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); 100 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; 110 } 111 112 return conn; 113 } 114 115 /** Run unit tests for buffers.c */ 116 static void 117 test_oom_circbuf(void *arg) 118 { 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; 123 124 (void) arg; 125 126 monotime_enable_test_mocking(); 127 MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); 128 129 /* Far too low for real life. */ 130 options->MaxMemInQueues = 256*packed_cell_mem_cost(); 131 options->CellStatistics = 0; 132 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); 136 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); 141 142 now_ns += 10 * 1000000; 143 monotime_coarse_set_mock_time_nsec(now_ns); 144 c2 = dummy_or_circuit_new(20, 20); 145 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 */ 151 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); 158 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); 163 164 tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 165 packed_cell_mem_cost() * 257); 166 167 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ 168 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); 173 174 tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 175 packed_cell_mem_cost() * (257 - 30)); 176 177 circuit_free(c1); 178 179 monotime_coarse_set_mock_time_nsec(start_ns); /* go back in time */ 180 c1 = dummy_or_circuit_new(90, 0); 181 182 now_ns += 10 * 1000000; 183 monotime_coarse_set_mock_time_nsec(now_ns); 184 185 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ 186 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); 191 192 tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 193 packed_cell_mem_cost() * (257 - 30)); 194 195 done: 196 circuit_free(c1); 197 circuit_free(c2); 198 circuit_free(c3); 199 circuit_free(c4); 200 201 UNMOCK(circuit_mark_for_close_); 202 monotime_disable_test_mocking(); 203 } 204 205 /** Run unit tests for buffers.c */ 206 static void 207 test_oom_streambuf(void *arg) 208 { 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; 216 217 (void) arg; 218 monotime_enable_test_mocking(); 219 220 MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); 221 222 /* Far too low for real life. */ 223 options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34; 224 options->CellStatistics = 0; 225 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); 229 230 monotime_coarse_set_mock_time_nsec(start_ns); 231 232 /* Start all circuits with a bit of data queued in cells */ 233 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); 237 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); 246 247 now_ns = start_ns + 600 * 1000000; 248 monotime_coarse_set_mock_time_nsec(now_ns); 249 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); 274 } 275 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(); 280 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) 286 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); 291 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); 296 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); 301 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); 305 306 /* Now give c4 a very old buffer of modest size */ 307 { 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); 314 } 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); 317 318 tt_int_op(cell_queues_check_size(), OP_EQ, 0); 319 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); 324 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); 328 329 tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ 330 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); 337 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); 341 342 done: 343 circuit_free(c1); 344 circuit_free(c2); 345 circuit_free(c3); 346 circuit_free(c4); 347 circuit_free(c5); 348 349 SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec, 350 connection_free_minimal(TO_CONN(ec))); 351 smartlist_free(edgeconns); 352 353 UNMOCK(circuit_mark_for_close_); 354 monotime_disable_test_mocking(); 355 } 356 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 361 };