Skip to content

lexer_construct_number_object casts oversized numeric literal to int32 -> UBSan float-cast-overflow (SIGILL) (CWE-681) #5304

Description

@1820893135-pixel

Summary

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 literals
var a = 0xFF ^ 0x0F; var b = 1 << 20; var c = 3.14159; var d = -1.5e10; a += b; var r = a * c + d; r;
# 1) build (ASan+UBSan with trap)
cmake -S . -B build -DENABLE_LTO=OFF -DJERRY_SNAPSHOT_EXEC=ON -DJERRY_SNAPSHOT_SAVE=ON \
      -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang \
      -DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -O1 -g"
cmake --build build --target jerry-core jerry-port -j$(nproc)
clang -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=all -O1 -g -DJERRY_SNAPSHOT_EXEC=1 \
      -I jerry-core/include harness.c build/lib/libjerry-core.a build/lib/libjerry-port.a -lm -o jerry_fuzzer

# 2) reproduce
./jerry_fuzzer poc.js

Trigger result

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).
Image

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions