style: Check that we do not insert more than MAX_GRID_LINE subgrid line name lists while constructing the list

This helps avoid OOM with very large repeats.

Differential Revision: https://phabricator.services.mozilla.com/D161533
This commit is contained in:
Emily McDonough 2022-11-09 20:09:59 +00:00 committed by Martin Robinson
parent e520c3796d
commit 8193fb3985

View file

@ -669,6 +669,11 @@ impl Parse for LineNameList {
input.expect_ident_matching("subgrid")?; input.expect_ident_matching("subgrid")?;
let mut line_names = vec![]; let mut line_names = vec![];
let mut fill_data = None; let mut fill_data = None;
// Rather than truncating the result after inserting values, just
// have a maximum number of values. This gives us an early out on very
// large name lists, but more importantly prevents OOM on huge repeat
// expansions. (bug 1583429)
let mut max_remaining = MAX_GRID_LINE as usize;
loop { loop {
let repeat_parse_result = input.try_parse(|input| { let repeat_parse_result = input.try_parse(|input| {
@ -685,34 +690,40 @@ impl Parse for LineNameList {
}) })
}); });
if let Ok((names_list, count)) = repeat_parse_result { if let Ok((names_list, count)) = repeat_parse_result {
let mut handle_size = |n| {
let n = cmp::min(n, max_remaining);
max_remaining -= n;
n
};
match count { match count {
// FIXME(emilio): we shouldn't expand repeat() at // FIXME(emilio): we shouldn't expand repeat() at
// parse time for subgrid. (bug 1583429) // parse time for subgrid. (bug 1583429)
RepeatCount::Number(num) => line_names.extend( RepeatCount::Number(num) => {
names_list let n = handle_size(
.iter() num.value() as usize * names_list.len());
.cloned() line_names.extend(
.cycle() names_list.iter().cloned().cycle().take(n));
.take(num.value() as usize * names_list.len()), },
),
RepeatCount::AutoFill if fill_data.is_none() => { RepeatCount::AutoFill if fill_data.is_none() => {
let fill_idx = line_names.len(); let fill_idx = line_names.len();
let fill_len = names_list.len(); let fill_len = names_list.len();
fill_data = Some((fill_idx, fill_len)); fill_data = Some((fill_idx, fill_len));
line_names.extend(names_list.into_iter()); let n = handle_size(fill_len);
line_names.extend(names_list.into_iter().take(n));
}, },
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
} }
} else if let Ok(names) = input.try_parse(parse_line_names) { } else if let Ok(names) = input.try_parse(parse_line_names) {
line_names.push(names); if max_remaining > 0 {
line_names.push(names);
max_remaining -= 1;
}
} else { } else {
break; break;
} }
} }
if line_names.len() > MAX_GRID_LINE as usize { debug_assert!(line_names.len() <= MAX_GRID_LINE as usize);
line_names.truncate(MAX_GRID_LINE as usize);
}
let (fill_start, fill_len) = fill_data.unwrap_or((0, 0)); let (fill_start, fill_len) = fill_data.unwrap_or((0, 0));