1pub fn pt_interp_slot(data: &[u8]) -> Option<(usize, usize)> {
12 if data.len() < 64 || data[0..4] != *b"\x7fELF" {
13 return None;
14 }
15 let class = data[4];
16 let (e_phoff, e_phentsize, e_phnum) = match class {
17 2 => (
18 u64::from_le_bytes(data.get(32..40)?.try_into().ok()?) as usize,
19 u16::from_le_bytes(data.get(54..56)?.try_into().ok()?) as usize,
20 u16::from_le_bytes(data.get(56..58)?.try_into().ok()?) as usize,
21 ),
22 1 => (
23 u32::from_le_bytes(data.get(28..32)?.try_into().ok()?) as usize,
24 u16::from_le_bytes(data.get(42..44)?.try_into().ok()?) as usize,
25 u16::from_le_bytes(data.get(44..46)?.try_into().ok()?) as usize,
26 ),
27 _ => return None,
28 };
29 let min_phentsize = if class == 2 { 56 } else { 32 };
32 if e_phentsize < min_phentsize {
33 return None;
34 }
35 for i in 0..e_phnum {
36 let off = e_phoff.checked_add(i.checked_mul(e_phentsize)?)?;
37 let end = off.checked_add(e_phentsize)?;
38 if end > data.len() {
39 break;
40 }
41 let p_type = u32::from_le_bytes(data.get(off..off + 4)?.try_into().ok()?);
42 if p_type != 3 {
43 continue;
44 }
45 return match class {
46 2 => Some((
47 u64::from_le_bytes(data.get(off + 8..off + 16)?.try_into().ok()?) as usize,
48 u64::from_le_bytes(data.get(off + 32..off + 40)?.try_into().ok()?) as usize,
49 )),
50 1 => Some((
51 u32::from_le_bytes(data.get(off + 4..off + 8)?.try_into().ok()?) as usize,
52 u32::from_le_bytes(data.get(off + 16..off + 20)?.try_into().ok()?) as usize,
53 )),
54 _ => None,
55 };
56 }
57 None
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 fn elf64_with_interp(interp: &str) -> Vec<u8> {
67 let phoff = 64usize;
68 let phentsize = 56usize;
69 let interp_off = phoff + phentsize;
70 let mut v = vec![0u8; interp_off + interp.len() + 1];
71 v[0..4].copy_from_slice(b"\x7fELF");
72 v[4] = 2; v[32..40].copy_from_slice(&(phoff as u64).to_le_bytes());
74 v[54..56].copy_from_slice(&(phentsize as u16).to_le_bytes());
75 v[56..58].copy_from_slice(&1u16.to_le_bytes()); v[phoff..phoff + 4].copy_from_slice(&3u32.to_le_bytes()); v[phoff + 8..phoff + 16].copy_from_slice(&(interp_off as u64).to_le_bytes());
78 v[phoff + 32..phoff + 40].copy_from_slice(&((interp.len() + 1) as u64).to_le_bytes());
79 v[interp_off..interp_off + interp.len()].copy_from_slice(interp.as_bytes());
80 v
81 }
82
83 #[test]
84 fn locates_the_interp_slot() {
85 let name = "/lib64/ld-linux-x86-64.so.2";
86 let elf = elf64_with_interp(name);
87 let (off, sz) = pt_interp_slot(&elf).expect("slot");
88 assert_eq!(off, 120);
89 assert_eq!(sz, name.len() + 1);
90 assert_eq!(&elf[off..off + sz - 1], name.as_bytes());
91 }
92
93 #[test]
94 fn malformed_returns_none_without_panic() {
95 assert!(pt_interp_slot(b"not an elf").is_none());
96 assert!(pt_interp_slot(&[]).is_none());
97
98 let mut short = vec![0u8; 64];
100 short[0..4].copy_from_slice(b"\x7fELF");
101 short[4] = 2;
102 short[56..58].copy_from_slice(&9999u16.to_le_bytes());
103 short[32..40].copy_from_slice(&(1u64 << 40).to_le_bytes());
104 assert!(pt_interp_slot(&short).is_none());
105 }
106
107 #[test]
108 fn undersized_phentsize_is_rejected() {
109 let mut elf = elf64_with_interp("/lib/ld.so");
112 elf[54..56].copy_from_slice(&8u16.to_le_bytes());
113 assert!(pt_interp_slot(&elf).is_none());
114 }
115}