1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: MIT
use crate::{Error, Result};
/// Default payload per read or write call: 256 KiB.
pub const DEFAULT_IO_CHUNK: usize = 256 * 1024;
/// Hard payload ceiling per read or write call: 1 MiB.
pub const MAX_IO_CHUNK: usize = 1024 * 1024;
/// Resource limits applied before allocation and during I/O.
///
/// These are the only resource bounds of an operation. Each public entry
/// point validates them once; format handlers derive every other bound from
/// these fields or from the format itself.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Limits {
/// Maximum payload per source/sink call. Must be in `1..=MAX_IO_CHUNK`.
pub io_chunk_bytes: usize,
/// Maximum selected input bytes: a whole source, PDF range, or sum of fragment spans.
pub max_input_bytes: u64,
/// Maximum total output bytes for an operation.
pub max_output_bytes: u64,
/// Maximum single dynamic allocation requested by a format handler. It
/// also caps one image payload, one JBIG2 symbol store and one page or
/// region bitmap.
pub max_allocation_bytes: u64,
/// Maximum pages accepted from an input document.
pub max_pages: u32,
/// Maximum bookmarks accepted from an input document.
pub max_bookmarks: u32,
/// Maximum pixels of one decoded image, page, region or symbol bitmap.
pub max_image_pixels: u64,
/// Maximum symbols in one JBIG2 symbol dictionary, including imported
/// symbols, and the bound on its height classes and export runs.
pub max_symbols: u32,
}
impl Default for Limits {
fn default() -> Self {
Self {
io_chunk_bytes: DEFAULT_IO_CHUNK,
max_input_bytes: 8 * 1024 * 1024 * 1024,
max_output_bytes: 16 * 1024 * 1024 * 1024,
max_allocation_bytes: 64 * 1024 * 1024,
max_pages: 100_000,
max_bookmarks: 100_000,
max_image_pixels: 12_000_000,
max_symbols: 8192,
}
}
}
impl Limits {
/// Reject invalid configurations before any I/O or allocation occurs.
pub fn validate(&self) -> Result<()> {
if self.io_chunk_bytes == 0 {
return Err(Error::invalid("I/O chunk size must be nonzero"));
}
if self.io_chunk_bytes > MAX_IO_CHUNK {
return Err(Error::limit(
"I/O chunk bytes",
MAX_IO_CHUNK as u64,
self.io_chunk_bytes as u64,
));
}
self.check_allocation(self.io_chunk_bytes as u64)
}
/// Check the size of a requested single allocation.
pub fn check_allocation(&self, bytes: u64) -> Result<()> {
if bytes > self.max_allocation_bytes {
return Err(Error::limit(
"allocation bytes",
self.max_allocation_bytes,
bytes,
));
}
Ok(())
}
/// The unlocated error for an allocation of `attempted` bytes that passed
/// [`Self::check_allocation`] but was refused by the allocator.
pub(crate) fn allocation_refused(&self, resource: &'static str, attempted: u64) -> Error {
Error::limit(resource, self.max_allocation_bytes, attempted)
}
/// Check the selected input byte count for one operation.
pub fn check_input_size(&self, bytes: u64) -> Result<()> {
if bytes > self.max_input_bytes {
return Err(Error::limit("input bytes", self.max_input_bytes, bytes));
}
Ok(())
}
/// Check a page count before indexing or allocating pages.
pub fn check_pages(&self, count: u32) -> Result<()> {
if count > self.max_pages {
return Err(Error::limit(
"pages",
u64::from(self.max_pages),
u64::from(count),
));
}
Ok(())
}
/// Check the pixel count of one image, page, region or symbol bitmap.
pub fn check_image_pixels(&self, pixels: u64) -> Result<()> {
if pixels > self.max_image_pixels {
return Err(Error::limit("image pixels", self.max_image_pixels, pixels));
}
Ok(())
}
/// Check the symbol count of one JBIG2 symbol dictionary.
pub fn check_symbols(&self, symbols: u64) -> Result<()> {
if symbols > u64::from(self.max_symbols) {
return Err(Error::limit(
"symbols",
u64::from(self.max_symbols),
symbols,
));
}
Ok(())
}
/// Check a bookmark count before indexing or allocating bookmarks.
pub fn check_bookmarks(&self, count: u32) -> Result<()> {
if count > self.max_bookmarks {
return Err(Error::limit(
"bookmarks",
u64::from(self.max_bookmarks),
u64::from(count),
));
}
Ok(())
}
}