From 12dc2c2079b5c9a23571f6dc01b258e381322f12 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Fri, 6 Aug 2021 20:42:36 +0200 Subject: [PATCH] LibJS: Fix wraparound UB in `Value::to_u{8,16}` If we call these two functions on a negative value, undefined behavior occurs due to casting a negative double to an unsigned integer. These functions are defined to perform modular arithmetic, so negative values can be fixed up by adding 2^8/2^16. The reason why this step is not mentioned in ECMA-262 is that it defines modular arithmetic so that `x mod m` had the same sign as `m`, while LibM's `fmod(x, m)` copies `x`'s sign. This issue was found by UBSAN with the Clang toolchain. --- Userland/Libraries/LibJS/Runtime/Value.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index 76c6d4cc41..f2336f51cb 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -684,6 +684,8 @@ u16 Value::to_u16(GlobalObject& global_object) const if (signbit(value)) int_val = -int_val; auto int16bit = fmod(int_val, NumericLimits::max() + 1.0); + if (int16bit < 0) + int16bit += NumericLimits::max() + 1.0; return static_cast(int16bit); } @@ -720,6 +722,8 @@ u8 Value::to_u8(GlobalObject& global_object) const if (signbit(value)) int_val = -int_val; auto int8bit = fmod(int_val, NumericLimits::max() + 1.0); + if (int8bit < 0) + int8bit += NumericLimits::max() + 1.0; return static_cast(int8bit); }