libmagic_rs/evaluator/offset/
absolute.rs1#[derive(Debug, thiserror::Error)]
8pub enum OffsetError {
9 #[error("Buffer overrun: offset {offset} is beyond buffer length {buffer_len}")]
20 BufferOverrun {
21 offset: usize,
23 buffer_len: usize,
25 },
26
27 #[error("Invalid offset: {reason}")]
38 InvalidOffset {
39 reason: String,
41 },
42
43 #[error("Arithmetic overflow in offset calculation")]
54 ArithmeticOverflow,
55}
56
57pub fn resolve_absolute_offset(offset: i64, buffer: &[u8]) -> Result<usize, OffsetError> {
99 let buffer_len = buffer.len();
100
101 if offset >= 0 {
102 let abs_offset = usize::try_from(offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
104 if abs_offset >= buffer_len {
105 return Err(OffsetError::BufferOverrun {
106 offset: abs_offset,
107 buffer_len,
108 });
109 }
110 Ok(abs_offset)
111 } else {
112 if offset == i64::MIN {
115 return Err(OffsetError::ArithmeticOverflow);
116 }
117
118 let offset_from_end =
119 usize::try_from(-offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
120
121 if offset_from_end > buffer_len {
122 return Err(OffsetError::BufferOverrun {
123 offset: buffer_len.saturating_sub(offset_from_end),
124 buffer_len,
125 });
126 }
127
128 let resolved_offset = buffer_len - offset_from_end;
130 Ok(resolved_offset)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_resolve_absolute_offset_positive() {
140 let buffer = b"Hello, World!";
141
142 assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
144 assert_eq!(resolve_absolute_offset(1, buffer).unwrap(), 1);
145 assert_eq!(resolve_absolute_offset(7, buffer).unwrap(), 7);
146 assert_eq!(resolve_absolute_offset(12, buffer).unwrap(), 12); }
148
149 #[test]
150 fn test_resolve_absolute_offset_negative() {
151 let buffer = b"Hello, World!";
152
153 assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 12); assert_eq!(resolve_absolute_offset(-6, buffer).unwrap(), 7); assert_eq!(resolve_absolute_offset(-13, buffer).unwrap(), 0); }
158
159 #[test]
160 fn test_resolve_absolute_offset_out_of_bounds_positive() {
161 let buffer = b"Hello";
162
163 let result = resolve_absolute_offset(5, buffer);
165 assert!(result.is_err());
166
167 match result.unwrap_err() {
168 OffsetError::BufferOverrun { offset, buffer_len } => {
169 assert_eq!(offset, 5);
170 assert_eq!(buffer_len, 5);
171 }
172 _ => panic!("Expected BufferOverrun error"),
173 }
174
175 let result = resolve_absolute_offset(100, buffer);
177 assert!(result.is_err());
178 }
179
180 #[test]
181 fn test_resolve_absolute_offset_out_of_bounds_negative() {
182 let buffer = b"Hi";
183
184 let result = resolve_absolute_offset(-3, buffer);
186 assert!(result.is_err());
187
188 match result.unwrap_err() {
189 OffsetError::BufferOverrun { .. } => {
190 }
192 _ => panic!("Expected BufferOverrun error"),
193 }
194
195 let result = resolve_absolute_offset(-100, buffer);
197 assert!(result.is_err());
198 }
199
200 #[test]
201 fn test_resolve_absolute_offset_empty_buffer() {
202 let buffer = b"";
203
204 assert!(resolve_absolute_offset(0, buffer).is_err());
206 assert!(resolve_absolute_offset(1, buffer).is_err());
207 assert!(resolve_absolute_offset(-1, buffer).is_err());
208 }
209
210 #[test]
211 fn test_resolve_absolute_offset_edge_cases() {
212 let buffer = b"X"; assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
216 assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 0);
217
218 assert!(resolve_absolute_offset(1, buffer).is_err());
220 assert!(resolve_absolute_offset(-2, buffer).is_err());
221 }
222
223 #[test]
224 fn test_large_buffer_offsets() {
225 let large_buffer = vec![0u8; 1024];
227
228 assert_eq!(resolve_absolute_offset(0, &large_buffer).unwrap(), 0);
230 assert_eq!(resolve_absolute_offset(512, &large_buffer).unwrap(), 512);
231 assert_eq!(resolve_absolute_offset(1023, &large_buffer).unwrap(), 1023);
232
233 assert_eq!(resolve_absolute_offset(-1, &large_buffer).unwrap(), 1023);
235 assert_eq!(resolve_absolute_offset(-512, &large_buffer).unwrap(), 512);
236 assert_eq!(resolve_absolute_offset(-1024, &large_buffer).unwrap(), 0);
237
238 assert!(resolve_absolute_offset(1024, &large_buffer).is_err());
240 assert!(resolve_absolute_offset(-1025, &large_buffer).is_err());
241 }
242
243 #[test]
245 fn test_offset_security_edge_cases() {
246 let buffer = b"test";
247
248 let overflow_cases = vec![i64::MAX, i64::MIN, i64::MAX - 1, i64::MIN + 1];
250
251 for offset in overflow_cases {
252 let result = resolve_absolute_offset(offset, buffer);
253 if let Ok(resolved) = result {
255 assert!(
257 resolved < buffer.len(),
258 "Resolved offset {resolved} exceeds buffer length {}",
259 buffer.len()
260 );
261 } else {
262 }
264 }
265 }
266
267 #[test]
268 fn test_offset_error_display() {
269 let error = OffsetError::BufferOverrun {
270 offset: 10,
271 buffer_len: 5,
272 };
273 let error_str = error.to_string();
274 assert!(error_str.contains("Buffer overrun"));
275 assert!(error_str.contains("10"));
276 assert!(error_str.contains('5'));
277
278 let error = OffsetError::InvalidOffset {
279 reason: "test reason".to_string(),
280 };
281 let error_str = error.to_string();
282 assert!(error_str.contains("Invalid offset"));
283 assert!(error_str.contains("test reason"));
284
285 let error = OffsetError::ArithmeticOverflow;
286 let error_str = error.to_string();
287 assert!(error_str.contains("Arithmetic overflow"));
288 }
289
290 #[test]
291 fn test_resolve_absolute_offset_arithmetic_overflow() {
292 let buffer = b"test";
293
294 let result = resolve_absolute_offset(i64::MIN, buffer);
296 assert!(result.is_err());
297
298 match result.unwrap_err() {
299 OffsetError::ArithmeticOverflow => {
300 }
302 _ => panic!("Expected ArithmeticOverflow error"),
303 }
304 }
305}