From 27c1c4271a14cc2ebc27227212c19d4227ef212d Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 23 Dec 2019 17:58:09 +1030 Subject: [PATCH] ubsan: wasm: shift is too large for 64-bit type 'bfd_vma' bfd/ * wasm-module.c (wasm_read_leb128): Don't allow oversize shifts. Catch value overflow. Sign extend only on terminating byte. opcodes/ * wasm32-dis.c (wasm_read_leb128): Don't allow oversize shifts. Catch value overflow. Sign extend only on terminating byte. --- bfd/ChangeLog | 5 +++++ bfd/wasm-module.c | 23 +++++++++++++++-------- opcodes/ChangeLog | 5 +++++ opcodes/wasm32-dis.c | 29 ++++++++++++++++++----------- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 40f4b663642..8a2120ec2a2 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,8 @@ +2019-12-23 Alan Modra + + * wasm-module.c (wasm_read_leb128): Don't allow oversize shifts. + Catch value overflow. Sign extend only on terminating byte. + 2019-12-20 Alan Modra * xtensa-isa.c (xtensa_insnbuf_from_chars): Avoid signed overflow. diff --git a/bfd/wasm-module.c b/bfd/wasm-module.c index a827d108ede..3fa2a87fb47 100644 --- a/bfd/wasm-module.c +++ b/bfd/wasm-module.c @@ -111,18 +111,28 @@ wasm_read_leb128 (bfd * abfd, unsigned int num_read = 0; unsigned int shift = 0; unsigned char byte = 0; - bfd_boolean success = FALSE; + int status = 1; while (bfd_bread (&byte, 1, abfd) == 1) { num_read++; - result |= ((bfd_vma) (byte & 0x7f)) << shift; + if (shift < sizeof (result) * 8) + { + result |= ((bfd_vma) (byte & 0x7f)) << shift; + if ((result >> shift) != (byte & 0x7f)) + /* Overflow. */ + status |= 2; + shift += 7; + } + else if ((byte & 0x7f) != 0) + status |= 2; - shift += 7; if ((byte & 0x80) == 0) { - success = TRUE; + status &= ~1; + if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((bfd_vma) 1 << shift); break; } } @@ -130,10 +140,7 @@ wasm_read_leb128 (bfd * abfd, if (length_return != NULL) *length_return = num_read; if (error_return != NULL) - *error_return = ! success; - - if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40)) - result |= -((bfd_vma) 1 << shift); + *error_return = status != 0; return result; } diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index d6d73111320..9315ddef7ad 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,8 @@ +2019-12-23 Alan Modra + + * wasm32-dis.c (wasm_read_leb128): Don't allow oversize shifts. + Catch value overflow. Sign extend only on terminating byte. + 2019-12-20 Alan Modra PR 25281 diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c index b1042793a5e..a885148c8bd 100644 --- a/opcodes/wasm32-dis.c +++ b/opcodes/wasm32-dis.c @@ -192,29 +192,36 @@ wasm_read_leb128 (bfd_vma pc, unsigned int num_read = 0; unsigned int shift = 0; unsigned char byte = 0; - bfd_boolean success = FALSE; + int status = 1; while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0) { num_read++; - result |= ((bfd_vma) (byte & 0x7f)) << shift; + if (shift < sizeof (result) * 8) + { + result |= ((uint64_t) (byte & 0x7f)) << shift; + if ((result >> shift) != (byte & 0x7f)) + /* Overflow. */ + status |= 2; + shift += 7; + } + else if ((byte & 0x7f) != 0) + status |= 2; - shift += 7; if ((byte & 0x80) == 0) - { - success = TRUE; - break; - } + { + status &= ~1; + if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((uint64_t) 1 << shift); + break; + } } if (length_return != NULL) *length_return = num_read; if (error_return != NULL) - *error_return = ! success; - - if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40)) - result |= -((uint64_t) 1 << shift); + *error_return = status != 0; return result; } -- 2.30.2