Skip to main content

structured_asn1/
limits.rs

1//! What the caller is willing to spend on one parse.
2//!
3//! These bound work rather than document size: a fixed ceiling refuses real
4//! documents while a hostile input stays under it. Each is checked before the
5//! work it guards.
6
7use core::cell::Cell;
8
9use crate::error::Limit;
10use crate::memory::Memory;
11
12/// How much a parse may spend.
13///
14/// The defaults are generous, because they exist to stop a document that is
15/// trying to make this run forever, not to decide that real documents are too
16/// big. A caller on a device with less to spend lowers them.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct Limits {
19    /// How deeply elements may nest.
20    ///
21    /// Certificates and signed data nest perhaps a dozen deep. A thousand is a
22    /// document doing something else.
23    pub max_depth: u32,
24
25    /// How many elements may be read in total.
26    pub max_elements: u64,
27
28    /// How long a single element's contents may be.
29    pub max_element_length: u64,
30
31    /// How many bytes of header and structure may be examined.
32    ///
33    /// Content bytes are not counted against this: reading a large certificate
34    /// is not the thing worth stopping. What this bounds is the number of
35    /// elements-worth of parsing a document can ask for, which is what a deeply
36    /// or densely nested input inflates.
37    pub max_work: u64,
38}
39
40impl Default for Limits {
41    fn default() -> Self {
42        Self {
43            max_depth: 1_000,
44            max_elements: 1_000_000,
45            max_element_length: 1 << 32,
46            max_work: 16_000_000,
47        }
48    }
49}
50
51impl Limits {
52    /// Limits that allow nothing beyond a single shallow element, for tests
53    /// and for callers that want to prove a budget is enforced.
54    #[must_use]
55    pub fn none() -> Self {
56        Self {
57            max_depth: 0,
58            max_elements: 0,
59            max_element_length: 0,
60            max_work: 0,
61        }
62    }
63}
64
65/// What has been spent so far, against what was allowed.
66///
67/// The counters are cells so that a reader entered from another reader shares
68/// one budget rather than each getting a fresh one. A per-container budget
69/// would bound nothing: a document could ask for any amount of work by asking
70/// for a little in each of many containers.
71#[derive(Debug)]
72pub(crate) struct Budget {
73    limits: Limits,
74    elements: Cell<u64>,
75    work: Cell<u64>,
76    memory: Memory,
77}
78
79impl Budget {
80    pub(crate) fn new(limits: Limits, memory: Memory) -> Self {
81        Self {
82            limits,
83            elements: Cell::new(0),
84            work: Cell::new(0),
85            memory,
86        }
87    }
88
89    pub(crate) fn limits(&self) -> Limits {
90        self.limits
91    }
92
93    pub(crate) fn memory(&self) -> &Memory {
94        &self.memory
95    }
96
97    /// Charges for one element about to be read, before reading it.
98    ///
99    /// Reports which budget ran out and nothing else: where it happened is
100    /// known to the caller, who assembles that only when there is an error.
101    pub(crate) fn element(&self) -> Result<(), Limit> {
102        let spent = self.elements.get();
103        debug_assert!(spent <= self.limits.max_elements);
104        if spent >= self.limits.max_elements {
105            return Err(Limit::Elements);
106        }
107        self.elements.set(spent + 1);
108        Ok(())
109    }
110
111    /// Charges structure in the original domain for budget unit tests.
112    #[cfg(test)]
113    pub(crate) fn work(&self, amount: u64) -> Result<(), Limit> {
114        self.work_in(amount, &self.memory)
115    }
116
117    /// Retains the session ceiling while charging the current execution domain.
118    pub(crate) fn work_in(&self, amount: u64, execution: &Memory) -> Result<(), Limit> {
119        let spent = self.work.get();
120        debug_assert!(spent <= self.limits.max_work);
121        if amount > self.limits.max_work - spent {
122            return Err(Limit::Work);
123        }
124        execution.charge_work(amount).map_err(|_| Limit::Work)?;
125        self.work.set(spent + amount);
126        Ok(())
127    }
128
129    /// Checks a depth before descending to it.
130    pub(crate) fn depth(&self, depth: u32) -> Result<(), Limit> {
131        if depth > self.limits.max_depth {
132            return Err(Limit::Depth);
133        }
134        Ok(())
135    }
136
137    /// Checks a stated length before trusting it enough to slice with.
138    pub(crate) fn length(&self, length: u64) -> Result<(), Limit> {
139        if length > self.limits.max_element_length {
140            return Err(Limit::ElementLength);
141        }
142        Ok(())
143    }
144}
145
146#[cfg(test)]
147mod tests;