LCOV - code coverage report
Current view: top level - test - test_oom.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 201 202 99.5 %
Date: 2021-11-24 03:28:48 Functions: 6 6 100.0 %

          Line data    Source code
       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           3 : circuit_mark_for_close_dummy_(circuit_t *circ, int reason, int line,
      30             :                               const char *file)
      31             : {
      32           3 :   (void) reason;
      33           3 :   if (circ->marked_for_close) {
      34           0 :     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           3 :   circ->marked_for_close = line;
      41           3 :   circ->marked_for_close_file = file;
      42           3 : }
      43             : 
      44             : static circuit_t *
      45           8 : dummy_or_circuit_new(int n_p_cells, int n_n_cells)
      46             : {
      47           8 :   or_circuit_t *circ = or_circuit_new(0, NULL);
      48           8 :   int i;
      49           8 :   cell_t cell;
      50             : 
      51         258 :   for (i=0; i < n_p_cells; ++i) {
      52         242 :     crypto_rand((void*)&cell, sizeof(cell));
      53         242 :     cell_queue_append_packed_copy(TO_CIRCUIT(circ), &circ->p_chan_cells,
      54             :                                   0, &cell, 1, 0);
      55             :   }
      56             : 
      57         148 :   for (i=0; i < n_n_cells; ++i) {
      58         140 :     crypto_rand((void*)&cell, sizeof(cell));
      59         140 :     cell_queue_append_packed_copy(TO_CIRCUIT(circ),
      60             :                                   &TO_CIRCUIT(circ)->n_chan_cells,
      61             :                                   1, &cell, 1, 0);
      62             :   }
      63             : 
      64           8 :   TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_OR;
      65           8 :   return TO_CIRCUIT(circ);
      66             : }
      67             : 
      68             : static void
      69          34 : add_bytes_to_buf(buf_t *buf, size_t n_bytes)
      70             : {
      71          34 :   char b[3000];
      72             : 
      73          68 :   while (n_bytes) {
      74          34 :     size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes;
      75          34 :     crypto_rand(b, this_add);
      76          34 :     buf_add(buf, b, this_add);
      77          34 :     n_bytes -= this_add;
      78             :   }
      79          34 : }
      80             : 
      81             : static edge_connection_t *
      82          17 : dummy_edge_conn_new(circuit_t *circ,
      83             :                     int type, size_t in_bytes, size_t out_bytes)
      84             : {
      85          17 :   edge_connection_t *conn;
      86          17 :   buf_t *inbuf, *outbuf;
      87             : 
      88          17 :   if (type == CONN_TYPE_EXIT)
      89          13 :     conn = edge_connection_new(type, AF_INET);
      90             :   else
      91           4 :     conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET));
      92             : 
      93          17 :   inbuf = TO_CONN(conn)->inbuf;
      94          17 :   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          17 :   add_bytes_to_buf(inbuf, in_bytes);
      99          17 :   add_bytes_to_buf(outbuf, out_bytes);
     100             : 
     101          17 :   conn->on_circuit = circ;
     102          17 :   if (type == CONN_TYPE_EXIT) {
     103          13 :     or_circuit_t *oc  = TO_OR_CIRCUIT(circ);
     104          13 :     conn->next_stream = oc->n_streams;
     105          13 :     oc->n_streams = conn;
     106             :   } else {
     107           4 :     origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
     108           4 :     conn->next_stream = oc->p_streams;
     109           4 :     oc->p_streams = conn;
     110             :   }
     111             : 
     112          17 :   return conn;
     113             : }
     114             : 
     115             : /** Run unit tests for buffers.c */
     116             : static void
     117           1 : test_oom_circbuf(void *arg)
     118             : {
     119           1 :   or_options_t *options = get_options_mutable();
     120           1 :   circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL;
     121           1 :   uint64_t now_ns = 1389631048 * (uint64_t)1000000000;
     122           1 :   const uint64_t start_ns = now_ns;
     123             : 
     124           1 :   (void) arg;
     125             : 
     126           1 :   monotime_enable_test_mocking();
     127           1 :   MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
     128             : 
     129             :   /* Far too low for real life. */
     130           1 :   options->MaxMemInQueues = 256*packed_cell_mem_cost();
     131           1 :   options->CellStatistics = 0;
     132             : 
     133           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */
     134           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0);
     135           1 :   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           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     140           1 :   c1 = dummy_origin_circuit_new(30);
     141             : 
     142           1 :   now_ns += 10 * 1000000;
     143           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     144           1 :   c2 = dummy_or_circuit_new(20, 20);
     145             : 
     146           1 :   tt_int_op(packed_cell_mem_cost(), OP_EQ,
     147             :             sizeof(packed_cell_t));
     148           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     149             :             packed_cell_mem_cost() * 70);
     150           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
     151             : 
     152           1 :   now_ns += 10 * 1000000;
     153           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     154           1 :   c3 = dummy_or_circuit_new(100, 85);
     155           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
     156           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     157             :             packed_cell_mem_cost() * 255);
     158             : 
     159           1 :   now_ns += 10 * 1000000;
     160           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     161             :   /* Adding this cell will trigger our OOM handler. */
     162           1 :   c4 = dummy_or_circuit_new(2, 0);
     163             : 
     164           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     165             :             packed_cell_mem_cost() * 257);
     166             : 
     167           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
     168             : 
     169           1 :   tt_assert(c1->marked_for_close);
     170           1 :   tt_assert(! c2->marked_for_close);
     171           1 :   tt_assert(! c3->marked_for_close);
     172           1 :   tt_assert(! c4->marked_for_close);
     173             : 
     174           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     175             :             packed_cell_mem_cost() * (257 - 30));
     176             : 
     177           1 :   circuit_free(c1);
     178             : 
     179           1 :   monotime_coarse_set_mock_time_nsec(start_ns); /* go back in time */
     180           1 :   c1 = dummy_or_circuit_new(90, 0);
     181             : 
     182           1 :   now_ns += 10 * 1000000;
     183           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     184             : 
     185           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
     186             : 
     187           1 :   tt_assert(c1->marked_for_close);
     188           1 :   tt_assert(! c2->marked_for_close);
     189           1 :   tt_assert(! c3->marked_for_close);
     190           1 :   tt_assert(! c4->marked_for_close);
     191             : 
     192           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     193             :             packed_cell_mem_cost() * (257 - 30));
     194             : 
     195           1 :  done:
     196           1 :   circuit_free(c1);
     197           1 :   circuit_free(c2);
     198           1 :   circuit_free(c3);
     199           1 :   circuit_free(c4);
     200             : 
     201           1 :   UNMOCK(circuit_mark_for_close_);
     202           1 :   monotime_disable_test_mocking();
     203           1 : }
     204             : 
     205             : /** Run unit tests for buffers.c */
     206             : static void
     207           1 : test_oom_streambuf(void *arg)
     208             : {
     209           1 :   or_options_t *options = get_options_mutable();
     210           1 :   circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL;
     211           1 :   uint32_t tvts;
     212           1 :   int i;
     213           1 :   smartlist_t *edgeconns = smartlist_new();
     214           1 :   const uint64_t start_ns = 1389641159 * (uint64_t)1000000000;
     215           1 :   uint64_t now_ns = start_ns;
     216             : 
     217           1 :   (void) arg;
     218           1 :   monotime_enable_test_mocking();
     219             : 
     220           1 :   MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
     221             : 
     222             :   /* Far too low for real life. */
     223           1 :   options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34;
     224           1 :   options->CellStatistics = 0;
     225             : 
     226           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */
     227           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0);
     228           1 :   tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
     229             : 
     230           1 :   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           1 :   monotime_coarse_set_mock_time_nsec(start_ns + 500 * 1000000);
     236           1 :   c1 = dummy_or_circuit_new(10,10);
     237             : 
     238           1 :   monotime_coarse_set_mock_time_nsec(start_ns + 510 * 1000000);
     239           1 :   c2 = dummy_origin_circuit_new(20);
     240           1 :   monotime_coarse_set_mock_time_nsec(start_ns + 520 * 1000000);
     241           1 :   c3 = dummy_or_circuit_new(20,20);
     242           1 :   monotime_coarse_set_mock_time_nsec(start_ns + 530 * 1000000);
     243           1 :   c4 = dummy_or_circuit_new(0,0);
     244           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     245             :             packed_cell_mem_cost() * 80);
     246             : 
     247           1 :   now_ns = start_ns + 600 * 1000000;
     248           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     249             : 
     250             :   /* Add some connections to c1...c4. */
     251           6 :   for (i = 0; i < 4; ++i) {
     252           4 :     edge_connection_t *ec;
     253             :     /* link it to a circuit */
     254           4 :     now_ns += 10 * 1000000;
     255           4 :     monotime_coarse_set_mock_time_nsec(now_ns);
     256           4 :     ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000);
     257           4 :     tt_assert(ec);
     258           4 :     smartlist_add(edgeconns, ec);
     259           4 :     now_ns += 10 * 1000000;
     260           4 :     monotime_coarse_set_mock_time_nsec(now_ns);
     261           4 :     ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000);
     262           4 :     tt_assert(ec);
     263           4 :     smartlist_add(edgeconns, ec);
     264           4 :     now_ns += 10 * 1000000;
     265           4 :     monotime_coarse_set_mock_time_nsec(now_ns);
     266           4 :     ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/
     267           4 :     tt_assert(ec);
     268           4 :     smartlist_add(edgeconns, ec);
     269           4 :     now_ns += 10 * 1000000;
     270           4 :     monotime_coarse_set_mock_time_nsec(now_ns);
     271           4 :     ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
     272           4 :     smartlist_add(edgeconns, ec);
     273           4 :     tt_assert(ec);
     274             :   }
     275             : 
     276           1 :   now_ns -= now_ns % 1000000000;
     277           1 :   now_ns += 1000000000;
     278           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     279           1 :   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           1 :   ts_is_approx(circuit_max_queued_cell_age(c1, tvts), 500);
     288           1 :   ts_is_approx(circuit_max_queued_cell_age(c2, tvts), 490);
     289           1 :   ts_is_approx(circuit_max_queued_cell_age(c3, tvts), 480);
     290           1 :   ts_is_approx(circuit_max_queued_cell_age(c4, tvts), 0);
     291             : 
     292           1 :   ts_is_approx(circuit_max_queued_data_age(c1, tvts), 390);
     293           1 :   ts_is_approx(circuit_max_queued_data_age(c2, tvts), 380);
     294           1 :   ts_is_approx(circuit_max_queued_data_age(c3, tvts), 0);
     295           1 :   ts_is_approx(circuit_max_queued_data_age(c4, tvts), 370);
     296             : 
     297           1 :   ts_is_approx(circuit_max_queued_item_age(c1, tvts), 500);
     298           1 :   ts_is_approx(circuit_max_queued_item_age(c2, tvts), 490);
     299           1 :   ts_is_approx(circuit_max_queued_item_age(c3, tvts), 480);
     300           1 :   ts_is_approx(circuit_max_queued_item_age(c4, tvts), 370);
     301             : 
     302           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     303             :             packed_cell_mem_cost() * 80);
     304           1 :   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           1 :     edge_connection_t *ec;
     309           1 :     now_ns -= 1000000000;
     310           1 :     monotime_coarse_set_mock_time_nsec(now_ns);
     311           1 :     ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
     312           1 :     tt_assert(ec);
     313           1 :     smartlist_add(edgeconns, ec);
     314             :   }
     315           1 :   tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2);
     316           1 :   ts_is_approx(circuit_max_queued_item_age(c4, tvts), 1000);
     317             : 
     318           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 0);
     319             : 
     320             :   /* And run over the limit. */
     321           1 :   now_ns += 800*1000000;
     322           1 :   monotime_coarse_set_mock_time_nsec(now_ns);
     323           1 :   c5 = dummy_or_circuit_new(0,5);
     324             : 
     325           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     326             :             packed_cell_mem_cost() * 85);
     327           1 :   tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2);
     328             : 
     329           1 :   tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
     330             : 
     331             :   /* C4 should have died. */
     332           1 :   tt_assert(! c1->marked_for_close);
     333           1 :   tt_assert(! c2->marked_for_close);
     334           1 :   tt_assert(! c3->marked_for_close);
     335           1 :   tt_assert(c4->marked_for_close);
     336           1 :   tt_assert(! c5->marked_for_close);
     337             : 
     338           1 :   tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
     339             :             packed_cell_mem_cost() * 85);
     340           1 :   tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*8*2);
     341             : 
     342           1 :  done:
     343           1 :   circuit_free(c1);
     344           1 :   circuit_free(c2);
     345           1 :   circuit_free(c3);
     346           1 :   circuit_free(c4);
     347           1 :   circuit_free(c5);
     348             : 
     349          18 :   SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec,
     350             :                     connection_free_minimal(TO_CONN(ec)));
     351           1 :   smartlist_free(edgeconns);
     352             : 
     353           1 :   UNMOCK(circuit_mark_for_close_);
     354           1 :   monotime_disable_test_mocking();
     355           1 : }
     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             : };
     362             : 

Generated by: LCOV version 1.14