Skip to content

Commit af46d19

Browse files
Fix UB in SIMD dot product arithmetic (#8234)
* Use Literal::add and Literal::mul to avoid overflow of signed arithmetic. These do the right thing by casting to unsigned first. * Use bit_cast instead of a C-style cast to convey the intent better when converting between signed and unsigned. * Also add a unit test for i8x16.avgr_u to demonstrate that it doesn't overflow. From reading the code, most other arithmetic in Literal.cpp shouldn't invoke UB. There are some spec tests that are disabled due to float/nan behavior that I'll look at in future PRs.
1 parent d9e6ed3 commit af46d19

File tree

3 files changed

+18
-7
lines changed

3 files changed

+18
-7
lines changed

scripts/test/shared.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,6 @@ def get_tests(test_dir, extensions=[], recursive=False):
462462
'simd_f64x2.wast', # Min of 0 and NaN should give a canonical NaN
463463
'simd_f64x2_arith.wast', # Adding inf and -inf should give a canonical NaN
464464
'simd_f64x2_rounding.wast', # Ceil of NaN should give a canonical NaN
465-
'simd_i32x4_cmp.wast', # UBSan error on integer overflow
466-
'simd_i32x4_arith2.wast', # UBSan error on integer overflow
467-
'simd_i32x4_dot_i16x8.wast', # UBSan error on integer overflow
468465
'token.wast', # Lexer should require spaces between strings and non-paren tokens
469466
]
470467

src/wasm/literal.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,9 +1161,9 @@ Literal Literal::demote() const {
11611161
Literal Literal::add(const Literal& other) const {
11621162
switch (type.getBasic()) {
11631163
case Type::i32:
1164-
return Literal(uint32_t(i32) + uint32_t(other.i32));
1164+
return Literal(bit_cast<uint32_t>(i32) + bit_cast<uint32_t>(other.i32));
11651165
case Type::i64:
1166-
return Literal(uint64_t(i64) + uint64_t(other.i64));
1166+
return Literal(bit_cast<uint64_t>(i64) + bit_cast<uint64_t>(other.i64));
11671167
case Type::f32:
11681168
return standardizeNaN(Literal(getf32() + other.getf32()));
11691169
case Type::f64:
@@ -1408,6 +1408,8 @@ Literal Literal::maxUInt(const Literal& other) const {
14081408
}
14091409

14101410
Literal Literal::avgrUInt(const Literal& other) const {
1411+
// This looks like it could overflow, but these are promoted from uint8 in the
1412+
// caller (`binary`).
14111413
return Literal((geti32() + other.geti32() + 1) / 2);
14121414
}
14131415

@@ -2652,8 +2654,7 @@ static Literal dot(const Literal& left, const Literal& right) {
26522654
for (size_t i = 0; i < Lanes; ++i) {
26532655
result[i] = Literal(int32_t(0));
26542656
for (size_t j = 0; j < Factor; ++j) {
2655-
result[i] = Literal(result[i].geti32() + lhs[i * Factor + j].geti32() *
2656-
rhs[i * Factor + j].geti32());
2657+
result[i] = result[i].add(lhs[i * Factor + j].mul(rhs[i * Factor + j]));
26572658
}
26582659
}
26592660
return Literal(result);

test/lit/exec/simd.wast

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,25 @@
2323
(i64.const -4611686018427387904)
2424
)
2525
)
26+
27+
;; CHECK: [fuzz-exec] calling i8x16.avgr_u
28+
;; CHECK-NEXT: [fuzz-exec] note result: i8x16.avgr_u => i32x4 0x80808080 0x80808080 0x80808080 0x80808080
29+
(func $i8x16.avgr_u (export "i8x16.avgr_u") (result v128)
30+
(i8x16.avgr_u
31+
(v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)
32+
(v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128)
33+
)
34+
)
2635
)
2736

2837
;; CHECK: [fuzz-exec] calling load8x8_s
2938
;; CHECK-NEXT: [fuzz-exec] note result: load8x8_s => i32x4 0x00620061 0x00640063 0x00660065 0x00000067
3039

3140
;; CHECK: [fuzz-exec] calling load32x2_u
3241
;; CHECK-NEXT: [trap final > memory: 13835058055282163712 > 1048576]
42+
43+
;; CHECK: [fuzz-exec] calling i8x16.avgr_u
44+
;; CHECK-NEXT: [fuzz-exec] note result: i8x16.avgr_u => i32x4 0x80808080 0x80808080 0x80808080 0x80808080
45+
;; CHECK-NEXT: [fuzz-exec] comparing i8x16.avgr_u
3346
;; CHECK-NEXT: [fuzz-exec] comparing load32x2_u
3447
;; CHECK-NEXT: [fuzz-exec] comparing load8x8_s

0 commit comments

Comments
 (0)