Skip to main content

winreg_format/
bytes.rs

1//! Bounds-checked little-endian readers (panic-free on crafted offsets).
2//!
3//! Registry cells are untrusted input; an out-of-range read yields a zero value
4//! instead of an out-of-bounds slice panic, keeping the parser panic-free even
5//! if a caller's bounds guard is ever broken (defence-in-depth).
6
7/// Read a little-endian `u32` at `off`, or `0` if `off..off+4` is out of range.
8#[must_use]
9pub fn le_u32(data: &[u8], off: usize) -> u32 {
10    data.get(off..off.wrapping_add(4))
11        .and_then(|s| <[u8; 4]>::try_from(s).ok())
12        .map_or(0, u32::from_le_bytes)
13}
14
15/// Read 4 raw bytes at `off`, or `[0; 4]` if `off..off+4` is out of range.
16#[must_use]
17pub fn read4(data: &[u8], off: usize) -> [u8; 4] {
18    data.get(off..off.wrapping_add(4))
19        .and_then(|s| <[u8; 4]>::try_from(s).ok())
20        .unwrap_or([0; 4])
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    #[test]
28    fn le_u32_reads_and_guards() {
29        assert_eq!(le_u32(&[1, 0, 0, 0], 0), 1);
30        assert_eq!(le_u32(&[1, 2, 3], 0), 0);
31        assert_eq!(le_u32(&[0; 4], usize::MAX), 0);
32    }
33
34    #[test]
35    fn read4_reads_and_guards() {
36        assert_eq!(read4(&[1, 2, 3, 4, 5], 1), [2, 3, 4, 5]);
37        assert_eq!(read4(&[1, 2], 0), [0; 4]);
38        assert_eq!(read4(&[0; 4], usize::MAX), [0; 4]);
39    }
40}