Skip to content

Commit 6cb03c0

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 abd8e22 commit 6cb03c0

File tree

1 file changed

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

1 file changed

+83
-9
lines changed

html5ever/src/tree_builder/mod.rs

+83-9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ use std::default::Default;
3333
use std::mem::replace;
3434
use std::borrow::Cow::Borrowed;
3535
use std::collections::VecDeque;
36+
use std::ops::{Deref, DerefMut};
37+
use std::cmp::max;
38+
use std::slice::Iter;
3639

3740
#[macro_use] mod tag_sets;
3841

@@ -46,6 +49,69 @@ mod rules {
4649
include!(concat!(env!("OUT_DIR"), "/rules.rs"));
4750
}
4851

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

134+
/// The maximum amount that the parser will process through the list
135+
/// of active formatting elements and the the stack of open elements.
136+
/// This is set to a finite number to prevent denial-of-service security
137+
/// vulnerabilities. 0 is treated as the default (currently 10); any other
138+
/// value is used as-is.
139+
pub max_stack_depth: u8,
140+
68141
/// Initial TreeBuilder quirks mode. Default: NoQuirks
69142
pub quirks_mode: QuirksMode,
70143
}
@@ -77,6 +150,7 @@ impl Default for TreeBuilderOpts {
77150
iframe_srcdoc: false,
78151
drop_doctype: false,
79152
ignore_missing_rules: false,
153+
max_stack_depth: 10,
80154
quirks_mode: NoQuirks,
81155
}
82156
}
@@ -110,10 +184,10 @@ pub struct TreeBuilder<Handle, Sink> {
110184
doc_handle: Handle,
111185

112186
/// Stack of open elements, most recently added at end.
113-
open_elems: Vec<Handle>,
187+
open_elems: LimitedVec<Handle>,
114188

115189
/// List of active formatting elements.
116-
active_formatting: Vec<FormatEntry<Handle>>,
190+
active_formatting: LimitedVec<FormatEntry<Handle>>,
117191

118192
//§ the-element-pointers
119193
/// Head element pointer.
@@ -163,8 +237,8 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
163237
pending_table_text: vec!(),
164238
quirks_mode: opts.quirks_mode,
165239
doc_handle: doc_handle,
166-
open_elems: vec!(),
167-
active_formatting: vec!(),
240+
open_elems: LimitedVec::new(opts.max_stack_depth as usize),
241+
active_formatting: LimitedVec::new(opts.max_stack_depth as usize),
168242
head_elem: None,
169243
form_elem: None,
170244
frameset_ok: true,
@@ -195,8 +269,8 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
195269
pending_table_text: vec!(),
196270
quirks_mode: opts.quirks_mode,
197271
doc_handle: doc_handle,
198-
open_elems: vec!(),
199-
active_formatting: vec!(),
272+
open_elems: LimitedVec::new(opts.max_stack_depth as usize),
273+
active_formatting: LimitedVec::new(opts.max_stack_depth as usize),
200274
head_elem: None,
201275
form_elem: form_elem,
202276
frameset_ok: true,
@@ -249,10 +323,10 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
249323
/// internal state. This is intended to support garbage-collected DOMs.
250324
pub fn trace_handles(&self, tracer: &Tracer<Handle=Handle>) {
251325
tracer.trace_handle(&self.doc_handle);
252-
for e in &self.open_elems {
326+
for e in self.open_elems.real_iter() {
253327
tracer.trace_handle(e);
254328
}
255-
for e in &self.active_formatting {
329+
for e in self.active_formatting.real_iter() {
256330
match e {
257331
&Element(ref h, _) => tracer.trace_handle(h),
258332
_ => (),
@@ -475,7 +549,7 @@ impl<Handle, Sink> TokenSink
475549
}
476550

477551
fn end(&mut self) {
478-
for elem in self.open_elems.drain(..).rev() {
552+
for elem in self.open_elems.into_iter().rev() {
479553
self.sink.pop(&elem);
480554
}
481555
}

0 commit comments

Comments
 (0)