diff --git a/changes/bug10409 b/changes/bug10409
new file mode 100644
index 0000000000..5ef5ae29de
--- /dev/null
+++ b/changes/bug10409
@@ -0,0 +1,3 @@
+ o Minor bugfixes:
+ - Avoid a crash bug when starting with a corrupted microdescriptor
+ cache file. Fix for bug 10406; bugfix on 0.2.2.6-alpha.
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 456a033ec9..4d3ee9cdb3 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -3592,7 +3592,8 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
{
smartlist_t *lst = microdescs_parse_from_string(output,
- output+strlen(output), 0, 1);
+ output+strlen(output), 0,
+ SAVED_NOWHERE);
if (smartlist_len(lst) != 1) {
log_warn(LD_DIR, "We generated a microdescriptor we couldn't parse.");
SMARTLIST_FOREACH(lst, microdesc_t *, md, microdesc_free(md));
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 7765d27da6..2bf6f54ab6 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -154,11 +154,10 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
{
smartlist_t *descriptors, *added;
const int allow_annotations = (where != SAVED_NOWHERE);
- const int copy_body = (where != SAVED_IN_CACHE);
descriptors = microdescs_parse_from_string(s, eos,
allow_annotations,
- copy_body);
+ where);
if (listed_at != (time_t)-1) {
SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
md->last_listed = listed_at);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 6d837159e1..571167fad0 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -4240,12 +4240,17 @@ find_start_of_next_microdesc(const char *s, const char *eos)
/** Parse as many microdescriptors as are found from the string starting at
* s and ending at eos. If allow_annotations is set, read any
- * annotations we recognize and ignore ones we don't. If copy_body is
- * true, then strdup the bodies of the microdescriptors. Return all newly
+ * annotations we recognize and ignore ones we don't.
+ *
+ * If saved_location isn't SAVED_IN_CACHE, make a local copy of each
+ * descriptor in the body field of each microdesc_t.
+ *
+ * Return all newly
* parsed microdescriptors in a newly allocated smartlist_t. */
smartlist_t *
microdescs_parse_from_string(const char *s, const char *eos,
- int allow_annotations, int copy_body)
+ int allow_annotations,
+ saved_location_t where)
{
smartlist_t *tokens;
smartlist_t *result;
@@ -4254,6 +4259,7 @@ microdescs_parse_from_string(const char *s, const char *eos,
const char *start = s;
const char *start_of_next_microdesc;
int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0;
+ const int copy_body = (where != SAVED_IN_CACHE);
directory_token_t *tok;
@@ -4283,6 +4289,7 @@ microdescs_parse_from_string(const char *s, const char *eos,
tor_assert(cp);
md->bodylen = start_of_next_microdesc - cp;
+ md->saved_location = where;
if (copy_body)
md->body = tor_memdup_nulterm(cp, md->bodylen);
else
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index c65cdc996c..eb2e885cb1 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -64,7 +64,7 @@ ns_detached_signatures_t *networkstatus_parse_detached_signatures(
smartlist_t *microdescs_parse_from_string(const char *s, const char *eos,
int allow_annotations,
- int copy_body);
+ saved_location_t where);
authority_cert_t *authority_cert_parse_from_string(const char *s,
const char **end_of_string);
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index a8171a325c..53a03a48ad 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -240,8 +240,53 @@ test_md_cache(void *data)
tor_free(fn);
}
+static const char truncated_md[] =
+ "@last-listed 2013-08-08 19:02:59\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM91vLFNaM+gGhnRIdz2Cm/Kl7Xz0cOobIdVzhS3cKUJfk867hCuTipS\n"
+ "NveLBzNopvgXKruAAzEj3cACxk6Q8lv5UWOGCD1UolkgsWSE62RBjap44g+oc9J1\n"
+ "RI9968xOTZw0VaBQg9giEILNXl0djoikQ+5tQRUvLDDa67gpa5Q1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "family @\n";
+
+static void
+test_md_cache_broken(void *data)
+{
+ or_options_t *options;
+ char *fn=NULL;
+ microdesc_cache_t *mc = NULL;
+
+ (void)data;
+
+ options = get_options_mutable();
+ tt_assert(options);
+ options->DataDirectory = tor_strdup(get_fname("md_datadir_test2"));
+
+#ifdef _WIN32
+ tt_int_op(0, ==, mkdir(options->DataDirectory));
+#else
+ tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
+#endif
+
+ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
+ options->DataDirectory);
+
+ write_str_to_file(fn, truncated_md, 1);
+
+ mc = get_microdesc_cache();
+ tt_assert(mc);
+
+ done:
+ if (options)
+ tor_free(options->DataDirectory);
+ tor_free(fn);
+ microdesc_free_all();
+}
+
struct testcase_t microdesc_tests[] = {
{ "cache", test_md_cache, TT_FORK, NULL, NULL },
+ { "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};