tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 };