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;