|
1 |
| -//! Tests for integer overflow behavior of encoded_len* functions. |
2 |
| -//! |
3 |
| -//! Many of the tests in this module allocate and fill large amounts of RAM, |
4 |
| -//! so they should better be run in isolation and in a sufficiently |
5 |
| -//! memory-budgeted environment. Particularly on 32-bit platforms, the tests |
6 |
| -//! should not be run in parallel as the combined allocation requests can exceed |
7 |
| -//! the addressable memory. |
8 |
| -
|
9 | 1 | mod proto {
|
10 | 2 | include!(concat!(env!("OUT_DIR"), "/encoded_len.rs"));
|
11 | 3 | }
|
12 | 4 |
|
13 |
| -use prost::alloc::vec; |
14 |
| - |
15 |
| -#[cfg(target_pointer_width = "64")] |
16 |
| -fn verify_overflowing_encoded_len(actual: usize, expected: u64) -> bool { |
17 |
| - if actual as u64 == expected { |
18 |
| - true |
19 |
| - } else { |
20 |
| - cfg_if! { |
21 |
| - if #[cfg(feature = "std")] { |
22 |
| - eprintln!("expected {} but the function returned {}", expected, actual); |
23 |
| - } |
24 |
| - } |
25 |
| - false |
26 |
| - } |
27 |
| -} |
28 |
| - |
29 |
| -#[cfg(target_pointer_width = "32")] |
30 |
| -fn verify_overflowing_encoded_len(actual: usize, _expected: u64) -> bool { |
31 |
| - // Tests calling this function are expected to panic on 32-bit platforms |
32 |
| - // before this check is called. Returning true here allows the |
33 |
| - // #[should_panic] tests to fail. |
34 |
| - cfg_if! { |
35 |
| - if #[cfg(feature = "std")] { |
36 |
| - eprintln!("expected panic, but the function returned {actual}"); |
37 |
| - } |
38 |
| - } |
39 |
| - true |
40 |
| -} |
41 |
| - |
42 |
| -mod field { |
43 |
| - // Test encoded_len* functions in prost::encoding submodules for various field types. |
44 |
| - |
45 |
| - use super::*; |
46 |
| - |
47 |
| - mod bool { |
48 |
| - use super::*; |
49 |
| - use prost::encoding::{bool, MAX_TAG}; |
50 |
| - |
51 |
| - #[test] |
52 |
| - #[ignore = "allocates and fills about 666 MiB"] |
53 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
54 |
| - fn encoded_len_repeated_can_overflow_u32() { |
55 |
| - let filler = false; |
56 |
| - let filler_len = bool::encoded_len(MAX_TAG, &filler); |
57 |
| - let bomb32 = vec![filler; u32::MAX as usize / filler_len + 1]; |
58 |
| - let encoded_len = bool::encoded_len_repeated(MAX_TAG, &bomb32); |
59 |
| - let expected_len = bomb32.len() as u64 * filler_len as u64; |
60 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
61 |
| - } |
62 |
| - } |
63 |
| - |
64 |
| - // These tests may abort on the large allocations when built on a 32-bit |
65 |
| - // target and run in company with some other tests, even with |
66 |
| - // --test-threads=1. |
67 |
| - // As heap fragmentation seemingly becomes a problem, these tests are best |
68 |
| - // run in isolation. |
69 |
| - mod int32 { |
70 |
| - use super::*; |
71 |
| - use prost::encoding::{int32, MAX_TAG}; |
72 |
| - |
73 |
| - #[test] |
74 |
| - #[ignore = "allocates and fills more than 1 GiB"] |
75 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
76 |
| - fn encoded_len_repeated_can_overflow_u32() { |
77 |
| - let filler = -1i32; |
78 |
| - let filler_len = int32::encoded_len(MAX_TAG, &filler); |
79 |
| - let bomb32 = vec![filler; u32::MAX as usize / filler_len + 1]; |
80 |
| - let encoded_len = int32::encoded_len_repeated(MAX_TAG, &bomb32); |
81 |
| - let expected_len = bomb32.len() as u64 * filler_len as u64; |
82 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
83 |
| - } |
84 |
| - |
85 |
| - #[test] |
86 |
| - #[ignore = "allocates and fills about 1.6 GiB"] |
87 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
88 |
| - fn encoded_len_packed_can_overflow_u32() { |
89 |
| - use prost::encoding::{encoded_len_varint, key_len}; |
90 |
| - |
91 |
| - let filler = -1i32; |
92 |
| - let filler_len = encoded_len_varint(filler as u64); |
93 |
| - let bomb_len = (u32::MAX as usize - key_len(MAX_TAG) - 5) / filler_len + 1; |
94 |
| - let bomb32 = vec![filler; bomb_len]; |
95 |
| - let encoded_len = int32::encoded_len_packed(MAX_TAG, &bomb32); |
96 |
| - let expected_data_len = bomb_len as u64 * filler_len as u64; |
97 |
| - let expected_len = key_len(MAX_TAG) as u64 |
98 |
| - + encoded_len_varint(expected_data_len) as u64 |
99 |
| - + expected_data_len; |
100 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
101 |
| - } |
102 |
| - } |
103 |
| - |
104 |
| - mod message { |
105 |
| - use super::*; |
106 |
| - use crate::encoded_len::proto; |
107 |
| - use prost::encoding::{encoded_len_varint, key_len, message, MAX_TAG}; |
108 |
| - |
109 |
| - #[test] |
110 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
111 |
| - fn encoded_len_can_overflow_u32() { |
112 |
| - let filler = proto::Empty {}; |
113 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
114 |
| - let subcritical = vec![filler; u32::MAX as usize / filler_len]; |
115 |
| - let payload_len = subcritical.len() * filler_len; |
116 |
| - assert_eq!(encoded_len_varint(payload_len as u64), 5); |
117 |
| - assert!(key_len(MAX_TAG) + 5 >= filler_len); |
118 |
| - let bomb32 = proto::Testbed { |
119 |
| - repeated_empty: subcritical, |
120 |
| - ..Default::default() |
121 |
| - }; |
122 |
| - let encoded_len = message::encoded_len(MAX_TAG, &bomb32); |
123 |
| - let expected_len = (key_len(MAX_TAG) + 5) as u64 + payload_len as u64; |
124 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
125 |
| - } |
126 |
| - |
127 |
| - #[test] |
128 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
129 |
| - fn encoded_len_repeated_can_overflow_u32() { |
130 |
| - let filler = proto::Empty {}; |
131 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
132 |
| - let bomb32 = vec![filler; u32::MAX as usize / filler_len + 1]; |
133 |
| - let encoded_len = message::encoded_len_repeated(MAX_TAG, &bomb32); |
134 |
| - let expected_len = bomb32.len() as u64 * filler_len as u64; |
135 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
136 |
| - } |
137 |
| - } |
138 |
| - |
139 |
| - macro_rules! test_map { |
140 |
| - ($map_mod:ident, $map_init:expr) => { |
141 |
| - use prost::encoding::{int32, message, $map_mod, MAX_TAG}; |
142 |
| - |
143 |
| - #[test] |
144 |
| - #[ignore = "allocates and fills about 1 GiB"] |
145 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
146 |
| - fn encoded_len_can_overflow_u32() { |
147 |
| - let encoded_entry_len = 5 + 1 + 1 + 10; |
148 |
| - let num_entries = u32::MAX as usize / encoded_entry_len + 1; |
149 |
| - let mut map = $map_init(num_entries); |
150 |
| - map.extend((-(num_entries as i32)..0).map(|i| (i, proto::Empty {}))); |
151 |
| - let encoded_len = |
152 |
| - $map_mod::encoded_len(int32::encoded_len, message::encoded_len, MAX_TAG, &map); |
153 |
| - let expected_len = num_entries as u64 * encoded_entry_len as u64; |
154 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
155 |
| - } |
156 |
| - }; |
157 |
| - } |
158 |
| - |
159 |
| - mod btree_map { |
160 |
| - use super::*; |
161 |
| - use prost::alloc::collections::BTreeMap; |
162 |
| - |
163 |
| - test_map!(btree_map, |_| BTreeMap::new()); |
164 |
| - } |
165 |
| - |
166 |
| - #[cfg(feature = "std")] |
167 |
| - mod hash_map { |
168 |
| - use super::*; |
169 |
| - use std::collections::HashMap; |
170 |
| - |
171 |
| - test_map!(hash_map, HashMap::with_capacity); |
172 |
| - } |
173 |
| -} |
174 |
| - |
175 |
| -mod derived { |
176 |
| - // Test overflow behavior of Message::encoded_len method implementations |
177 |
| - // generated by prost-derive. |
178 |
| - |
179 |
| - use super::*; |
180 |
| - use crate::encoded_len::proto; |
181 |
| - use prost::alloc::collections::BTreeMap; |
182 |
| - use prost::alloc::string::String; |
183 |
| - use prost::alloc::vec::Vec; |
184 |
| - use prost::encoding::{message, MAX_TAG}; |
185 |
| - use prost::Message; |
186 |
| - |
187 |
| - // Initializes all scalar fields so as to give the largest possible |
188 |
| - // encodings for these fields. |
189 |
| - const FATTEST_SCALARS: proto::Testbed = proto::Testbed { |
190 |
| - int32: -1, |
191 |
| - int64: -1, |
192 |
| - uint32: u32::MAX, |
193 |
| - uint64: u64::MAX, |
194 |
| - sint32: i32::MIN, |
195 |
| - sint64: i64::MIN, |
196 |
| - fixed32: 1, |
197 |
| - fixed64: 1, |
198 |
| - sfixed32: -1, |
199 |
| - sfixed64: -1, |
200 |
| - float: 1.0, |
201 |
| - double: 1.0, |
202 |
| - bool: true, |
203 |
| - enumeration: proto::BadEnum::Long as i32, |
204 |
| - string: String::new(), |
205 |
| - bytes: Vec::new(), |
206 |
| - packed_int32: vec![], |
207 |
| - map: BTreeMap::new(), |
208 |
| - repeated_empty: vec![], |
209 |
| - }; |
210 |
| - |
211 |
| - const SCALAR_ENCODED_LEN_LIMITS: &[usize] = &[ |
212 |
| - 10, // int32 |
213 |
| - 10, // int64 |
214 |
| - 5, // uint32 |
215 |
| - 10, // uint64 |
216 |
| - 5, // sint32 |
217 |
| - 10, // sint64 |
218 |
| - 4, // fixed32 |
219 |
| - 8, // fixed64 |
220 |
| - 4, // sfixed32 |
221 |
| - 8, // sfixed64 |
222 |
| - 4, // float |
223 |
| - 8, // double |
224 |
| - 1, // bool |
225 |
| - 10, // enumeration |
226 |
| - ]; |
227 |
| - |
228 |
| - #[test] |
229 |
| - fn limited_length_scalar_encodings_are_accounted_for() { |
230 |
| - assert_eq!( |
231 |
| - FATTEST_SCALARS.encoded_len(), |
232 |
| - SCALAR_ENCODED_LEN_LIMITS |
233 |
| - .iter() |
234 |
| - .cloned() |
235 |
| - .map(|len| 1 + len) |
236 |
| - .sum() |
237 |
| - ); |
238 |
| - } |
239 |
| - |
240 |
| - #[test] |
241 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
242 |
| - fn encoded_len_can_overflow_u32_with_repeated_field() { |
243 |
| - let filler = proto::Empty {}; |
244 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
245 |
| - let supercritical = |
246 |
| - vec![filler; (u32::MAX as usize - FATTEST_SCALARS.encoded_len()) / filler_len + 1]; |
247 |
| - let payload_len = supercritical.len() as u64 * filler_len as u64; |
248 |
| - let bomb32 = proto::Testbed { |
249 |
| - repeated_empty: supercritical, |
250 |
| - ..FATTEST_SCALARS |
251 |
| - }; |
252 |
| - let encoded_len = bomb32.encoded_len(); |
253 |
| - let expected_len = FATTEST_SCALARS.encoded_len() as u64 + payload_len as u64; |
254 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
255 |
| - } |
256 |
| - |
257 |
| - #[test] |
258 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
259 |
| - fn encoded_len_can_overflow_u32_with_string() { |
260 |
| - let filler = proto::Empty {}; |
261 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
262 |
| - let padding = |
263 |
| - vec![filler; (u32::MAX as usize - FATTEST_SCALARS.encoded_len()) / filler_len]; |
264 |
| - let padding_len = padding.len() * filler_len; |
265 |
| - let bomb32 = proto::Testbed { |
266 |
| - repeated_empty: padding, |
267 |
| - string: " ".repeat(filler_len - 2 - 1), |
268 |
| - ..FATTEST_SCALARS |
269 |
| - }; |
270 |
| - let encoded_len = bomb32.encoded_len(); |
271 |
| - let expected_len = |
272 |
| - FATTEST_SCALARS.encoded_len() as u64 + padding_len as u64 + filler_len as u64; |
273 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
274 |
| - } |
275 |
| - |
276 |
| - #[test] |
277 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
278 |
| - fn encoded_len_can_overflow_u32_with_bytes() { |
279 |
| - let filler = proto::Empty {}; |
280 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
281 |
| - let padding = |
282 |
| - vec![filler; (u32::MAX as usize - FATTEST_SCALARS.encoded_len()) / filler_len]; |
283 |
| - let padding_len = padding.len() * filler_len; |
284 |
| - let bomb32 = proto::Testbed { |
285 |
| - repeated_empty: padding, |
286 |
| - bytes: b" ".repeat(filler_len - 2 - 1), |
287 |
| - ..FATTEST_SCALARS |
288 |
| - }; |
289 |
| - let encoded_len = bomb32.encoded_len(); |
290 |
| - let expected_len = |
291 |
| - FATTEST_SCALARS.encoded_len() as u64 + padding_len as u64 + filler_len as u64; |
292 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
293 |
| - } |
294 |
| - |
295 |
| - #[test] |
296 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
297 |
| - fn encoded_len_can_overflow_u32_with_packed_varint() { |
298 |
| - let filler = proto::Empty {}; |
299 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
300 |
| - let padding = |
301 |
| - vec![filler; (u32::MAX as usize - FATTEST_SCALARS.encoded_len()) / filler_len]; |
302 |
| - let padding_len = padding.len() * filler_len; |
303 |
| - let bomb32 = proto::Testbed { |
304 |
| - repeated_empty: padding, |
305 |
| - packed_int32: vec![0; filler_len - 2 - 1], |
306 |
| - ..FATTEST_SCALARS |
307 |
| - }; |
308 |
| - let encoded_len = bomb32.encoded_len(); |
309 |
| - let expected_len = |
310 |
| - FATTEST_SCALARS.encoded_len() as u64 + padding_len as u64 + filler_len as u64; |
311 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
312 |
| - } |
313 |
| - |
314 |
| - #[test] |
315 |
| - #[cfg_attr(target_pointer_width = "32", should_panic)] |
316 |
| - fn encoded_len_can_overflow_u32_with_map() { |
317 |
| - let filler = proto::Empty {}; |
318 |
| - let filler_len = message::encoded_len(MAX_TAG, &filler); |
319 |
| - let padding = |
320 |
| - vec![filler; (u32::MAX as usize - FATTEST_SCALARS.encoded_len()) / filler_len]; |
321 |
| - let padding_len = padding.len() * filler_len; |
322 |
| - let map = [(0, -1)].iter().cloned().collect(); |
323 |
| - let bomb32 = proto::Testbed { |
324 |
| - repeated_empty: padding, |
325 |
| - map, |
326 |
| - ..FATTEST_SCALARS |
327 |
| - }; |
328 |
| - let encoded_len = bomb32.encoded_len(); |
329 |
| - let expected_len = FATTEST_SCALARS.encoded_len() as u64 + padding_len as u64 + 14; |
330 |
| - assert!(verify_overflowing_encoded_len(encoded_len, expected_len)); |
331 |
| - } |
332 |
| -} |
| 5 | +mod limit; |
| 6 | +mod overflow; |
0 commit comments