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}")]
11 BufferOverrun {
12 offset: usize,
14 buffer_len: usize,
16 },
17
18 #[error("Invalid offset: {reason}")]
20 InvalidOffset {
21 reason: String,
23 },
24
25 #[error("Arithmetic overflow in offset calculation")]
27 ArithmeticOverflow,
28}
29
30pub fn resolve_absolute_offset(offset: i64, buffer: &[u8]) -> Result<usize, OffsetError> {
72 let buffer_len = buffer.len();
73
74 if offset >= 0 {
75 let abs_offset = usize::try_from(offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
77 if abs_offset >= buffer_len {
78 return Err(OffsetError::BufferOverrun {
79 offset: abs_offset,
80 buffer_len,
81 });
82 }
83 Ok(abs_offset)
84 } else {
85 if offset == i64::MIN {
88 return Err(OffsetError::ArithmeticOverflow);
89 }
90
91 let offset_from_end =
92 usize::try_from(-offset).map_err(|_| OffsetError::ArithmeticOverflow)?;
93
94 if offset_from_end > buffer_len {
95 return Err(OffsetError::BufferOverrun {
96 offset: buffer_len.saturating_sub(offset_from_end),
97 buffer_len,
98 });
99 }
100
101 let resolved_offset = buffer_len - offset_from_end;
103 Ok(resolved_offset)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_resolve_absolute_offset_positive() {
113 let buffer = b"Hello, World!";
114
115 assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
117 assert_eq!(resolve_absolute_offset(1, buffer).unwrap(), 1);
118 assert_eq!(resolve_absolute_offset(7, buffer).unwrap(), 7);
119 assert_eq!(resolve_absolute_offset(12, buffer).unwrap(), 12); }
121
122 #[test]
123 fn test_resolve_absolute_offset_negative() {
124 let buffer = b"Hello, World!";
125
126 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); }
131
132 #[test]
133 fn test_resolve_absolute_offset_out_of_bounds_positive() {
134 let buffer = b"Hello";
135
136 let result = resolve_absolute_offset(5, buffer);
138 assert!(result.is_err());
139
140 match result.unwrap_err() {
141 OffsetError::BufferOverrun { offset, buffer_len } => {
142 assert_eq!(offset, 5);
143 assert_eq!(buffer_len, 5);
144 }
145 _ => panic!("Expected BufferOverrun error"),
146 }
147
148 let result = resolve_absolute_offset(100, buffer);
150 assert!(result.is_err());
151 }
152
153 #[test]
154 fn test_resolve_absolute_offset_out_of_bounds_negative() {
155 let buffer = b"Hi";
156
157 let result = resolve_absolute_offset(-3, buffer);
159 assert!(result.is_err());
160
161 match result.unwrap_err() {
162 OffsetError::BufferOverrun { .. } => {
163 }
165 _ => panic!("Expected BufferOverrun error"),
166 }
167
168 let result = resolve_absolute_offset(-100, buffer);
170 assert!(result.is_err());
171 }
172
173 #[test]
174 fn test_resolve_absolute_offset_empty_buffer() {
175 let buffer = b"";
176
177 assert!(resolve_absolute_offset(0, buffer).is_err());
179 assert!(resolve_absolute_offset(1, buffer).is_err());
180 assert!(resolve_absolute_offset(-1, buffer).is_err());
181 }
182
183 #[test]
184 fn test_resolve_absolute_offset_edge_cases() {
185 let buffer = b"X"; assert_eq!(resolve_absolute_offset(0, buffer).unwrap(), 0);
189 assert_eq!(resolve_absolute_offset(-1, buffer).unwrap(), 0);
190
191 assert!(resolve_absolute_offset(1, buffer).is_err());
193 assert!(resolve_absolute_offset(-2, buffer).is_err());
194 }
195
196 #[test]
197 fn test_large_buffer_offsets() {
198 let large_buffer = vec![0u8; 1024];
200
201 assert_eq!(resolve_absolute_offset(0, &large_buffer).unwrap(), 0);
203 assert_eq!(resolve_absolute_offset(512, &large_buffer).unwrap(), 512);
204 assert_eq!(resolve_absolute_offset(1023, &large_buffer).unwrap(), 1023);
205
206 assert_eq!(resolve_absolute_offset(-1, &large_buffer).unwrap(), 1023);
208 assert_eq!(resolve_absolute_offset(-512, &large_buffer).unwrap(), 512);
209 assert_eq!(resolve_absolute_offset(-1024, &large_buffer).unwrap(), 0);
210
211 assert!(resolve_absolute_offset(1024, &large_buffer).is_err());
213 assert!(resolve_absolute_offset(-1025, &large_buffer).is_err());
214 }
215
216 #[test]
218 fn test_offset_security_edge_cases() {
219 let buffer = b"test";
220
221 let overflow_cases = vec![i64::MAX, i64::MIN, i64::MAX - 1, i64::MIN + 1];
223
224 for offset in overflow_cases {
225 let result = resolve_absolute_offset(offset, buffer);
226 if let Ok(resolved) = result {
228 assert!(
230 resolved < buffer.len(),
231 "Resolved offset {resolved} exceeds buffer length {}",
232 buffer.len()
233 );
234 } else {
235 }
237 }
238 }
239
240 #[test]
241 fn test_offset_error_display() {
242 let error = OffsetError::BufferOverrun {
243 offset: 10,
244 buffer_len: 5,
245 };
246 let error_str = error.to_string();
247 assert!(error_str.contains("Buffer overrun"));
248 assert!(error_str.contains("10"));
249 assert!(error_str.contains('5'));
250
251 let error = OffsetError::InvalidOffset {
252 reason: "test reason".to_string(),
253 };
254 let error_str = error.to_string();
255 assert!(error_str.contains("Invalid offset"));
256 assert!(error_str.contains("test reason"));
257
258 let error = OffsetError::ArithmeticOverflow;
259 let error_str = error.to_string();
260 assert!(error_str.contains("Arithmetic overflow"));
261 }
262
263 #[test]
264 fn test_resolve_absolute_offset_arithmetic_overflow() {
265 let buffer = b"test";
266
267 let result = resolve_absolute_offset(i64::MIN, buffer);
269 assert!(result.is_err());
270
271 match result.unwrap_err() {
272 OffsetError::ArithmeticOverflow => {
273 }
275 _ => panic!("Expected ArithmeticOverflow error"),
276 }
277 }
278}