mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 13:13:44 +01:00
Merge remote-tracking branch 'origin/maint-0.2.3' into maint-0.2.4
Conflicts: src/or/config.c src/or/relay.c
This commit is contained in:
commit
d3063da691
4
changes/bug9002
Normal file
4
changes/bug9002
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
o Major bugfixes:
|
||||||
|
- Limit hidden service descriptors to at most ten introduction
|
||||||
|
points, to slow one kind of guard enumeration. Fixes bug 9002;
|
||||||
|
bugfix on 0.1.1.11-alpha.
|
15
changes/bug9063_redux
Normal file
15
changes/bug9063_redux
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
o Major bugfixes:
|
||||||
|
- When we have too much memory queued in circuits (according to a new
|
||||||
|
MaxMemInCellQueues option), close the circuits consuming the most
|
||||||
|
memory. This prevents us from running out of memory as a relay if
|
||||||
|
circuits fill up faster than they can be drained. Fixes
|
||||||
|
bug 9063; bugfix on the 54th commit of Tor. This bug is a further
|
||||||
|
fix beyond bug 6252, whose fix was merged into 0.2.3.21-rc.
|
||||||
|
|
||||||
|
Also fixes an earlier approach taken in 0.2.4.13-alpha, where we
|
||||||
|
tried to solve this issue simply by imposing an upper limit on the
|
||||||
|
number of queued cells for a single circuit. That approach proved to
|
||||||
|
be problematic, since there are ways to provoke clients to send a
|
||||||
|
number of cells in excess of any such reasonable limit.
|
||||||
|
Fixes bug 9072; bugfix on 0.2.4.13-alpha.
|
||||||
|
|
@ -1663,6 +1663,15 @@ is non-zero):
|
|||||||
localhost, RFC1918 addresses, and so on. This can create security issues;
|
localhost, RFC1918 addresses, and so on. This can create security issues;
|
||||||
you should probably leave it off. (Default: 0)
|
you should probably leave it off. (Default: 0)
|
||||||
|
|
||||||
|
**MaxMemInCellQueues** __N__ **bytes**|**KB**|**MB**|**GB**::
|
||||||
|
This option configures a threshold above which Tor will assume that it
|
||||||
|
needs to stop queueing cells because it's about to run out of memory.
|
||||||
|
If it hits this threshold, it will begin killing circuits until it
|
||||||
|
has recovered at least 10% of this memory. Do not set this option too
|
||||||
|
low, or your relay may be unreliable under load. This option only
|
||||||
|
effects circuit queues, so the actual process size will be larger than
|
||||||
|
this. (Default: 8GB)
|
||||||
|
|
||||||
DIRECTORY SERVER OPTIONS
|
DIRECTORY SERVER OPTIONS
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ void mp_pool_destroy(mp_pool_t *pool);
|
|||||||
void mp_pool_assert_ok(mp_pool_t *pool);
|
void mp_pool_assert_ok(mp_pool_t *pool);
|
||||||
void mp_pool_log_status(mp_pool_t *pool, int severity);
|
void mp_pool_log_status(mp_pool_t *pool, int severity);
|
||||||
|
|
||||||
|
#define MP_POOL_ITEM_OVERHEAD (sizeof(void*))
|
||||||
|
|
||||||
#define MEMPOOL_STATS
|
#define MEMPOOL_STATS
|
||||||
|
|
||||||
#ifdef MEMPOOL_PRIVATE
|
#ifdef MEMPOOL_PRIVATE
|
||||||
|
@ -1499,6 +1499,108 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to
|
||||||
|
* recover memory. */
|
||||||
|
static void
|
||||||
|
marked_circuit_free_cells(circuit_t *circ)
|
||||||
|
{
|
||||||
|
if (!circ->marked_for_close) {
|
||||||
|
log_warn(LD_BUG, "Called on non-marked circuit");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cell_queue_clear(&circ->n_conn_cells);
|
||||||
|
if (! CIRCUIT_IS_ORIGIN(circ))
|
||||||
|
cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_conn_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return the number of cells used by the circuit <b>c</b>'s cell queues. */
|
||||||
|
static size_t
|
||||||
|
n_cells_in_circ_queues(const circuit_t *c)
|
||||||
|
{
|
||||||
|
size_t n = c->n_conn_cells.n;
|
||||||
|
if (! CIRCUIT_IS_ORIGIN(c))
|
||||||
|
n += TO_OR_CIRCUIT((circuit_t*)c)->p_conn_cells.n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** helper to sort a list of circuit_q by total queue lengths, in descending
|
||||||
|
* order. */
|
||||||
|
static int
|
||||||
|
circuits_compare_by_queue_len_(const void **a_, const void **b_)
|
||||||
|
{
|
||||||
|
const circuit_t *a = *a_;
|
||||||
|
const circuit_t *b = *b_;
|
||||||
|
size_t a_n = n_cells_in_circ_queues(a);
|
||||||
|
size_t b_n = n_cells_in_circ_queues(b);
|
||||||
|
|
||||||
|
if (a_n < b_n)
|
||||||
|
return 1;
|
||||||
|
else if (a_n == b_n)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM 0.90
|
||||||
|
|
||||||
|
/** We're out of memory for cells, having allocated <b>current_allocation</b>
|
||||||
|
* bytes' worth. Kill the 'worst' circuits until we're under
|
||||||
|
* FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM of our maximum usage. */
|
||||||
|
void
|
||||||
|
circuits_handle_oom(size_t current_allocation)
|
||||||
|
{
|
||||||
|
/* Let's hope there's enough slack space for this allocation here... */
|
||||||
|
smartlist_t *circlist = smartlist_new();
|
||||||
|
circuit_t *circ;
|
||||||
|
size_t n_cells_removed=0, n_cells_to_remove;
|
||||||
|
int n_circuits_killed=0;
|
||||||
|
log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
|
||||||
|
"over-long queues. (This behavior is controlled by "
|
||||||
|
"MaxMemInCellQueues.)");
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t mem_target = (size_t)(get_options()->MaxMemInCellQueues *
|
||||||
|
FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM);
|
||||||
|
size_t mem_to_recover;
|
||||||
|
if (current_allocation <= mem_target)
|
||||||
|
return;
|
||||||
|
mem_to_recover = current_allocation - mem_target;
|
||||||
|
n_cells_to_remove = CEIL_DIV(mem_to_recover, packed_cell_mem_cost());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This algorithm itself assumes that you've got enough memory slack
|
||||||
|
* to actually run it. */
|
||||||
|
for (circ = global_circuitlist; circ; circ = circ->next)
|
||||||
|
smartlist_add(circlist, circ);
|
||||||
|
|
||||||
|
/* This is O(n log n); there are faster algorithms we could use instead.
|
||||||
|
* Let's hope this doesn't happen enough to be in the critical path. */
|
||||||
|
smartlist_sort(circlist, circuits_compare_by_queue_len_);
|
||||||
|
|
||||||
|
/* Okay, now the worst circuits are at the front of the list. Let's mark
|
||||||
|
* them, and reclaim their storage aggressively. */
|
||||||
|
SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {
|
||||||
|
size_t n = n_cells_in_circ_queues(circ);
|
||||||
|
if (! circ->marked_for_close) {
|
||||||
|
circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
|
||||||
|
}
|
||||||
|
marked_circuit_free_cells(circ);
|
||||||
|
|
||||||
|
++n_circuits_killed;
|
||||||
|
n_cells_removed += n;
|
||||||
|
if (n_cells_removed >= n_cells_to_remove)
|
||||||
|
break;
|
||||||
|
} SMARTLIST_FOREACH_END(circ);
|
||||||
|
|
||||||
|
clean_cell_pool(); /* In case this helps. */
|
||||||
|
|
||||||
|
log_notice(LD_GENERAL, "Removed "U64_FORMAT" bytes by killing %d circuits.",
|
||||||
|
U64_PRINTF_ARG(n_cells_removed * packed_cell_mem_cost()),
|
||||||
|
n_circuits_killed);
|
||||||
|
|
||||||
|
smartlist_free(circlist);
|
||||||
|
}
|
||||||
|
|
||||||
/** Verify that cpath layer <b>cp</b> has all of its invariants
|
/** Verify that cpath layer <b>cp</b> has all of its invariants
|
||||||
* correct. Trigger an assert if anything is invalid.
|
* correct. Trigger an assert if anything is invalid.
|
||||||
*/
|
*/
|
||||||
|
@ -61,6 +61,7 @@ int circuit_count_pending_on_channel(channel_t *chan);
|
|||||||
void assert_cpath_layer_ok(const crypt_path_t *cp);
|
void assert_cpath_layer_ok(const crypt_path_t *cp);
|
||||||
void assert_circuit_ok(const circuit_t *c);
|
void assert_circuit_ok(const circuit_t *c);
|
||||||
void circuit_free_all(void);
|
void circuit_free_all(void);
|
||||||
|
void circuits_handle_oom(size_t current_allocation);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -299,6 +299,7 @@ static config_var_t option_vars_[] = {
|
|||||||
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
|
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
|
||||||
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
|
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
|
||||||
V(MaxClientCircuitsPending, UINT, "32"),
|
V(MaxClientCircuitsPending, UINT, "32"),
|
||||||
|
V(MaxMemInCellQueues, MEMUNIT, "8 GB"),
|
||||||
OBSOLETE("MaxOnionsPending"),
|
OBSOLETE("MaxOnionsPending"),
|
||||||
V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"),
|
V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"),
|
||||||
V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"),
|
V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"),
|
||||||
@ -2610,7 +2611,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
|||||||
REJECT("If EntryNodes is set, UseEntryGuards must be enabled.");
|
REJECT("If EntryNodes is set, UseEntryGuards must be enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options->MaxMemInCellQueues < (500 << 20)) {
|
||||||
|
log_warn(LD_CONFIG, "MaxMemInCellQueues must be at least 500 MB for now. "
|
||||||
|
"Ideally, have it as large as you can afford.");
|
||||||
|
options->MaxMemInCellQueues = (500 << 20);
|
||||||
|
}
|
||||||
|
|
||||||
options->AllowInvalid_ = 0;
|
options->AllowInvalid_ = 0;
|
||||||
|
|
||||||
if (options->AllowInvalidNodes) {
|
if (options->AllowInvalidNodes) {
|
||||||
SMARTLIST_FOREACH_BEGIN(options->AllowInvalidNodes, const char *, cp) {
|
SMARTLIST_FOREACH_BEGIN(options->AllowInvalidNodes, const char *, cp) {
|
||||||
if (!strcasecmp(cp, "entry"))
|
if (!strcasecmp(cp, "entry"))
|
||||||
|
@ -3433,6 +3433,10 @@ typedef struct {
|
|||||||
config_line_t *DirPort_lines;
|
config_line_t *DirPort_lines;
|
||||||
config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */
|
config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */
|
||||||
|
|
||||||
|
uint64_t MaxMemInCellQueues; /**< If we have more memory than this allocated
|
||||||
|
* for circuit cell queues, run the OOM handler
|
||||||
|
*/
|
||||||
|
|
||||||
/** @name port booleans
|
/** @name port booleans
|
||||||
*
|
*
|
||||||
* Derived booleans: True iff there is a non-listener port on an AF_INET or
|
* Derived booleans: True iff there is a non-listener port on an AF_INET or
|
||||||
|
@ -2034,7 +2034,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** The total number of cells we have allocated from the memory pool. */
|
/** The total number of cells we have allocated from the memory pool. */
|
||||||
static int total_cells_allocated = 0;
|
static size_t total_cells_allocated = 0;
|
||||||
|
|
||||||
/** A memory pool to allocate packed_cell_t objects. */
|
/** A memory pool to allocate packed_cell_t objects. */
|
||||||
static mp_pool_t *cell_pool = NULL;
|
static mp_pool_t *cell_pool = NULL;
|
||||||
@ -2114,7 +2114,7 @@ dump_cell_pool_usage(int severity)
|
|||||||
}
|
}
|
||||||
tor_log(severity, LD_MM,
|
tor_log(severity, LD_MM,
|
||||||
"%d cells allocated on %d circuits. %d cells leaked.",
|
"%d cells allocated on %d circuits. %d cells leaked.",
|
||||||
n_cells, n_circs, total_cells_allocated - n_cells);
|
n_cells, n_circs, (int)total_cells_allocated - n_cells);
|
||||||
mp_pool_log_status(cell_pool, severity);
|
mp_pool_log_status(cell_pool, severity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2222,6 +2222,29 @@ cell_queue_pop(cell_queue_t *queue)
|
|||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return the total number of bytes used for each packed_cell in a queue.
|
||||||
|
* Approximate. */
|
||||||
|
size_t
|
||||||
|
packed_cell_mem_cost(void)
|
||||||
|
{
|
||||||
|
return sizeof(packed_cell_t) + MP_POOL_ITEM_OVERHEAD +
|
||||||
|
get_options()->CellStatistics ?
|
||||||
|
(sizeof(insertion_time_elem_t)+MP_POOL_ITEM_OVERHEAD) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check whether we've got too much space used for cells. If so,
|
||||||
|
* call the OOM handler and return 1. Otherwise, return 0. */
|
||||||
|
static int
|
||||||
|
cell_queues_check_size(void)
|
||||||
|
{
|
||||||
|
size_t alloc = total_cells_allocated * packed_cell_mem_cost();
|
||||||
|
if (alloc >= get_options()->MaxMemInCellQueues) {
|
||||||
|
circuits_handle_oom(alloc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the number of cells available on the circuit's n_chan or p_chan's
|
* Update the number of cells available on the circuit's n_chan or p_chan's
|
||||||
* circuit mux.
|
* circuit mux.
|
||||||
@ -2513,6 +2536,12 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
|
|||||||
|
|
||||||
cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids);
|
cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids);
|
||||||
|
|
||||||
|
if (PREDICT_UNLIKELY(cell_queues_check_size())) {
|
||||||
|
/* We ran the OOM handler */
|
||||||
|
if (circ->marked_for_close)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* If we have too many cells on the circuit, we should stop reading from
|
/* If we have too many cells on the circuit, we should stop reading from
|
||||||
* the edge streams for a while. */
|
* the edge streams for a while. */
|
||||||
if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE)
|
if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE)
|
||||||
|
@ -46,6 +46,7 @@ void init_cell_pool(void);
|
|||||||
void free_cell_pool(void);
|
void free_cell_pool(void);
|
||||||
void clean_cell_pool(void);
|
void clean_cell_pool(void);
|
||||||
void dump_cell_pool_usage(int severity);
|
void dump_cell_pool_usage(int severity);
|
||||||
|
size_t packed_cell_mem_cost(void);
|
||||||
|
|
||||||
/* For channeltls.c */
|
/* For channeltls.c */
|
||||||
void packed_cell_free(packed_cell_t *cell);
|
void packed_cell_free(packed_cell_t *cell);
|
||||||
|
@ -1002,6 +1002,10 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do not allow more than this many introduction points in a hidden service
|
||||||
|
* descriptor */
|
||||||
|
#define MAX_INTRO_POINTS 10
|
||||||
|
|
||||||
/** Parse *desc, calculate its service id, and store it in the cache.
|
/** Parse *desc, calculate its service id, and store it in the cache.
|
||||||
* If we have a newer v0 descriptor with the same ID, ignore this one.
|
* If we have a newer v0 descriptor with the same ID, ignore this one.
|
||||||
* If we have an older descriptor with the same ID, replace it.
|
* If we have an older descriptor with the same ID, replace it.
|
||||||
@ -1070,6 +1074,15 @@ rend_cache_store(const char *desc, size_t desc_len, int published,
|
|||||||
rend_service_descriptor_free(parsed);
|
rend_service_descriptor_free(parsed);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (parsed->intro_nodes &&
|
||||||
|
smartlist_len(parsed->intro_nodes) > MAX_INTRO_POINTS) {
|
||||||
|
log_warn(LD_REND, "Found too many introduction points on a hidden "
|
||||||
|
"service descriptor for %s. This is probably a (misguided) "
|
||||||
|
"attempt to improve reliability, but it could also be an "
|
||||||
|
"attempt to do a guard enumeration attack. Rejecting.",
|
||||||
|
safe_str_client(query));
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
tor_snprintf(key, sizeof(key), "0%s", query);
|
tor_snprintf(key, sizeof(key), "0%s", query);
|
||||||
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
|
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
|
||||||
if (e && e->parsed->timestamp > parsed->timestamp) {
|
if (e && e->parsed->timestamp > parsed->timestamp) {
|
||||||
@ -1288,6 +1301,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
|||||||
}
|
}
|
||||||
/* Decode/decrypt introduction points. */
|
/* Decode/decrypt introduction points. */
|
||||||
if (intro_content) {
|
if (intro_content) {
|
||||||
|
int n_intro_points;
|
||||||
if (rend_query->auth_type != REND_NO_AUTH &&
|
if (rend_query->auth_type != REND_NO_AUTH &&
|
||||||
!tor_mem_is_zero(rend_query->descriptor_cookie,
|
!tor_mem_is_zero(rend_query->descriptor_cookie,
|
||||||
sizeof(rend_query->descriptor_cookie))) {
|
sizeof(rend_query->descriptor_cookie))) {
|
||||||
@ -1308,13 +1322,20 @@ rend_cache_store_v2_desc_as_client(const char *desc,
|
|||||||
intro_size = ipos_decrypted_size;
|
intro_size = ipos_decrypted_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rend_parse_introduction_points(parsed, intro_content,
|
n_intro_points = rend_parse_introduction_points(parsed, intro_content,
|
||||||
intro_size) <= 0) {
|
intro_size);
|
||||||
|
if (n_intro_points <= 0) {
|
||||||
log_warn(LD_REND, "Failed to parse introduction points. Either the "
|
log_warn(LD_REND, "Failed to parse introduction points. Either the "
|
||||||
"service has published a corrupt descriptor or you have "
|
"service has published a corrupt descriptor or you have "
|
||||||
"provided invalid authorization data.");
|
"provided invalid authorization data.");
|
||||||
retval = -2;
|
retval = -2;
|
||||||
goto err;
|
goto err;
|
||||||
|
} else if (n_intro_points > MAX_INTRO_POINTS) {
|
||||||
|
log_warn(LD_REND, "Found too many introduction points on a hidden "
|
||||||
|
"service descriptor for %s. This is probably a (misguided) "
|
||||||
|
"attempt to improve reliability, but it could also be an "
|
||||||
|
"attempt to do a guard enumeration attack. Rejecting.",
|
||||||
|
safe_str_client(rend_query->onion_address));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_info(LD_REND, "Descriptor does not contain any introduction points.");
|
log_info(LD_REND, "Descriptor does not contain any introduction points.");
|
||||||
|
Loading…
Reference in New Issue
Block a user