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 already has all the checks.
Signed-off-by: Aleksei Vetrov <vvv...@google.com> --- libdw/memory-access.h | 10 ++++++---- tests/leb128.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libdw/memory-access.h b/libdw/memory-access.h index fca4129a..1cac6af3 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -72,13 +72,14 @@ __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); 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); + if (likely (max > 0)) + 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 +125,7 @@ __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); /* 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. */ @@ -131,9 +133,9 @@ __libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end) /* Unroll the first step to help the compiler optimize for the common single-byte case. */ - get_sleb128_step (acc, *addrp, 0); + if (likely (max > 0)) + 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.405.gd4c25cc71f-goog