aboutsummaryrefslogtreecommitdiff
path: root/src/or/microdesc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/microdesc.c')
-rw-r--r--src/or/microdesc.c107
1 files changed, 86 insertions, 21 deletions
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 0e72c0b89..fdb549a9a 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -45,12 +45,7 @@ struct microdesc_cache_t {
static INLINE unsigned int
microdesc_hash_(microdesc_t *md)
{
- unsigned *d = (unsigned*)md->digest;
-#if SIZEOF_INT == 4
- return d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6] ^ d[7];
-#else
- return d[0] ^ d[1] ^ d[2] ^ d[3];
-#endif
+ return (unsigned) siphash24g(md->digest, sizeof(md->digest));
}
/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */
@@ -139,7 +134,7 @@ get_microdesc_cache(void)
* ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no_save</b>,
* mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE,
* leave their bodies as pointers to the mmap'd cache. If where is
- * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive,
+ * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is not -1,
* set the last_listed field of every microdesc to listed_at. If
* requested_digests is non-null, then it contains a list of digests we mean
* to allow, so we should reject any non-requested microdesc with a different
@@ -158,7 +153,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
descriptors = microdescs_parse_from_string(s, eos,
allow_annotations,
where);
- if (listed_at > 0) {
+ if (listed_at != (time_t)-1) {
SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
md->last_listed = listed_at);
}
@@ -280,6 +275,7 @@ void
microdesc_cache_clear(microdesc_cache_t *cache)
{
microdesc_t **entry, **next;
+
for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) {
microdesc_t *md = *entry;
next = HT_NEXT_RMV(microdesc_map, &cache->map, entry);
@@ -288,7 +284,13 @@ microdesc_cache_clear(microdesc_cache_t *cache)
}
HT_CLEAR(microdesc_map, &cache->map);
if (cache->cache_content) {
- tor_munmap_file(cache->cache_content);
+ int res = tor_munmap_file(cache->cache_content);
+ if (res != 0) {
+ log_warn(LD_FS,
+ "tor_munmap_file() failed clearing microdesc cache; "
+ "we are probably about to leak memory.");
+ /* TODO something smarter? */
+ }
cache->cache_content = NULL;
}
cache->total_len_seen = 0;
@@ -368,7 +370,9 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
cutoff = now - TOLERATE_MICRODESC_AGE;
for (mdp = HT_START(microdesc_map, &cache->map); mdp != NULL; ) {
- if ((*mdp)->last_listed < cutoff) {
+ const int is_old = (*mdp)->last_listed < cutoff;
+ const unsigned held_by_nodes = (*mdp)->held_by_nodes;
+ if (is_old && !held_by_nodes) {
++dropped;
victim = *mdp;
mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp);
@@ -376,6 +380,57 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
bytes_dropped += victim->bodylen;
microdesc_free(victim);
} else {
+ if (is_old) {
+ /* It's old, but it has held_by_nodes set. That's not okay. */
+ /* Let's try to diagnose and fix #7164 . */
+ smartlist_t *nodes = nodelist_find_nodes_with_microdesc(*mdp);
+ const networkstatus_t *ns = networkstatus_get_latest_consensus();
+ long networkstatus_age = -1;
+ const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
+ if (ns) {
+ networkstatus_age = now - ns->valid_after;
+ }
+ log_warn(LD_BUG, "Microdescriptor seemed very old "
+ "(last listed %d hours ago vs %d hour cutoff), but is still "
+ "marked as being held by %d node(s). I found %d node(s) "
+ "holding it. Current networkstatus is %ld hours old. "
+ "Hashtable badness is %d.",
+ (int)((now - (*mdp)->last_listed) / 3600),
+ (int)((now - cutoff) / 3600),
+ held_by_nodes,
+ smartlist_len(nodes),
+ networkstatus_age / 3600,
+ ht_badness);
+
+ SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
+ const char *rs_match = "No RS";
+ const char *rs_present = "";
+ if (node->rs) {
+ if (tor_memeq(node->rs->descriptor_digest,
+ (*mdp)->digest, DIGEST256_LEN)) {
+ rs_match = "Microdesc digest in RS matches";
+ } else {
+ rs_match = "Microdesc digest in RS does match";
+ }
+ if (ns) {
+ /* This should be impossible, but let's see! */
+ rs_present = " RS not present in networkstatus.";
+ SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *,rs, {
+ if (rs == node->rs) {
+ rs_present = " RS okay in networkstatus.";
+ }
+ });
+ }
+ }
+ log_warn(LD_BUG, " [%d]: ID=%s. md=%p, rs=%p, ri=%p. %s.%s",
+ node_sl_idx,
+ hex_str(node->identity, DIGEST_LEN),
+ node->md, node->rs, node->ri, rs_match, rs_present);
+ } SMARTLIST_FOREACH_END(node);
+ smartlist_free(nodes);
+ (*mdp)->last_listed = now;
+ }
+
++kept;
mdp = HT_NEXT(microdesc_map, &cache->map, mdp);
}
@@ -434,7 +489,7 @@ int
microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
{
open_file_t *open_file;
- int fd = -1;
+ int fd = -1, res;
microdesc_t **mdp;
smartlist_t *wrote;
ssize_t size;
@@ -489,7 +544,8 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
"By my count, I'm at "I64_FORMAT
", but I should be at "I64_FORMAT,
I64_PRINTF_ARG(off), I64_PRINTF_ARG(off_real));
- off = off_real;
+ if (off_real >= 0)
+ off = off_real;
}
if (md->saved_location != SAVED_IN_CACHE) {
tor_free(md->body);
@@ -500,8 +556,14 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
/* We must do this unmap _before_ we call finish_writing_to_file(), or
* windows will not actually replace the file. */
- if (cache->cache_content)
- tor_munmap_file(cache->cache_content);
+ if (cache->cache_content) {
+ res = tor_munmap_file(cache->cache_content);
+ if (res != 0) {
+ log_warn(LD_FS,
+ "Failed to unmap old microdescriptor cache while rebuilding");
+ }
+ cache->cache_content = NULL;
+ }
if (finish_writing_to_file(open_file) < 0) {
log_warn(LD_DIR, "Error rebuilding microdescriptor cache: %s",
@@ -605,8 +667,10 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno)
tor_fragile_assert();
}
if (md->held_by_nodes) {
+ microdesc_cache_t *cache = get_microdesc_cache();
int found=0;
const smartlist_t *nodes = nodelist_get_list();
+ const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
SMARTLIST_FOREACH(nodes, node_t *, node, {
if (node->md == md) {
++found;
@@ -614,13 +678,14 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno)
}
});
if (found) {
- log_info(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
- "referenced %d node(s); held_by_nodes == %u",
- fname, lineno, found, md->held_by_nodes);
+ log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
+ "referenced %d node(s); held_by_nodes == %u, ht_badness == %d",
+ fname, lineno, found, md->held_by_nodes, ht_badness);
} else {
log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_by_nodes "
- "set to %u, but md was not referenced by any nodes",
- fname, lineno, md->held_by_nodes);
+ "set to %u, but md was not referenced by any nodes. "
+ "ht_badness == %d",
+ fname, lineno, md->held_by_nodes, ht_badness);
}
tor_fragile_assert();
}
@@ -696,7 +761,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
continue;
if (downloadable_only &&
!download_status_is_ready(&rs->dl_status, now,
- MAX_MICRODESC_DOWNLOAD_FAILURES))
+ get_options()->TestingMicrodescMaxDownloadTries))
continue;
if (skip && digestmap_get(skip, rs->descriptor_digest))
continue;
@@ -725,7 +790,7 @@ update_microdesc_downloads(time_t now)
smartlist_t *missing;
digestmap_t *pending;
- if (should_delay_dir_fetches(options))
+ if (should_delay_dir_fetches(options, NULL))
return;
if (directory_too_idle_to_fetch_descriptors(options, now))
return;