You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
lexer_construct_number_object converts a JS numeric literal to an ecma_number and, in expression context, executes int32_t int_num = (int32_t) num; to attempt folding into a CBC_PUSH_NUMBER_BYTE_RANGE immediate. When the literal exceeds the int32 range (e.g., 1e308, or values above 2**53), the double-to-int32 cast is undefined behavior; UBSan trap mode executes __builtin_trap() and raises SIGILL. The trigger is plain, valid JavaScript — no malformed input is required.
Affected: JerryScript 3.0.0 (jerry-core/parser/js/js-lexer.c:2539 in lexer_construct_number_object)
Severity: Medium
CWE: CWE-681 (Incorrect Conversion between Numeric Types)
POC (plain JS, no malformed input)
// poc.js — valid JavaScript with oversized numeric literalsvara=0xFF^0x0F;varb=1<<20;varc=3.14159;vard=-1.5e10;a+=b;varr=a*c+d;r;
ERROR: UBSan: runtime error: value ... is outside the range of representable values of type 'int' (SIGILL)
#0 ... lexer_construct_number_object js-lexer.c:2539
Replay exit code: 132 (SIGILL)
Deterministic: yes — also triggered by the snapshot-deserialization number path (crash_seed_seed_refine_143_eval_numbers.bin).
Impact
Any application that submits JS source to jerry_parse/jerry_eval containing an oversized numeric literal SIGILL-crashes under a UBSan-trap build (the OSS-Fuzz official configuration) — stable DoS. In a regular build, the hardware cast yields 0x80000000, and the int_num == num check usually makes the fold not apply, so the semantic impact is limited; but the UBSan-trap build (as configured by OSS-Fuzz) is a stable DoS. This particularly affects web/edge runtimes that take untrusted JS as input.
Suggested fix
In lexer_construct_number_object (js-lexer.c:2539), only perform the (int32_t) fold when num is exactly representable as an int32_t (e.g., check num >= -2147483648.0 && num <= 2147483647.0 && (int32_t)num == num), and skip the immediate-fold path otherwise — avoiding the UB cast on oversized literals.
Opened #5308 with a fix: move the int32_t range test in front of the cast in lexer_construct_number_object(), so literals outside the range take the float
path instead of triggering the UB cast.
Re-verified on current master (b706935) with the OSS-Fuzz harness
(jerry_parse + jerry_run); the input is plain JavaScript:
var a = 1e308;
js-lexer.c:2539:25: runtime error: 1e+308 is outside the range of representable values of type 'int'
Activity
[-]lexer_construct_number_object casts oversized numeric literal to int32 -> UBSan float-cast-overflow (SIGILL) (CWE-681)[/-][+][Bug]lexer_construct_number_object casts oversized numeric literal to int32 -> UBSan float-cast-overflow (SIGILL) (CWE-681)[/+]1820893135-pixel commentedon Sep 23, 2026
Opened #5308 with a fix: move the
int32_trange test in front of the cast inlexer_construct_number_object(), so literals outside the range take the floatpath instead of triggering the UB cast.
Re-verified on current
master(b706935) with the OSS-Fuzz harness(
jerry_parse+jerry_run); the input is plain JavaScript:The PR adds
tests/jerry/number-literal-range.js.