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.
This commit is contained in:
Alan Modra 2020-01-13 14:27:19 +10:30
parent df08b5881b
commit febda64f15
4 changed files with 237 additions and 199 deletions

View File

@ -1,3 +1,7 @@
2020-01-13 Alan Modra <amodra@gmail.com>
* testsuite/gas/wasm32/allinsn.d: Update expected output.
2020-01-13 Alan Modra <amodra@gmail.com> 2020-01-13 Alan Modra <amodra@gmail.com>
* config/tc-tic4x.c (tic4x_operands_match): Correct tic3x trap * config/tc-tic4x.c (tic4x_operands_match): Correct tic3x trap

View File

@ -20,7 +20,7 @@ Disassembly of section .text:
12: 8b f32.abs 12: 8b f32.abs
13: 92 f32.add 13: 92 f32.add
14: 8d f32.ceil 14: 8d f32.ceil
15: 43 d0 0f 49 f32.const 3.141590118408203125 15: 43 d0 0f 49 f32.const 3.14159012
19: 40 19: 40
1a: b2 f32.convert_s/i32 1a: b2 f32.convert_s/i32
1b: b4 f32.convert_s/i64 1b: b4 f32.convert_s/i64
@ -50,7 +50,7 @@ Disassembly of section .text:
37: 99 f64.abs 37: 99 f64.abs
38: a0 f64.add 38: a0 f64.add
39: 9b f64.ceil 39: 9b f64.ceil
3a: 44 97 5f 4f f64.const 3.14158999999999976088e\+200 3a: 44 97 5f 4f f64.const 3.1415899999999998e\+200
3e: fd bc 6a 90 3e: fd bc 6a 90
42: 69 42: 69
43: b7 f64.convert_s/i32 43: b7 f64.convert_s/i32

View File

@ -1,3 +1,10 @@
2020-01-13 Alan Modra <amodra@gmail.com>
* 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.
2020-01-13 Alan Modra <amodra@gmail.com> 2020-01-13 Alan Modra <amodra@gmail.com>
* score-dis.c: Formatting. * score-dis.c: Formatting.

View File

@ -271,33 +271,10 @@ print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
void *stream = info->stream; void *stream = info->stream;
fprintf_ftype prin = info->fprintf_func; fprintf_ftype prin = info->fprintf_func;
struct wasm32_private_data *private_data = info->private_data; struct wasm32_private_data *private_data = info->private_data;
long long constant = 0; uint64_t val;
double fconstant = 0.0; int len;
long flags = 0; unsigned int bytes_read;
long offset = 0; bfd_boolean error;
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;
if (info->read_memory_func (pc, buffer, 1, info)) if (info->read_memory_func (pc, buffer, 1, info))
return -1; 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]); prin (stream, "\t.byte 0x%02x\n", buffer[0]);
return 1; return 1;
} }
else
len = 1;
prin (stream, "\t");
prin (stream, "%s", op->name);
if (op->clas == wasm_typed)
{ {
len = 1; 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;
}
}
prin (stream, "\t"); switch (op->clas)
prin (stream, "%s", op->name); {
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;
if (op->clas == wasm_typed) case wasm_break_table:
{ {
block_type = wasm_read_leb128 uint32_t target_count, i;
(pc + len, info, &error, &bytes_read, FALSE); val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
if (error) FALSE);
return -1; target_count = val;
len += bytes_read; if (error || target_count != val || target_count == (uint32_t) -1)
switch (block_type) return -1;
{ len += bytes_read;
case BLOCK_TYPE_NONE: prin (stream, " %u", target_count);
prin (stream, "[]"); for (i = 0; i < target_count + 1; i++)
break; {
case BLOCK_TYPE_I32: uint32_t target;
prin (stream, "[i]"); val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
break; FALSE);
case BLOCK_TYPE_I64: target = val;
prin (stream, "[l]"); if (error || target != val)
break; return -1;
case BLOCK_TYPE_F32: len += bytes_read;
prin (stream, "[f]"); prin (stream, " %u", target);
break; }
case BLOCK_TYPE_F64: }
prin (stream, "[d]"); break;
break;
}
}
switch (op->clas) case wasm_break:
{ case wasm_break_if:
case wasm_special: {
case wasm_eqz: uint32_t depth;
case wasm_binary: val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
case wasm_unary: FALSE);
case wasm_conv: depth = val;
case wasm_relational: if (error || depth != val)
case wasm_drop: return -1;
case wasm_signature: len += bytes_read;
case wasm_call_import: prin (stream, " %u", depth);
case wasm_typed: }
case wasm_select: break;
break;
case wasm_break_table: case wasm_return:
target_count = wasm_read_leb128 break;
(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_constant_i32:
case wasm_break_if: case wasm_constant_i64:
depth = wasm_read_leb128 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, TRUE);
(pc + len, info, &error, &bytes_read, FALSE); if (error)
if (error) return -1;
return -1; len += bytes_read;
len += bytes_read; prin (stream, " %" PRId64, val);
prin (stream, " %ld", depth); break;
break;
case wasm_return: case wasm_constant_f32:
break; {
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_i32: case wasm_constant_f64:
case wasm_constant_i64: {
constant = wasm_read_leb128 double fconstant;
(pc + len, info, &error, &bytes_read, TRUE); int ret;
if (error) ret = read_f64 (&fconstant, pc + len, info);
return -1; if (ret < 0)
len += bytes_read; return -1;
prin (stream, " %lld", constant); len += ret;
break; prin (stream, " %.17g", fconstant);
}
break;
case wasm_constant_f32: case wasm_call:
/* This appears to be the best we can do, even though we're {
using host doubles for WebAssembly floats. */ uint32_t function_index;
ret = read_f32 (&fconstant, pc + len, info); val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
if (ret < 0) FALSE);
return -1; function_index = val;
len += ret; if (error || function_index != val)
prin (stream, " %.9g", fconstant); return -1;
break; 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_constant_f64: case wasm_call_indirect:
ret = read_f64 (&fconstant, pc + len, info); {
if (ret < 0) uint32_t type_index, xtra_index;
return -1; val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
len += ret; FALSE);
prin (stream, " %.17g", fconstant); type_index = val;
break; 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_call: case wasm_get_local:
function_index = wasm_read_leb128 case wasm_set_local:
(pc + len, info, &error, &bytes_read, FALSE); case wasm_tee_local:
if (error) {
return -1; uint32_t local_index;
len += bytes_read; val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
prin (stream, " "); FALSE);
private_data->section_prefix = ".space.function_index"; local_index = val;
(*info->print_address_func) ((bfd_vma) function_index, info); if (error || local_index != val)
private_data->section_prefix = NULL; return -1;
break; 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_call_indirect: case wasm_grow_memory:
constant = wasm_read_leb128 case wasm_current_memory:
(pc + len, info, &error, &bytes_read, FALSE); {
if (error) uint32_t reserved_size;
return -1; val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
len += bytes_read; FALSE);
prin (stream, " %lld", constant); reserved_size = val;
constant = wasm_read_leb128 if (error || reserved_size != val)
(pc + len, info, &error, &bytes_read, FALSE); return -1;
if (error) len += bytes_read;
return -1; prin (stream, " %u", reserved_size);
len += bytes_read; }
prin (stream, " %lld", constant); break;
break;
case wasm_get_local: case wasm_load:
case wasm_set_local: case wasm_store:
case wasm_tee_local: {
constant = wasm_read_leb128 uint32_t flags, offset;
(pc + len, info, &error, &bytes_read, FALSE); val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
if (error) FALSE);
return -1; flags = val;
len += bytes_read; if (error || flags != val)
prin (stream, " %lld", constant); return -1;
if (strcmp (op->name + 4, "local") == 0) len += bytes_read;
{ val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
if (private_data->print_registers FALSE);
&& constant >= 0 && constant < nlocals) offset = val;
prin (stream, " <%s>", locals[constant]); if (error || offset != val)
} return -1;
else len += bytes_read;
{ prin (stream, " a=%u %u", flags, offset);
if (private_data->print_well_known_globals }
&& constant >= 0 && constant < nglobals) break;
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);
}
} }
return len; return len;
} }