Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d5294cb

Browse files
committedAug 10, 2017
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)
Please sign in to comment.