From: Aleksei Vetrov <vvv...@google.com> __libdw_get_uleb128 and __libdw_get_sleb128 should check if addrp has already reached the end before unrolling the first step. It is done by moving __libdw_max_len to the beginning of the function, which can notice, that addrp is beyond the end. Then we just check the result of this function.
Signed-off-by: Aleksei Vetrov <vvv...@google.com> --- libdw/memory-access.h | 10 ++++++++-- tests/leb128.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/libdw/memory-access.h b/libdw/memory-access.h index fca4129a..6d79343c 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -72,13 +72,16 @@ __libdw_max_len_sleb128 (const unsigned char *addr, const unsigned char *end) static inline uint64_t __libdw_get_uleb128 (const unsigned char **addrp, const unsigned char *end) { + const size_t max = __libdw_max_len_uleb128 (*addrp, end); + if (unlikely (max == 0)) + return UINT64_MAX; + uint64_t acc = 0; /* Unroll the first step to help the compiler optimize for the common single-byte case. */ get_uleb128_step (acc, *addrp, 0); - const size_t max = __libdw_max_len_uleb128 (*addrp - 1, end); for (size_t i = 1; i < max; ++i) get_uleb128_step (acc, *addrp, i); /* Other implementations set VALUE to UINT_MAX in this @@ -124,6 +127,10 @@ __libdw_get_uleb128_unchecked (const unsigned char **addrp) static inline int64_t __libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end) { + const size_t max = __libdw_max_len_sleb128 (*addrp, end); + if (unlikely (max == 0)) + return INT64_MAX; + /* Do the work in an unsigned type, but use implementation-defined behavior to cast to signed on return. This avoids some undefined behavior when shifting. */ @@ -133,7 +140,6 @@ __libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end) for the common single-byte case. */ get_sleb128_step (acc, *addrp, 0); - const size_t max = __libdw_max_len_sleb128 (*addrp - 1, end); for (size_t i = 1; i < max; ++i) get_sleb128_step (acc, *addrp, i); if (*addrp == end) diff --git a/tests/leb128.c b/tests/leb128.c index 47b57c0d..03090d80 100644 --- a/tests/leb128.c +++ b/tests/leb128.c @@ -117,6 +117,19 @@ test_sleb (void) return OK; } +static int +test_sleb_safety (void) +{ + const int64_t expected_error = INT64_MAX; + int64_t value; + const unsigned char *test = NULL; + get_sleb128 (value, test, test); + if (value != expected_error) + return FAIL; + + return OK; +} + static int test_one_uleb (const unsigned char *data, size_t len, uint64_t expect) { @@ -166,8 +179,22 @@ test_uleb (void) return OK; } +static int +test_uleb_safety (void) +{ + const uint64_t expected_error = UINT64_MAX; + uint64_t value; + const unsigned char *test = NULL; + get_uleb128 (value, test, test); + if (value != expected_error) + return FAIL; + + return OK; +} + int main (void) { - return test_sleb () || test_uleb (); + return test_sleb () || test_sleb_safety () || test_uleb () + || test_uleb_safety (); } -- 2.39.1.581.gbfd45094c4-goog