aboutsummaryrefslogtreecommitdiff
path: root/opcodes/wasm32-dis.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2020-01-13 14:27:19 +1030
committerAlan Modra <amodra@gmail.com>2020-01-13 16:44:27 +1030
commitfebda64f152972d8edf0147fb29f89b02f6a4cf9 (patch)
tree530633bd36c29a3fd0715e8658777f665385aa9a /opcodes/wasm32-dis.c
parentdf08b5881b4972d78f9a2069955dad5b12bc972e (diff)
downloadfsf-binutils-gdb-febda64f152972d8edf0147fb29f89b02f6a4cf9.zip
fsf-binutils-gdb-febda64f152972d8edf0147fb29f89b02f6a4cf9.tar.gz
fsf-binutils-gdb-febda64f152972d8edf0147fb29f89b02f6a4cf9.tar.bz2
ubsan: wasm32: signed integer overflow
The signed integer overflow occurred when adding one to target_count for (i = 0; i < target_count + 1; i++) but that's the least of the worries here. target_count was long and i int, leading to the possibility of a loop that never ended. So to avoid this type of vulnerability, this patch uses what I believe to be the proper types for arguments of various wasm32 opcodes, rather than using "long" which may change in size. gas/ * testsuite/gas/wasm32/allinsn.d: Update expected output. opcodes/ * wasm32-dis.c (print_insn_wasm32): Localise variables. Store result of wasm_read_leb128 in a uint64_t and check that bits are not lost when copying to other locals. Use uint32_t for most locals. Use PRId64 when printing int64_t.
Diffstat (limited to 'opcodes/wasm32-dis.c')
-rw-r--r--opcodes/wasm32-dis.c445
1 files changed, 236 insertions, 209 deletions
diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c
index aea93b8..946ce5f 100644
--- a/opcodes/wasm32-dis.c
+++ b/opcodes/wasm32-dis.c
@@ -271,33 +271,10 @@ print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
void *stream = info->stream;
fprintf_ftype prin = info->fprintf_func;
struct wasm32_private_data *private_data = info->private_data;
- long long constant = 0;
- double fconstant = 0.0;
- long flags = 0;
- long offset = 0;
- long depth = 0;
- long function_index = 0;
- long target_count = 0;
- long block_type = 0;
- int len = 1;
- int ret = 0;
- unsigned int bytes_read = 0;
- int i;
- const char *locals[] =
- {
- "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
- "$rp", "$fp", "$sp",
- "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
- "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
- "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
- };
- int nlocals = ARRAY_SIZE (locals);
- const char *globals[] =
- {
- "$got", "$plt", "$gpo"
- };
- int nglobals = ARRAY_SIZE (globals);
- bfd_boolean error = FALSE;
+ uint64_t val;
+ int len;
+ unsigned int bytes_read;
+ bfd_boolean error;
if (info->read_memory_func (pc, buffer, 1, info))
return -1;
@@ -313,189 +290,239 @@ print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
prin (stream, "\t.byte 0x%02x\n", buffer[0]);
return 1;
}
- else
+
+ len = 1;
+
+ prin (stream, "\t");
+ prin (stream, "%s", op->name);
+
+ if (op->clas == wasm_typed)
{
- len = 1;
-
- prin (stream, "\t");
- prin (stream, "%s", op->name);
-
- if (op->clas == wasm_typed)
- {
- block_type = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- switch (block_type)
- {
- case BLOCK_TYPE_NONE:
- prin (stream, "[]");
- break;
- case BLOCK_TYPE_I32:
- prin (stream, "[i]");
- break;
- case BLOCK_TYPE_I64:
- prin (stream, "[l]");
- break;
- case BLOCK_TYPE_F32:
- prin (stream, "[f]");
- break;
- case BLOCK_TYPE_F64:
- prin (stream, "[d]");
- break;
- }
- }
-
- switch (op->clas)
- {
- case wasm_special:
- case wasm_eqz:
- case wasm_binary:
- case wasm_unary:
- case wasm_conv:
- case wasm_relational:
- case wasm_drop:
- case wasm_signature:
- case wasm_call_import:
- case wasm_typed:
- case wasm_select:
- break;
-
- case wasm_break_table:
- target_count = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", target_count);
- for (i = 0; i < target_count + 1; i++)
- {
- long target = 0;
- target = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", target);
- }
- break;
-
- case wasm_break:
- case wasm_break_if:
- depth = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", depth);
- break;
-
- case wasm_return:
- break;
-
- case wasm_constant_i32:
- case wasm_constant_i64:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, TRUE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
-
- case wasm_constant_f32:
- /* This appears to be the best we can do, even though we're
- using host doubles for WebAssembly floats. */
- ret = read_f32 (&fconstant, pc + len, info);
- if (ret < 0)
- return -1;
- len += ret;
- prin (stream, " %.9g", fconstant);
- break;
-
- case wasm_constant_f64:
- ret = read_f64 (&fconstant, pc + len, info);
- if (ret < 0)
- return -1;
- len += ret;
- prin (stream, " %.17g", fconstant);
- break;
-
- case wasm_call:
- function_index = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " ");
- private_data->section_prefix = ".space.function_index";
- (*info->print_address_func) ((bfd_vma) function_index, info);
- private_data->section_prefix = NULL;
- break;
-
- case wasm_call_indirect:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
-
- case wasm_get_local:
- case wasm_set_local:
- case wasm_tee_local:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- if (strcmp (op->name + 4, "local") == 0)
- {
- if (private_data->print_registers
- && constant >= 0 && constant < nlocals)
- prin (stream, " <%s>", locals[constant]);
- }
- else
- {
- if (private_data->print_well_known_globals
- && constant >= 0 && constant < nglobals)
- prin (stream, " <%s>", globals[constant]);
- }
- break;
-
- case wasm_grow_memory:
- case wasm_current_memory:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
-
- case wasm_load:
- case wasm_store:
- flags = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- offset = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " a=%ld %ld", flags, offset);
- }
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, FALSE);
+ if (error)
+ return -1;
+ len += bytes_read;
+ switch (val)
+ {
+ case BLOCK_TYPE_NONE:
+ prin (stream, "[]");
+ break;
+ case BLOCK_TYPE_I32:
+ prin (stream, "[i]");
+ break;
+ case BLOCK_TYPE_I64:
+ prin (stream, "[l]");
+ break;
+ case BLOCK_TYPE_F32:
+ prin (stream, "[f]");
+ break;
+ case BLOCK_TYPE_F64:
+ prin (stream, "[d]");
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ switch (op->clas)
+ {
+ case wasm_special:
+ case wasm_eqz:
+ case wasm_binary:
+ case wasm_unary:
+ case wasm_conv:
+ case wasm_relational:
+ case wasm_drop:
+ case wasm_signature:
+ case wasm_call_import:
+ case wasm_typed:
+ case wasm_select:
+ break;
+
+ case wasm_break_table:
+ {
+ uint32_t target_count, i;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ target_count = val;
+ if (error || target_count != val || target_count == (uint32_t) -1)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", target_count);
+ for (i = 0; i < target_count + 1; i++)
+ {
+ uint32_t target;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ target = val;
+ if (error || target != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", target);
+ }
+ }
+ break;
+
+ case wasm_break:
+ case wasm_break_if:
+ {
+ uint32_t depth;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ depth = val;
+ if (error || depth != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", depth);
+ }
+ break;
+
+ case wasm_return:
+ break;
+
+ case wasm_constant_i32:
+ case wasm_constant_i64:
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, TRUE);
+ if (error)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %" PRId64, val);
+ break;
+
+ case wasm_constant_f32:
+ {
+ double fconstant;
+ int ret;
+ /* This appears to be the best we can do, even though we're
+ using host doubles for WebAssembly floats. */
+ ret = read_f32 (&fconstant, pc + len, info);
+ if (ret < 0)
+ return -1;
+ len += ret;
+ prin (stream, " %.9g", fconstant);
+ }
+ break;
+
+ case wasm_constant_f64:
+ {
+ double fconstant;
+ int ret;
+ ret = read_f64 (&fconstant, pc + len, info);
+ if (ret < 0)
+ return -1;
+ len += ret;
+ prin (stream, " %.17g", fconstant);
+ }
+ break;
+
+ case wasm_call:
+ {
+ uint32_t function_index;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ function_index = val;
+ if (error || function_index != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " ");
+ private_data->section_prefix = ".space.function_index";
+ (*info->print_address_func) ((bfd_vma) function_index, info);
+ private_data->section_prefix = NULL;
+ }
+ break;
+
+ case wasm_call_indirect:
+ {
+ uint32_t type_index, xtra_index;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ type_index = val;
+ if (error || type_index != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", type_index);
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ xtra_index = val;
+ if (error || xtra_index != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", xtra_index);
+ }
+ break;
+
+ case wasm_get_local:
+ case wasm_set_local:
+ case wasm_tee_local:
+ {
+ uint32_t local_index;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ local_index = val;
+ if (error || local_index != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", local_index);
+ if (strcmp (op->name + 4, "local") == 0)
+ {
+ static const char *locals[] =
+ {
+ "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
+ "$rp", "$fp", "$sp",
+ "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
+ "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ };
+ if (private_data->print_registers
+ && local_index < ARRAY_SIZE (locals))
+ prin (stream, " <%s>", locals[local_index]);
+ }
+ else
+ {
+ static const char *globals[] =
+ {
+ "$got", "$plt", "$gpo"
+ };
+ if (private_data->print_well_known_globals
+ && local_index < ARRAY_SIZE (globals))
+ prin (stream, " <%s>", globals[local_index]);
+ }
+ }
+ break;
+
+ case wasm_grow_memory:
+ case wasm_current_memory:
+ {
+ uint32_t reserved_size;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ reserved_size = val;
+ if (error || reserved_size != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " %u", reserved_size);
+ }
+ break;
+
+ case wasm_load:
+ case wasm_store:
+ {
+ uint32_t flags, offset;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ flags = val;
+ if (error || flags != val)
+ return -1;
+ len += bytes_read;
+ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
+ FALSE);
+ offset = val;
+ if (error || offset != val)
+ return -1;
+ len += bytes_read;
+ prin (stream, " a=%u %u", flags, offset);
+ }
+ break;
}
return len;
}