diff options
author | Aleksei Vetrov <vvvvvv@google.com> | 2023-02-13 20:10:05 +0000 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2023-02-14 16:37:35 +0100 |
commit | e444d60a341b7b9bc3ae763a843d3e7190234ca9 (patch) | |
tree | 83c930311b14ee673593d946375a9f206a07b7ec | |
parent | f2c522567ad63ac293535fba9704895e685ab5bc (diff) | |
download | elfutils-e444d60a341b7b9bc3ae763a843d3e7190234ca9.tar.gz |
libdw: check memory access in get_(u|s)leb128
__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 <vvvvvv@google.com>
-rw-r--r-- | libdw/memory-access.h | 10 | ||||
-rw-r--r-- | 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 @@ -118,6 +118,19 @@ test_sleb (void) } 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) { uint64_t value; @@ -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 (); } |