aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2017-02-07 10:58:02 -0500
committerNick Mathewson <nickm@torproject.org>2017-02-14 16:10:27 -0500
commit194e31057fbf07d6bdf4b62d26e1a9db334e5f1c (patch)
tree5dfedea08b7fb6b4bc0a0e2cae42e3f0afb1f77f
parent635c5a8a92c8066412645b291817eadcc82d8f8f (diff)
downloadtor-194e31057fbf07d6bdf4b62d26e1a9db334e5f1c.tar.gz
tor-194e31057fbf07d6bdf4b62d26e1a9db334e5f1c.zip
Avoid integer underflow in tor_version_compare.
Fix for TROVE-2017-001 and bug 21278. (Note: Instead of handling signed ints "correctly", we keep the old behavior, except for the part where we would crash with -ftrapv.)
-rw-r--r--changes/trove-2017-001.28
-rw-r--r--src/or/routerparse.c49
2 files changed, 38 insertions, 19 deletions
diff --git a/changes/trove-2017-001.2 b/changes/trove-2017-001.2
new file mode 100644
index 0000000000..3ef073cf9f
--- /dev/null
+++ b/changes/trove-2017-001.2
@@ -0,0 +1,8 @@
+ o Major bugfixes (parsing):
+ - Fix an integer underflow bug when comparing malformed Tor versions.
+ This bug is harmless, except when Tor has been built with
+ --enable-expensive-hardening, which would turn it into a crash;
+ or on Tor 0.2.9.1-alpha through Tor 0.2.9.8, which were built with
+ -ftrapv by default.
+ Part of TROVE-2017-001. Fixes bug 21278; bugfix on
+ 0.0.8pre1. Found by OSS-Fuzz.
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 71373ce63e..1243035f90 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -4545,26 +4545,37 @@ tor_version_compare(tor_version_t *a, tor_version_t *b)
int i;
tor_assert(a);
tor_assert(b);
- if ((i = a->major - b->major))
- return i;
- else if ((i = a->minor - b->minor))
- return i;
- else if ((i = a->micro - b->micro))
- return i;
- else if ((i = a->status - b->status))
- return i;
- else if ((i = a->patchlevel - b->patchlevel))
- return i;
- else if ((i = strcmp(a->status_tag, b->status_tag)))
- return i;
- else if ((i = a->svn_revision - b->svn_revision))
- return i;
- else if ((i = a->git_tag_len - b->git_tag_len))
- return i;
- else if (a->git_tag_len)
- return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
+
+ /* We take this approach to comparison to ensure the same (bogus!) behavior
+ * on all inputs as we would have seen before bug #21278 was fixed. The
+ * only important difference here is that this method doesn't cause
+ * a signed integer underflow.
+ */
+#define CMP(field) do { \
+ unsigned aval = (unsigned) a->field; \
+ unsigned bval = (unsigned) b->field; \
+ int result = (int) (aval - bval); \
+ if (result < 0) \
+ return -1; \
+ else if (result > 0) \
+ return 1; \
+ } while (0)
+
+ CMP(major);
+ CMP(minor);
+ CMP(micro);
+ CMP(status);
+ CMP(patchlevel);
+ if ((i = strcmp(a->status_tag, b->status_tag)))
+ return i;
+ CMP(svn_revision);
+ CMP(git_tag_len);
+ if (a->git_tag_len)
+ return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
else
- return 0;
+ return 0;
+
+#undef CMP
}
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.