Description
format64_to_fixed panics in debug builds (and has UB in release builds) when rounding causes a carry that propagates through all integer digits (e.g., 9.5 with toFixed(0) should produce "10").
Reproducer
Using Boa (which calls ryu_js::Buffer::format_to_fixed):
(9.5).toFixed(0) // panics (expected: "10")
(99.5).toFixed(0) // panics (expected: "100")
Panic
assertion failed: (0..self.len).contains(&i)
Location: ryu-js-1.0.2/src/pretty/to_fixed/mod.rs:225
Root Cause
In the rounding loop at the end of format64_to_fixed (line ~621), result.get(round_index) is called before checking round_index == -1:
round_index -= 1;
let c = result.get(round_index); // reads buffer[-1] when round_index is -1
if round_index == -1 || c == b'-' { // check happens AFTER the read
When all integer digits are 9, the carry turns each to 0 and round_index reaches -1. The next result.get(-1) is an out-of-bounds read.
Negative numbers are unaffected because '-' at index 0 stops the loop before round_index can underflow.
Fix
Move the boundary check before the read:
loop {
round_index -= 1;
if round_index == -1 {
result.set(0, b'1');
if dot_index > 0 {
result.set(dot_index, b'0');
result.set(dot_index + 1, b'.');
}
result.append_byte(b'0');
break;
}
let c = result.get(round_index);
if c == b'-' {
result.set(round_index + 1, b'1');
if dot_index > 0 {
result.set(dot_index, b'0');
result.set(dot_index + 1, b'.');
}
result.append_byte(b'0');
break;
}
// ... rest unchanged
}
Description
format64_to_fixedpanics in debug builds (and has UB in release builds) when rounding causes a carry that propagates through all integer digits (e.g.,9.5withtoFixed(0)should produce"10").Reproducer
Using Boa (which calls
ryu_js::Buffer::format_to_fixed):Panic
Root Cause
In the rounding loop at the end of
format64_to_fixed(line ~621),result.get(round_index)is called before checkinground_index == -1:When all integer digits are
9, the carry turns each to0andround_indexreaches-1. The nextresult.get(-1)is an out-of-bounds read.Negative numbers are unaffected because
'-'at index 0 stops the loop beforeround_indexcan underflow.Fix
Move the boundary check before the read: