1use crate::LibmagicError;
10use crate::parser::ast::OffsetSpec;
11
12#[derive(Debug, thiserror::Error)]
14pub enum OffsetError {
15 #[error("Buffer overrun: offset {offset} is beyond buffer length {buffer_len}")]
17 BufferOverrun {
18 offset: usize,
20 buffer_len: usize,
22 },
23
24 #[error("Invalid offset: {reason}")]
26 InvalidOffset {
27 reason: String,
29 },
30
31 #[error("Arithmetic overflow in offset calculation")]
33 ArithmeticOverflow,
34}
35
36pub fn resolve_absolute_offset(offset: i64, buffer: &[u8]) -> Result<usize, OffsetError> {
78 let buffer_len = buffer.len();
79
80 if offset >= 0 {
81 let abs_offset = usize::try_from(offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
83 if abs_offset >= buffer_len {
84 return Err(OffsetError::BufferOverrun {
85 offset: abs_offset,
86 buffer_len,
87 });
88 }
89 Ok(abs_offset)
90 } else {
91 if offset == i64::MIN {
94 return Err(OffsetError::ArithmeticOverflow);
95 }
96
97 let offset_from_end =
98 usize::try_from(-offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
99
100 if offset_from_end > buffer_len {
101 return Err(OffsetError::BufferOverrun {
102 offset: buffer_len.saturating_sub(offset_from_end),
103 buffer_len,
104 });
105 }
106
107 let resolved_offset = buffer_len - offset_from_end;
109 Ok(resolved_offset)
110 }
111}
112
113pub fn resolve_offset(spec: &OffsetSpec, buffer: &[u8]) -> Result<usize, LibmagicError> {
145 match spec {
146 OffsetSpec::Absolute(offset) => {
147 resolve_absolute_offset(*offset, buffer).map_err(|e| match e {
148 OffsetError::BufferOverrun {
149 offset,
150 buffer_len: _,
151 } => LibmagicError::EvaluationError(crate::error::EvaluationError::BufferOverrun {
152 offset,
153 }),
154 OffsetError::InvalidOffset { reason: _ } | OffsetError::ArithmeticOverflow => {
155 LibmagicError::EvaluationError(crate::error::EvaluationError::InvalidOffset {
156 offset: *offset,
157 })
158 }
159 })
160 }
161 OffsetSpec::Indirect { .. } => {
162 Err(LibmagicError::EvaluationError(
164 crate::error::EvaluationError::unsupported_type(
165 "Indirect offsets not yet implemented",
166 ),
167 ))
168 }
169 OffsetSpec::Relative(_) => {
170 Err(LibmagicError::EvaluationError(
172 crate::error::EvaluationError::unsupported_type(
173 "Relative offsets not yet implemented",
174 ),
175 ))
176 }
177 OffsetSpec::FromEnd(offset) => {
178 resolve_absolute_offset(*offset, buffer).map_err(|e| match e {
180 OffsetError::BufferOverrun {
181 offset,
182 buffer_len: _,
183 } => LibmagicError::EvaluationError(crate::error::EvaluationError::BufferOverrun {
184 offset,
185 }),
186 OffsetError::InvalidOffset { reason: _ } | OffsetError::ArithmeticOverflow => {
187 LibmagicError::EvaluationError(crate::error::EvaluationError::InvalidOffset {
188 offset: *offset,
189 })
190 }
191 })
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_resolve_absolute_offset_positive() {
202 let buffer = b"Hello, World!";
203
204 assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
206 assert_eq!(resolve_absolute_offset(1, buffer).unwrap(), 1);
207 assert_eq!(resolve_absolute_offset(7, buffer).unwrap(), 7);
208 assert_eq!(resolve_absolute_offset(12, buffer).unwrap(), 12); }
210
211 #[test]
212 fn test_resolve_absolute_offset_negative() {
213 let buffer = b"Hello, World!";
214
215 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); }
220
221 #[test]
222 fn test_resolve_absolute_offset_out_of_bounds_positive() {
223 let buffer = b"Hello";
224
225 let result = resolve_absolute_offset(5, buffer);
227 assert!(result.is_err());
228
229 match result.unwrap_err() {
230 OffsetError::BufferOverrun { offset, buffer_len } => {
231 assert_eq!(offset, 5);
232 assert_eq!(buffer_len, 5);
233 }
234 _ => panic!("Expected BufferOverrun error"),
235 }
236
237 let result = resolve_absolute_offset(100, buffer);
239 assert!(result.is_err());
240 }
241
242 #[test]
243 fn test_resolve_absolute_offset_out_of_bounds_negative() {
244 let buffer = b"Hi";
245
246 let result = resolve_absolute_offset(-3, buffer);
248 assert!(result.is_err());
249
250 match result.unwrap_err() {
251 OffsetError::BufferOverrun { .. } => {
252 }
254 _ => panic!("Expected BufferOverrun error"),
255 }
256
257 let result = resolve_absolute_offset(-100, buffer);
259 assert!(result.is_err());
260 }
261
262 #[test]
263 fn test_resolve_absolute_offset_empty_buffer() {
264 let buffer = b"";
265
266 assert!(resolve_absolute_offset(0, buffer).is_err());
268 assert!(resolve_absolute_offset(1, buffer).is_err());
269 assert!(resolve_absolute_offset(-1, buffer).is_err());
270 }
271
272 #[test]
273 fn test_resolve_absolute_offset_edge_cases() {
274 let buffer = b"X"; assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
278 assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 0);
279
280 assert!(resolve_absolute_offset(1, buffer).is_err());
282 assert!(resolve_absolute_offset(-2, buffer).is_err());
283 }
284
285 #[test]
286 fn test_resolve_offset_absolute() {
287 let buffer = b"Test data for offset resolution";
288 let spec = OffsetSpec::Absolute(5);
289
290 let result = resolve_offset(&spec, buffer).unwrap();
291 assert_eq!(result, 5);
292 }
293
294 #[test]
295 fn test_resolve_offset_absolute_negative() {
296 let buffer = b"Test data";
297 let spec = OffsetSpec::Absolute(-4);
298
299 let result = resolve_offset(&spec, buffer).unwrap();
300 assert_eq!(result, 5); }
302
303 #[test]
304 fn test_resolve_offset_from_end() {
305 let buffer = b"Test data";
306 let spec = OffsetSpec::FromEnd(-3);
307
308 let result = resolve_offset(&spec, buffer).unwrap();
309 assert_eq!(result, 6); }
311
312 #[test]
313 fn test_resolve_offset_absolute_out_of_bounds() {
314 let buffer = b"Short";
315 let spec = OffsetSpec::Absolute(10);
316
317 let result = resolve_offset(&spec, buffer);
318 assert!(result.is_err());
319
320 match result.unwrap_err() {
321 LibmagicError::EvaluationError(crate::error::EvaluationError::BufferOverrun {
322 ..
323 }) => {
324 }
326 _ => panic!("Expected EvaluationError with BufferOverrun"),
327 }
328 }
329
330 #[test]
331 fn test_resolve_offset_indirect_not_implemented() {
332 let buffer = b"Test data";
333 let spec = OffsetSpec::Indirect {
334 base_offset: 0,
335 pointer_type: crate::parser::ast::TypeKind::Byte,
336 adjustment: 0,
337 endian: crate::parser::ast::Endianness::Little,
338 };
339
340 let result = resolve_offset(&spec, buffer);
341 assert!(result.is_err());
342
343 match result.unwrap_err() {
344 LibmagicError::EvaluationError(crate::error::EvaluationError::UnsupportedType {
345 type_name,
346 }) => {
347 assert!(type_name.contains("Indirect offsets not yet implemented"));
348 }
349 _ => panic!("Expected EvaluationError with UnsupportedType"),
350 }
351 }
352
353 #[test]
354 fn test_resolve_offset_relative_not_implemented() {
355 let buffer = b"Test data";
356 let spec = OffsetSpec::Relative(4);
357
358 let result = resolve_offset(&spec, buffer);
359 assert!(result.is_err());
360
361 match result.unwrap_err() {
362 LibmagicError::EvaluationError(crate::error::EvaluationError::UnsupportedType {
363 type_name,
364 }) => {
365 assert!(type_name.contains("Relative offsets not yet implemented"));
366 }
367 _ => panic!("Expected EvaluationError with UnsupportedType"),
368 }
369 }
370
371 #[test]
372 fn test_offset_error_display() {
373 let error = OffsetError::BufferOverrun {
374 offset: 10,
375 buffer_len: 5,
376 };
377 let error_str = error.to_string();
378 assert!(error_str.contains("Buffer overrun"));
379 assert!(error_str.contains("10"));
380 assert!(error_str.contains('5'));
381
382 let error = OffsetError::InvalidOffset {
383 reason: "test reason".to_string(),
384 };
385 let error_str = error.to_string();
386 assert!(error_str.contains("Invalid offset"));
387 assert!(error_str.contains("test reason"));
388
389 let error = OffsetError::ArithmeticOverflow;
390 let error_str = error.to_string();
391 assert!(error_str.contains("Arithmetic overflow"));
392 }
393
394 #[test]
395 fn test_large_buffer_offsets() {
396 let large_buffer = vec![0u8; 1024];
398
399 assert_eq!(resolve_absolute_offset(0, &large_buffer).unwrap(), 0);
401 assert_eq!(resolve_absolute_offset(512, &large_buffer).unwrap(), 512);
402 assert_eq!(resolve_absolute_offset(1023, &large_buffer).unwrap(), 1023);
403
404 assert_eq!(resolve_absolute_offset(-1, &large_buffer).unwrap(), 1023);
406 assert_eq!(resolve_absolute_offset(-512, &large_buffer).unwrap(), 512);
407 assert_eq!(resolve_absolute_offset(-1024, &large_buffer).unwrap(), 0);
408
409 assert!(resolve_absolute_offset(1024, &large_buffer).is_err());
411 assert!(resolve_absolute_offset(-1025, &large_buffer).is_err());
412 }
413
414 #[test]
415 fn test_resolve_offset_comprehensive() {
416 let buffer = b"0123456789ABCDEF";
417
418 let test_cases = vec![
420 (OffsetSpec::Absolute(0), 0),
421 (OffsetSpec::Absolute(8), 8),
422 (OffsetSpec::Absolute(15), 15),
423 (OffsetSpec::Absolute(-1), 15),
424 (OffsetSpec::Absolute(-8), 8),
425 (OffsetSpec::Absolute(-16), 0),
426 (OffsetSpec::FromEnd(-1), 15),
427 (OffsetSpec::FromEnd(-8), 8),
428 (OffsetSpec::FromEnd(-16), 0),
429 ];
430
431 for (spec, expected) in test_cases {
432 let result = resolve_offset(&spec, buffer).unwrap();
433 assert_eq!(result, expected, "Failed for spec: {spec:?}");
434 }
435 }
436
437 #[test]
439 fn test_offset_security_edge_cases() {
440 let buffer = b"test";
441
442 let overflow_cases = vec![i64::MAX, i64::MIN, i64::MAX - 1, i64::MIN + 1];
444
445 for offset in overflow_cases {
446 let result = resolve_absolute_offset(offset, buffer);
447 if let Ok(resolved) = result {
449 assert!(
451 resolved < buffer.len(),
452 "Resolved offset {resolved} exceeds buffer length {}",
453 buffer.len()
454 );
455 } else {
456 }
458 }
459 }
460}
461#[test]
462fn test_resolve_absolute_offset_arithmetic_overflow() {
463 let buffer = b"test";
464
465 let result = resolve_absolute_offset(i64::MIN, buffer);
467 assert!(result.is_err());
468
469 match result.unwrap_err() {
470 OffsetError::ArithmeticOverflow => {
471 }
473 _ => panic!("Expected ArithmeticOverflow error"),
474 }
475}