aboutsummaryrefslogtreecommitdiff
path: root/opcodes
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2021-02-16 23:46:40 +1030
committerAlan Modra <amodra@gmail.com>2021-02-17 16:57:59 +1030
commitb9b204b31164188228e585526feb4b4d7d80a114 (patch)
tree454c249a57dfc0a9f98074275cae07bc7c0ae4d7 /opcodes
parent0d6aab77761274e479ca443f8bcb35a0eee3a4c4 (diff)
downloadgdb-b9b204b31164188228e585526feb4b4d7d80a114.zip
gdb-b9b204b31164188228e585526feb4b4d7d80a114.tar.gz
gdb-b9b204b31164188228e585526feb4b4d7d80a114.tar.bz2
read_leb128 overflow checking
There is a tiny error left in dwarf.c:read_leb128 after Nick fixed the signed overflow problem in code I wrote. It's to do with sleb128 values that have unnecessary excess bytes. For example, -1 is represented as 0x7f, the most efficient encoding, but also as 0xff,0x7f or 0xff,0xff,0x7f and so on. None of these sequences overflow any size signed value, but read_leb128 will report an overflow given enough excess bytes. This patch fixes that problem, and since the proper test for signed values with excess bytes can easily be adapted to also test a sleb byte with just some bits that overflow the result, I changed the code to not use signed right shifts. (The C standard ISO/IEC 9899:1999 6.5.7 says signed right shifts of negative values have an implementation defined value. A long time ago I even used a C compiler for a certain microprocessor that always did unsigned right shifts. Mind you, it is very unlikely to be compiling binutils with such a compiler.) bfd/ * wasm-module.c: Guard include of limits.h. (CHAR_BIT): Provide backup define. (wasm_read_leb128): Use CHAR_BIT to size "result" in bits. Correct signed overflow checking. opcodes/ * wasm32-dis.c: Include limits.h. (CHAR_BIT): Provide backup define. (wasm_read_leb128): Use CHAR_BIT to size "result" in bits. Correct signed overflow checking. binutils/ * dwarf.c: Include limits.h. (CHAR_BIT): Provide backup define. (read_leb128): Use CHAR_BIT to size "result" in bits. Correct signed overflow checking. * testsuite/binutils-all/pr26548.s, * testsuite/binutils-all/pr26548.d, * testsuite/binutils-all/pr26548e.d: New tests. * testsuite/binutils-all/readelf.exp: Run them. (readelf_test): Drop unused "xfails" parameter. Update all uses.
Diffstat (limited to 'opcodes')
-rw-r--r--opcodes/ChangeLog7
-rw-r--r--opcodes/wasm32-dis.c26
2 files changed, 27 insertions, 6 deletions
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
index c05438b..03acd4d7 100644
--- a/opcodes/ChangeLog
+++ b/opcodes/ChangeLog
@@ -1,3 +1,10 @@
+2021-02-17 Alan Modra <amodra@gmail.com>
+
+ * wasm32-dis.c: Include limits.h.
+ (CHAR_BIT): Provide backup define.
+ (wasm_read_leb128): Use CHAR_BIT to size "result" in bits.
+ Correct signed overflow checking.
+
2021-02-16 Jan Beulich <jbeulich@suse.com>
* i386-opc.tbl: Split CVTPI2PD template. Add SSE2AVX variant.
diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c
index 727a49e..2fe5132 100644
--- a/opcodes/wasm32-dis.c
+++ b/opcodes/wasm32-dis.c
@@ -29,6 +29,13 @@
#include "elf/wasm32.h"
#include "bfd_stdint.h"
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
/* Type names for blocks and signatures. */
#define BLOCK_TYPE_NONE 0x40
#define BLOCK_TYPE_I32 0x7f
@@ -192,27 +199,34 @@ wasm_read_leb128 (bfd_vma pc,
unsigned int num_read = 0;
unsigned int shift = 0;
unsigned char byte = 0;
+ unsigned char lost, mask;
int status = 1;
while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
{
num_read++;
- if (shift < sizeof (result) * 8)
+ if (shift < CHAR_BIT * sizeof (result))
{
result |= ((uint64_t) (byte & 0x7f)) << shift;
- if ((result >> shift) != (byte & 0x7f))
- /* Overflow. */
- status |= 2;
+ /* These bits overflowed. */
+ lost = byte ^ (result >> shift);
+ /* And this is the mask of possible overflow bits. */
+ mask = 0x7f ^ ((uint64_t) 0x7f << shift >> shift);
shift += 7;
}
- else if ((byte & 0x7f) != 0)
+ else
+ {
+ lost = byte;
+ mask = 0x7f;
+ }
+ if ((lost & mask) != (sign && (int64_t) result < 0 ? mask : 0))
status |= 2;
if ((byte & 0x80) == 0)
{
status &= ~1;
- if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
+ if (sign && shift < CHAR_BIT * sizeof (result) && (byte & 0x40))
result |= -((uint64_t) 1 << shift);
break;
}