diff --git a/changes/bug7191 b/changes/bug7191
new file mode 100644
index 0000000000..a3bee6e5f7
--- /dev/null
+++ b/changes/bug7191
@@ -0,0 +1,5 @@
+ o Major bugfixes:
+ - Fix a denial of service attack by which any directory authority
+ could crash all the others, or by which a single v2 directory
+ authority could crash everybody downloading v2 directory
+ information. Fixes bug 7191; bugfix on 0.2.0.10-alpha.
diff --git a/src/common/container.c b/src/common/container.c
index d92f825784..ede98eca5a 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -571,7 +571,28 @@ smartlist_bsearch_idx(const smartlist_t *sl, const void *key,
int (*compare)(const void *key, const void **member),
int *found_out)
{
- int hi = smartlist_len(sl) - 1, lo = 0, cmp, mid;
+ const int len = smartlist_len(sl);
+ int hi, lo, cmp, mid;
+
+ if (len == 0) {
+ *found_out = 0;
+ return 0;
+ } else if (len == 1) {
+ cmp = compare(key, (const void **) &sl->list[0]);
+ if (cmp == 0) {
+ *found_out = 1;
+ return 0;
+ } else if (cmp < 0) {
+ *found_out = 0;
+ return 0;
+ } else {
+ *found_out = 0;
+ return 1;
+ }
+ }
+
+ hi = smartlist_len(sl) - 1;
+ lo = 0;
while (lo <= hi) {
mid = (lo + hi) / 2;
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index 615c489f41..9a306ad797 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -16,6 +16,15 @@ _compare_strs(const void **a, const void **b)
return strcmp(s1, s2);
}
+/** Helper: return a tristate based on comparing the strings in a and
+ * *b. */
+static int
+compare_strs_for_bsearch_(const void *a, const void **b)
+{
+ const char *s1 = a, *s2 = *b;
+ return strcmp(s1, s2);
+}
+
/** Helper: return a tristate based on comparing the strings in *a and
* *b, excluding a's first character, and ignoring case. */
static int
@@ -204,6 +213,8 @@ test_container_smartlist_strings(void)
/* Test bsearch_idx */
{
int f;
+ smartlist_t *tmp = NULL;
+
test_eq(0, smartlist_bsearch_idx(sl," aaa",_compare_without_first_ch,&f));
test_eq(f, 0);
test_eq(0, smartlist_bsearch_idx(sl," and",_compare_without_first_ch,&f));
@@ -216,6 +227,31 @@ test_container_smartlist_strings(void)
test_eq(f, 0);
test_eq(7, smartlist_bsearch_idx(sl," zzzz",_compare_without_first_ch,&f));
test_eq(f, 0);
+
+ /* Test trivial cases for list of length 0 or 1 */
+ tmp = smartlist_create();
+ test_eq(0, smartlist_bsearch_idx(tmp, "foo",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 0);
+ smartlist_insert(tmp, 0, (void *)("bar"));
+ test_eq(1, smartlist_bsearch_idx(tmp, "foo",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 0);
+ test_eq(0, smartlist_bsearch_idx(tmp, "aaa",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 0);
+ test_eq(0, smartlist_bsearch_idx(tmp, "bar",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 1);
+ /* ... and one for length 2 */
+ smartlist_insert(tmp, 1, (void *)("foo"));
+ test_eq(1, smartlist_bsearch_idx(tmp, "foo",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 1);
+ test_eq(2, smartlist_bsearch_idx(tmp, "goo",
+ compare_strs_for_bsearch_, &f));
+ test_eq(f, 0);
+ smartlist_free(tmp);
}
/* Test reverse() and pop_last() */