]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Avoid integer underflow in tor_version_compare.
authorNick Mathewson <nickm@torproject.org>
Tue, 7 Feb 2017 15:58:02 +0000 (10:58 -0500)
committerNick Mathewson <nickm@torproject.org>
Tue, 14 Feb 2017 21:10:27 +0000 (16:10 -0500)
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.)

changes/trove-2017-001.2 [new file with mode: 0644]
src/or/routerparse.c

diff --git a/changes/trove-2017-001.2 b/changes/trove-2017-001.2
new file mode 100644 (file)
index 0000000..3ef073c
--- /dev/null
@@ -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.
index 71373ce63e369c59d230bf435cbdb4f61af898cf..1243035f909fa747bca08be224a8915d368933eb 100644 (file)
@@ -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.