From bf1b572bee63d720ba35f483231bd0e9571e1833 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Sun, 25 Jan 2026 20:41:45 +0000 Subject: [PATCH] Fix arithmetic UB in dot product --- scripts/test/shared.py | 3 --- src/wasm/literal.cpp | 9 +++++---- test/lit/exec/simd.wast | 13 +++++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/scripts/test/shared.py b/scripts/test/shared.py index e36a3d726c6..714a855be4a 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -462,9 +462,6 @@ def get_tests(test_dir, extensions=[], recursive=False): 'simd_f64x2.wast', # Min of 0 and NaN should give a canonical NaN 'simd_f64x2_arith.wast', # Adding inf and -inf should give a canonical NaN 'simd_f64x2_rounding.wast', # Ceil of NaN should give a canonical NaN - 'simd_i32x4_cmp.wast', # UBSan error on integer overflow - 'simd_i32x4_arith2.wast', # UBSan error on integer overflow - 'simd_i32x4_dot_i16x8.wast', # UBSan error on integer overflow 'token.wast', # Lexer should require spaces between strings and non-paren tokens ] diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 8e68cd796c8..3e2dcab19b0 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -1161,9 +1161,9 @@ Literal Literal::demote() const { Literal Literal::add(const Literal& other) const { switch (type.getBasic()) { case Type::i32: - return Literal(uint32_t(i32) + uint32_t(other.i32)); + return Literal(bit_cast(i32) + bit_cast(other.i32)); case Type::i64: - return Literal(uint64_t(i64) + uint64_t(other.i64)); + return Literal(bit_cast(i64) + bit_cast(other.i64)); case Type::f32: return standardizeNaN(Literal(getf32() + other.getf32())); case Type::f64: @@ -1408,6 +1408,8 @@ Literal Literal::maxUInt(const Literal& other) const { } Literal Literal::avgrUInt(const Literal& other) const { + // This looks like it could overflow, but these are promoted from uint8 in the + // caller (`binary`). return Literal((geti32() + other.geti32() + 1) / 2); } @@ -2652,8 +2654,7 @@ static Literal dot(const Literal& left, const Literal& right) { for (size_t i = 0; i < Lanes; ++i) { result[i] = Literal(int32_t(0)); for (size_t j = 0; j < Factor; ++j) { - result[i] = Literal(result[i].geti32() + lhs[i * Factor + j].geti32() * - rhs[i * Factor + j].geti32()); + result[i] = result[i].add(lhs[i * Factor + j].mul(rhs[i * Factor + j])); } } return Literal(result); diff --git a/test/lit/exec/simd.wast b/test/lit/exec/simd.wast index a34a6827688..8dc453feacf 100644 --- a/test/lit/exec/simd.wast +++ b/test/lit/exec/simd.wast @@ -23,6 +23,15 @@ (i64.const -4611686018427387904) ) ) + + ;; CHECK: [fuzz-exec] calling i8x16.avgr_u + ;; CHECK-NEXT: [fuzz-exec] note result: i8x16.avgr_u => i32x4 0x80808080 0x80808080 0x80808080 0x80808080 + (func $i8x16.avgr_u (export "i8x16.avgr_u") (result v128) + (i8x16.avgr_u + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + (v128.const i8x16 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128 -128) + ) + ) ) ;; CHECK: [fuzz-exec] calling load8x8_s @@ -30,5 +39,9 @@ ;; CHECK: [fuzz-exec] calling load32x2_u ;; CHECK-NEXT: [trap final > memory: 13835058055282163712 > 1048576] + +;; CHECK: [fuzz-exec] calling i8x16.avgr_u +;; CHECK-NEXT: [fuzz-exec] note result: i8x16.avgr_u => i32x4 0x80808080 0x80808080 0x80808080 0x80808080 +;; CHECK-NEXT: [fuzz-exec] comparing i8x16.avgr_u ;; CHECK-NEXT: [fuzz-exec] comparing load32x2_u ;; CHECK-NEXT: [fuzz-exec] comparing load8x8_s