Skip to content

Guard L2 reordering against the maximum implicit level (126)#149

Open
gaoflow wants to merge 1 commit into
servo:mainfrom
gaoflow:fix-l2-overflow-max-implicit-level
Open

Guard L2 reordering against the maximum implicit level (126)#149
gaoflow wants to merge 1 commit into
servo:mainfrom
gaoflow:fix-l2-overflow-max-implicit-level

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 19, 2026

Copy link
Copy Markdown

BidiInfo::visual_runs panics with Level error: OutOfRangeNumber when a line
sits entirely at the maximum implicit level 126. In the L2 step,
min_level.new_lowest_ge_rtl() evaluates Level::new(126 | 1) = Level::new(127),
which is past the ceiling, so the following .expect(...) panics.

Level 126 is reachable by ordinary text: nest isolates/embeddings to the explicit
depth 125, then a digit inside is raised to implicit level 126 by rule I2 (as noted
in the issue, implicit resolution tops out at 126). Such a line is uniformly 126,
all even, so nothing needs reordering.

reorder_visual already guards this with if min == max && min.is_ltr(). The
other two L2 implementations were missing it. This adds the same guard to
visual_runs_for_line (which backs visual_runs for both utf-8 and utf-16) and
to the deprecated visual_runs. For a uniform even level the guard matches what
the existing loop already produces, so behaviour is otherwise unchanged and the
conformance suite still passes.

Repro:

let mut s = String::new();
for i in 0..140 { s.push(if i % 2 == 0 { '\u{2067}' } else { '\u{2066}' }); }
let d = s.len(); s.push('9');
let bidi = BidiInfo::new(&s, Some(Level::ltr())); // level at d == 126
bidi.visual_runs(&bidi.paragraphs[0], d..d + 1);  // panics before this change

Fixes #145.

visual_runs_for_line and the deprecated visual_runs panic with
"Level error: OutOfRangeNumber" when a line sits entirely at the maximum
implicit level 126: new_lowest_ge_rtl() computes 126 | 1 == 127, which
exceeds the ceiling. That level is reachable by ordinary text (nesting
isolates to explicit depth 125, then a digit raised to implicit 126 by
rule I2), so BidiInfo::visual_runs crashes on valid input for both utf-8
and utf-16.

Mirror the guard already in reorder_visual: a uniform even (LTR) level
needs no reordering, which also short-circuits before the overflow.

Fixes servo#145.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic because of level overflow

1 participant