Nothing to see here, move along meow
1use lancer_core::elf;
2
3fn make_minimal_elf(entry: u64, machine: u16, class: u8, segments: &[(u64, u64, u64)]) -> Vec<u8> {
4 let ehdr_size = 64u16;
5 let phdr_size = 56u16;
6 let phdr_offset = ehdr_size as u64;
7 let phdr_count = segments.len() as u16;
8
9 let total_header = ehdr_size as usize + phdr_size as usize * segments.len();
10 let mut buf = vec![0u8; total_header + 4096];
11
12 buf[0..4].copy_from_slice(&[0x7f, b'E', b'L', b'F']);
13 buf[4] = class;
14 buf[5] = 1;
15 buf[16..18].copy_from_slice(&2u16.to_le_bytes());
16 buf[18..20].copy_from_slice(&machine.to_le_bytes());
17 buf[20..24].copy_from_slice(&1u32.to_le_bytes());
18 buf[24..32].copy_from_slice(&entry.to_le_bytes());
19 buf[32..40].copy_from_slice(&phdr_offset.to_le_bytes());
20 buf[52..54].copy_from_slice(&ehdr_size.to_le_bytes());
21 buf[54..56].copy_from_slice(&phdr_size.to_le_bytes());
22 buf[56..58].copy_from_slice(&phdr_count.to_le_bytes());
23
24 segments
25 .iter()
26 .enumerate()
27 .for_each(|(i, &(vaddr, filesz, memsz))| {
28 let off = ehdr_size as usize + phdr_size as usize * i;
29 let data_offset = total_header as u64;
30 buf[off..off + 4].copy_from_slice(&1u32.to_le_bytes());
31 buf[off + 4..off + 8].copy_from_slice(&5u32.to_le_bytes());
32 buf[off + 8..off + 16].copy_from_slice(&data_offset.to_le_bytes());
33 buf[off + 16..off + 24].copy_from_slice(&vaddr.to_le_bytes());
34 buf[off + 24..off + 32].copy_from_slice(&0u64.to_le_bytes());
35 buf[off + 32..off + 40].copy_from_slice(&filesz.to_le_bytes());
36 buf[off + 40..off + 48].copy_from_slice(&memsz.to_le_bytes());
37 buf[off + 48..off + 56].copy_from_slice(&0x1000u64.to_le_bytes());
38 });
39
40 buf
41}
42
43#[test]
44fn parse_valid_elf() {
45 let data = make_minimal_elf(0x401000, 0x3E, 2, &[(0x401000, 256, 512)]);
46 let info = elf::parse(&data).expect("should parse valid ELF");
47 assert_eq!(info.entry, 0x401000);
48 assert_eq!(info.segments.len(), 1);
49 let seg = info.segments.get(0).unwrap();
50 assert_eq!(seg.vaddr, 0x401000);
51 assert_eq!(seg.filesz, 256);
52 assert_eq!(seg.memsz, 512);
53}
54
55#[test]
56fn parse_multiple_segments() {
57 let data = make_minimal_elf(0x1000, 0x3E, 2, &[(0x1000, 128, 256), (0x2000, 64, 128)]);
58 let info = elf::parse(&data).expect("should parse");
59 assert_eq!(info.segments.len(), 2);
60}
61
62#[test]
63fn reject_truncated() {
64 let data = vec![0x7f, b'E', b'L', b'F'];
65 assert_eq!(elf::parse(&data).unwrap_err(), elf::ElfError::Truncated);
66}
67
68#[test]
69fn reject_bad_magic() {
70 let mut data = make_minimal_elf(0x1000, 0x3E, 2, &[]);
71 data[0] = 0x00;
72 assert_eq!(elf::parse(&data).unwrap_err(), elf::ElfError::BadMagic);
73}
74
75#[test]
76fn reject_wrong_machine() {
77 let data = make_minimal_elf(0x1000, 0xB7, 2, &[]);
78 assert_eq!(
79 elf::parse(&data).unwrap_err(),
80 elf::ElfError::UnsupportedFormat
81 );
82}
83
84#[test]
85fn reject_32bit_class() {
86 let data = make_minimal_elf(0x1000, 0x3E, 1, &[]);
87 assert_eq!(
88 elf::parse(&data).unwrap_err(),
89 elf::ElfError::UnsupportedFormat
90 );
91}
92
93#[test]
94fn random_bytes_no_panic() {
95 let data: Vec<u8> = (0..512).map(|i| ((i * 37 + 13) % 256) as u8).collect();
96 let _ = elf::parse(&data);
97}
98
99#[test]
100fn empty_input() {
101 assert_eq!(elf::parse(&[]).unwrap_err(), elf::ElfError::Truncated);
102}
103
104#[test]
105fn header_exact_size_no_segments() {
106 let full = make_minimal_elf(0x1000, 0x3E, 2, &[]);
107 let exact = &full[..64];
108 let info = elf::parse(exact).expect("exact-header-size ELF with 0 phdrs should parse");
109 assert_eq!(info.entry, 0x1000);
110 assert_eq!(info.segments.len(), 0);
111}
112
113#[test]
114fn header_one_byte_short_truncates() {
115 let full = make_minimal_elf(0x1000, 0x3E, 2, &[]);
116 let short = &full[..63];
117 assert_eq!(
118 elf::parse(short).unwrap_err(),
119 elf::ElfError::Truncated,
120 "63 bytes (one short of Elf64Header) must be Truncated"
121 );
122}
123
124#[test]
125fn phdr_ends_at_last_byte() {
126 let segments = &[(0x40_0000, 0, 4096)];
127 let full = make_minimal_elf(0x40_0000, 0x3E, 2, segments);
128 let header_plus_phdr = 64 + 56;
129 let exact = &full[..header_plus_phdr];
130 let info = elf::parse(exact).expect("data ending exactly after phdr should parse");
131 assert_eq!(info.segments.len(), 1);
132}
133
134#[test]
135fn phdr_one_byte_short_skips_segment() {
136 let segments = &[(0x40_0000, 0, 4096)];
137 let full = make_minimal_elf(0x40_0000, 0x3E, 2, segments);
138 let header_plus_phdr = 64 + 56;
139 let short = &full[..header_plus_phdr - 1];
140 let info = elf::parse(short).expect("truncated phdr should be skipped, not fatal");
141 assert_eq!(
142 info.segments.len(),
143 0,
144 "phdr truncated by 1 byte should be silently skipped via filter_map"
145 );
146}