mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-16 05:51:55 +00:00
LibWeb/CSS: Protect against the billion-laughs attack
The attack unfortunately still slows us down, but this prevents us from OOMing. Currently, we don't save the value of `var(--foo)` after computing it once, so in this example, we end up computing `--prop1` 4 times to compute `--prop3`, but then we start again from scratch when computing `--prop4`: ```css --prop1: lol; --prop2: var(--prop1) var(--prop1); --prop3: var(--prop2) var(--prop2); --prop4: var(--prop3) var(--prop3); } ``` This should be solvable later if we update the computed values as we go.
This commit is contained in:
parent
b6032b0fcd
commit
ce79bc793c
Notes:
github-actions[bot]
2025-07-09 15:45:40 +00:00
Author: https://github.com/AtkinsSJ
Commit: ce79bc793c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5226
Reviewed-by: https://github.com/tcl3 ✅
1 changed files with 20 additions and 5 deletions
|
@ -219,7 +219,7 @@ static Vector<ComponentValue> replace_a_var_function(DOM::AbstractElement& eleme
|
|||
return result;
|
||||
}
|
||||
|
||||
static void substitute_arbitrary_substitution_functions_step_2(DOM::AbstractElement& element, GuardedSubstitutionContexts& guarded_contexts, TokenStream<ComponentValue>& source, Vector<ComponentValue>& dest)
|
||||
static ErrorOr<void> substitute_arbitrary_substitution_functions_step_2(DOM::AbstractElement& element, GuardedSubstitutionContexts& guarded_contexts, TokenStream<ComponentValue>& source, Vector<ComponentValue>& dest)
|
||||
{
|
||||
// Step 2 of https://drafts.csswg.org/css-values-5/#substitute-arbitrary-substitution-function
|
||||
// 2. For each arbitrary substitution function func in values (ordered via a depth-first pre-order traversal) that
|
||||
|
@ -262,7 +262,16 @@ static void substitute_arbitrary_substitution_functions_step_2(DOM::AbstractElem
|
|||
// NB: Because we're doing this in one pass recursively, we now need to substitute any ASFs in result.
|
||||
TokenStream result_stream { result };
|
||||
Vector<ComponentValue> result_after_processing;
|
||||
substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, result_stream, result_after_processing);
|
||||
TRY(substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, result_stream, result_after_processing));
|
||||
|
||||
// NB: Protect against the billion-laughs attack by limiting to an arbitrary large number of tokens.
|
||||
// https://drafts.csswg.org/css-values-5/#long-substitution
|
||||
if (source.remaining_token_count() + result_after_processing.size() > 16384) {
|
||||
dest.clear();
|
||||
dest.empend(GuaranteedInvalidValue {});
|
||||
return Error::from_string_literal("Stopped expanding arbitrary substitution functions: maximum length reached.");
|
||||
}
|
||||
|
||||
dest.extend(result_after_processing);
|
||||
}
|
||||
continue;
|
||||
|
@ -270,7 +279,7 @@ static void substitute_arbitrary_substitution_functions_step_2(DOM::AbstractElem
|
|||
|
||||
Vector<ComponentValue> function_values;
|
||||
TokenStream source_function_contents { source_function.value };
|
||||
substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source_function_contents, function_values);
|
||||
TRY(substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source_function_contents, function_values));
|
||||
dest.empend(Function { source_function.name, move(function_values) });
|
||||
continue;
|
||||
}
|
||||
|
@ -278,12 +287,14 @@ static void substitute_arbitrary_substitution_functions_step_2(DOM::AbstractElem
|
|||
auto const& source_block = value.block();
|
||||
TokenStream source_block_values { source_block.value };
|
||||
Vector<ComponentValue> block_values;
|
||||
substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source_block_values, block_values);
|
||||
TRY(substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source_block_values, block_values));
|
||||
dest.empend(SimpleBlock { source_block.token, move(block_values) });
|
||||
continue;
|
||||
}
|
||||
dest.empend(value);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-values-5/#substitute-arbitrary-substitution-function
|
||||
|
@ -308,7 +319,11 @@ Vector<ComponentValue> substitute_arbitrary_substitution_functions(DOM::Abstract
|
|||
// is not nested in the contents of another arbitrary substitution function:
|
||||
Vector<ComponentValue> new_values;
|
||||
TokenStream source { values };
|
||||
substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source, new_values);
|
||||
auto maybe_error = substitute_arbitrary_substitution_functions_step_2(element, guarded_contexts, source, new_values);
|
||||
if (maybe_error.is_error()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "{} (context? {})", maybe_error.release_error(), context.map([](auto& it) { return it.to_string(); }));
|
||||
return { ComponentValue { GuaranteedInvalidValue {} } };
|
||||
}
|
||||
|
||||
// 3. If context is marked as a cyclic substitution context, return the guaranteed-invalid value.
|
||||
// NOTE: Nested arbitrary substitution functions may have marked context as cyclic in step 2.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue