[tor-bugs] #18162 [- Select a component]: Potential heap corruption in smartlist_add(), smartlist_insert()
Tor Bug Tracker & Wiki
blackhole at torproject.org
Wed Jan 27 15:15:30 UTC 2016
#18162: Potential heap corruption in smartlist_add(), smartlist_insert()
--------------------------------------+-----------------
Reporter: asn | Owner:
Type: defect | Status: new
Priority: Medium | Milestone:
Component: - Select a component | Version:
Severity: Normal | Keywords:
Actual Points: | Parent ID:
Points: | Sponsor:
--------------------------------------+-----------------
Here follows vulnerability report by `Guido Vranken`.
The attack requires minimum 16GB of available memory on the victim's host,
so it's quite hard to be exploited.
== Walkthrough of the vulnerability ==
`smartlist_add` and `smartlist_insert` both invoke
`smartlist_ensure_capacity` prior adding an element to the list in order
to ensure that sufficient memory is available, to `exit()` if not enough
memory is available and to detect requests for an invalid size:
{{{
static INLINE void
smartlist_ensure_capacity(smartlist_t *sl, int size)
{
#if SIZEOF_SIZE_T > SIZEOF_INT
#define MAX_CAPACITY (INT_MAX)
#else
#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*))))
#define ASSERT_CAPACITY
#endif
if (size > sl->capacity) {
int higher = sl->capacity;
if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) {
#ifdef ASSERT_CAPACITY
/* We don't include this assertion when MAX_CAPACITY == INT_MAX,
* since int size; (size <= INT_MAX) makes analysis tools think
we're
* doing something stupid. */
tor_assert(size <= MAX_CAPACITY);
#endif
higher = MAX_CAPACITY;
} else {
while (size > higher)
higher *= 2;
}
sl->capacity = higher;
sl->list = tor_reallocarray(sl->list, sizeof(void*),
((size_t)sl->capacity));
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
}
}}}
On a typical 64-bit system, `SIZEOF_INT` is 4 and `SIZEOF_SIZE_T` is 8.
Consequently, `MAX_CAPACITY` is `INT_MAX`, which is 0x7FFFFFFF as can be
seen in torint.h:
{{{
#ifndef INT_MAX
#if (SIZEOF_INT == 4)
#define INT_MAX 0x7fffffffL
#elif (SIZEOF_INT == 8)
#define INT_MAX 0x7fffffffffffffffL
#else
#error "Can't define INT_MAX"
#endif
#endif
}}}
So `MAX_CAPACITY` is 0x7FFFFFFF. Now assume that that many (0x7FFFFFFF)
items have already been added to a smartlist via smartlist_add(sl, value).
smartlist_add() is:
{{{
void
smartlist_add(smartlist_t *sl, void *element)
{
smartlist_ensure_capacity(sl, sl->num_used+1);
sl->list[sl->num_used++] = element;
}
}}}
If `sl->num_used` is 0x7FFFFFFF prior to invoking `smartlist_add`, then
the next `smartlist_add` is effectively:
{{{
void
smartlist_add(smartlist_t *sl, void *element)
{
smartlist_ensure_capacity(sl, -2147483648);
sl->list[2147483647] = element;
sl->num_used = -2147483648
}
}}}
This is the case since we are dealing with a signed 32 bit integer, and
2147483647 + 1 equals -2147483647.
All of the code in `smartlist_ensure_capacity` is wrapped inside the
following `if` block:
{{{
if (size > sl->capacity) {
}
}}}
The expression -2147483648 > 2147483647 equals false, thus the code inside
the block is not executed.
What actually causes the segmentation fault is that a negative 32 bit
integer is used to compute a the location of array index on a 64 bit
memory layout, ie., the next call to smartlist_add is effectively:
{{{
void
smartlist_add(smartlist_t *sl, void *element)
{
smartlist_ensure_capacity(sl, -2147483647); // Note that this is
effective do-nothing code, as explained above
sl->list[-2147483648] = element;
sl->num_used = -2147483647
}
}}}
== Discussion ==
The requirement for 16 gigabytes of memory is considerable.
Triggering the vulnerability obviously also requires some code path which
will invoke `smartlist_add` or `smartlist_insert` upon the same smartlist
at the attacker's behest. Moreover, such a code path may have the side
effect that it requires a separate allocation for each object that is
added to the list; `smartlist_add` takes a pointer argument after all --
usually, but not always, this pointer refers to freshly allocated memory.
Exceptions to this rule are static strings and pointers to a place in a
large string or buffer that was already extant.
Once a vulnerable code path has been discovered, then it ultimately boils
down to how much memory a user's machine is able to allocate in order to
corrupt the heap.
Despite these constraints, smartlists form a considerable portion of the
infrastructure of your code (I count some 380+ occurrences of
`smartlist_add`/`smartlist_insert` in the .c files using grep, that is
excluding the test/ directory) and as such it's probably wise to revise
the checks in `smartlist_ensure_capacity`.
--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/18162>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online
More information about the tor-bugs
mailing list