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.)
This commit is contained in:
Nick Mathewson 2017-02-07 10:58:02 -05:00
parent 635c5a8a92
commit 194e31057f
2 changed files with 38 additions and 19 deletions

8
changes/trove-2017-001.2 Normal file
View File

@ -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.

View 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.