Skip to content

Commit d5294cb

Browse files
committed
Harden against denial of service attacks
This hardens the code against denial of service attacks by only going back a certain number of elements (set at parser construction time) in the list of active formatting elements and the stack of open elements.
1 parent 6e5ef0d commit d5294cb

File tree

1 file changed

+88
-9
lines changed
  • html5ever/src/tree_builder

1 file changed

+88
-9
lines changed

html5ever/src/tree_builder/mod.rs

+88-9
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ use tree_builder::tag_sets::*;
4040
use util::str::to_escaped_string;
4141

4242
pub use self::PushFlag::*;
43+
use std::ops::{Deref, DerefMut};
44+
use std::cmp::max;
45+
use std::slice::Iter;
4346

4447
#[macro_use] mod tag_sets;
4548

@@ -48,6 +51,69 @@ mod types;
4851

4952
include!(concat!(env!("OUT_DIR"), "/rules.rs"));
5053

54+
#[derive(Clone)]
55+
pub struct LimitedVec<T> {
56+
vec: Vec<T>,
57+
limit: usize,
58+
}
59+
60+
impl<T> LimitedVec<T> {
61+
pub fn new(limit: usize) -> Self {
62+
LimitedVec {
63+
vec: vec![],
64+
limit: if limit == 0 { 10 } else { limit },
65+
}
66+
}
67+
68+
fn lower_bound(&self) -> usize {
69+
let len = self.vec.len();
70+
// Watch out for overflow!
71+
max(len, self.limit) - self.limit
72+
}
73+
74+
pub fn push(&mut self, other: T) {
75+
self.vec.push(other)
76+
}
77+
78+
pub fn remove(&mut self, pos: usize) {
79+
let lower_bound = self.lower_bound();
80+
self.vec.remove(pos + lower_bound);
81+
}
82+
83+
pub fn truncate(&mut self, pos: usize) {
84+
let lower_bound = self.lower_bound();
85+
self.vec.truncate(pos + lower_bound);
86+
}
87+
88+
pub fn pop(&mut self) -> Option<T> {
89+
self.vec.pop()
90+
}
91+
92+
pub fn insert(&mut self, index: usize, element: T) {
93+
let lower_bound = self.lower_bound();
94+
self.vec.insert(index + lower_bound, element)
95+
}
96+
97+
fn real_iter(&self) -> Iter<T> {
98+
self.vec.iter()
99+
}
100+
}
101+
102+
impl<T> Deref for LimitedVec<T> {
103+
type Target = [T];
104+
fn deref(&self) -> &[T] {
105+
let bottom = self.lower_bound();
106+
&self.vec[bottom..]
107+
}
108+
}
109+
110+
impl<T> DerefMut for LimitedVec<T> {
111+
fn deref_mut(&mut self) -> &mut [T] {
112+
let bottom = self.lower_bound();
113+
&mut self.vec[bottom..]
114+
}
115+
}
116+
51117
/// Tree builder options, with an impl for Default.
52118
#[derive(Copy, Clone)]
53119
pub struct TreeBuilderOpts {
@@ -67,6 +133,13 @@ pub struct TreeBuilderOpts {
67133
/// Obsolete, ignored.
68134
pub ignore_missing_rules: bool,
69135

136+
/// The maximum amount that the parser will process through the list
137+
/// of active formatting elements and the the stack of open elements.
138+
/// This is set to a finite number to prevent denial-of-service security
139+
/// vulnerabilities. 0 is treated as the default (currently 20); any other
140+
/// value is used as-is.
141+
pub max_stack_depth: u8,
142+
70143
/// Initial TreeBuilder quirks mode. Default: NoQuirks
71144
pub quirks_mode: QuirksMode,
72145
}
@@ -79,6 +152,7 @@ impl Default for TreeBuilderOpts {
79152
iframe_srcdoc: false,
80153
drop_doctype: false,
81154
ignore_missing_rules: false,
155+
max_stack_depth: 10,
82156
quirks_mode: NoQuirks,
83157
}
84158
}
@@ -112,10 +186,10 @@ pub struct TreeBuilder<Handle, Sink> {
112186
doc_handle: Handle,
113187

114188
/// Stack of open elements, most recently added at end.
115-
open_elems: Vec<Handle>,
189+
open_elems: LimitedVec<Handle>,
116190

117191
/// List of active formatting elements.
118-
active_formatting: Vec<FormatEntry<Handle>>,
192+
active_formatting: LimitedVec<FormatEntry<Handle>>,
119193

120194
//§ the-element-pointers
121195
/// Head element pointer.
@@ -165,8 +239,8 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
165239
pending_table_text: vec!(),
166240
quirks_mode: opts.quirks_mode,
167241
doc_handle: doc_handle,
168-
open_elems: vec!(),
169-
active_formatting: vec!(),
242+
open_elems: LimitedVec::new(opts.max_stack_depth as usize),
243+
active_formatting: LimitedVec::new(opts.max_stack_depth as usize),
170244
head_elem: None,
171245
form_elem: None,
172246
frameset_ok: true,
@@ -197,8 +271,8 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
197271
pending_table_text: vec!(),
198272
quirks_mode: opts.quirks_mode,
199273
doc_handle: doc_handle,
200-
open_elems: vec!(),
201-
active_formatting: vec!(),
274+
open_elems: LimitedVec::new(opts.max_stack_depth as usize),
275+
active_formatting: LimitedVec::new(opts.max_stack_depth as usize),
202276
head_elem: None,
203277
form_elem: form_elem,
204278
frameset_ok: true,
@@ -251,10 +325,10 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
251325
/// internal state. This is intended to support garbage-collected DOMs.
252326
pub fn trace_handles(&self, tracer: &Tracer<Handle=Handle>) {
253327
tracer.trace_handle(&self.doc_handle);
254-
for e in &self.open_elems {
328+
for e in self.open_elems.real_iter() {
255329
tracer.trace_handle(e);
256330
}
257-
for e in &self.active_formatting {
331+
for e in self.active_formatting.real_iter() {
258332
match e {
259333
&Element(ref h, _) => tracer.trace_handle(h),
260334
_ => (),
@@ -477,7 +551,7 @@ impl<Handle, Sink> TokenSink
477551
}
478552

479553
fn end(&mut self) {
480-
for elem in self.open_elems.drain(..).rev() {
554+
for elem in self.open_elems.into_iter().rev() {
481555
self.sink.pop(&elem);
482556
}
483557
}
@@ -688,6 +762,11 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
688762
}
689763
);
690764

765+
if fmt_elem_stack_index == 0 {
766+
self.sink.parse_error(Borrowed("Tree too complex to parse correctly – returned tree will be inaccurate"));
767+
return
768+
}
769+
691770
// 11.
692771
let common_ancestor = self.open_elems[fmt_elem_stack_index - 1].clone();
693772

0 commit comments

Comments
 (0)