Skip to content

Commit

Permalink
[unicode-range] Avoid integer overflow (panic in debug builds)
Browse files Browse the repository at this point in the history
If a long string of hex digits are passed to unicode-range, the code tries to
parse them into a number before checking whether there are more than the 6 digits
allowed by the syntax, and this may lead to integer overflow.

To avoid this, check the number of digits and error out earlier if there are
too many to possibly be valid.

(See https://bugzilla.mozilla.org/show_bug.cgi?id=1900403)
  • Loading branch information
jfkthame authored and emilio committed Nov 7, 2024
1 parent b75ce6a commit 4765111
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 5 deletions.
5 changes: 5 additions & 0 deletions src/css-parsing-tests/urange.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
null,
null,
null
],

"U+26F9200D2640, U+10000-26F9200D2640", [
null,
null
]

]
14 changes: 9 additions & 5 deletions src/unicode_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn parse_concatenated(text: &[u8]) -> Result<UnicodeRange, ()> {
Some((&b'+', text)) => text,
_ => return Err(()),
};
let (first_hex_value, hex_digit_count) = consume_hex(&mut text);
let (first_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?;
let question_marks = consume_question_marks(&mut text);
let consumed = hex_digit_count + question_marks;
if consumed == 0 || consumed > 6 {
Expand All @@ -124,7 +124,7 @@ fn parse_concatenated(text: &[u8]) -> Result<UnicodeRange, ()> {
end: first_hex_value,
});
} else if let Some((&b'-', mut text)) = text.split_first() {
let (second_hex_value, hex_digit_count) = consume_hex(&mut text);
let (second_hex_value, hex_digit_count) = consume_hex(&mut text, 6)?;
if hex_digit_count > 0 && hex_digit_count <= 6 && text.is_empty() {
return Ok(UnicodeRange {
start: first_hex_value,
Expand All @@ -135,19 +135,23 @@ fn parse_concatenated(text: &[u8]) -> Result<UnicodeRange, ()> {
Err(())
}

fn consume_hex(text: &mut &[u8]) -> (u32, usize) {
// Consume hex digits, but return an error if more than digit_limit are found.
fn consume_hex(text: &mut &[u8], digit_limit: usize) -> Result<(u32, usize), ()> {
let mut value = 0;
let mut digits = 0;
while let Some((&byte, rest)) = text.split_first() {
if let Some(digit_value) = (byte as char).to_digit(16) {
if digits == digit_limit {
return Err(());
}
value = value * 0x10 + digit_value;
digits += 1;
*text = rest
*text = rest;
} else {
break;
}
}
(value, digits)
Ok((value, digits))
}

fn consume_question_marks(text: &mut &[u8]) -> usize {
Expand Down

0 comments on commit 4765111

Please sign in to comment.