td_shim_interface/td_uefi_pi/
fv.rs1use r_efi::efi::Guid;
18use scroll::Pread;
19
20use crate::pi::fv::*;
21
22pub fn read_fv_header(fv_data: &[u8]) -> Option<FirmwareVolumeHeader> {
24 let header: FirmwareVolumeHeader = fv_data.pread(0).ok()?;
25
26 if header.signature != FVH_SIGNATURE
30 || header.header_length as usize > fv_data.len()
31 || header.zero_vector != [0u8; 16]
32 || header.revision != FVH_REVISION
33 || header.fv_length != fv_data.len() as u64
34 || !header.validate_checksum()
35 {
36 return None;
37 }
38 Some(header)
39}
40
41pub fn validate_ffs_file_header(header: FfsFileHeader) -> bool {
43 if !header.validate_checksum() {
47 return false;
48 }
49 true
50}
51
52pub fn get_image_from_fv(
53 fv_data: &[u8],
54 fv_file_type: FvFileType,
55 section_type: SectionType,
56) -> Option<&[u8]> {
57 let fv_header = read_fv_header(fv_data)?;
58
59 let files = Files::parse(fv_data, fv_header.header_length as usize)?;
60 for (file_header, file_data) in files {
61 if !validate_ffs_file_header(file_header) {
62 return None;
63 }
64 if file_header.r#type == fv_file_type {
65 return get_image_from_sections(file_data, section_type);
66 }
67 }
68
69 None
70}
71
72pub fn get_file_from_fv(
73 fv_data: &[u8],
74 fv_file_type: FvFileType,
75 file_name: Guid,
76) -> Option<&[u8]> {
77 let fv_header = read_fv_header(fv_data)?;
78
79 let files = Files::parse(fv_data, fv_header.header_length as usize)?;
80 for (file_header, file_data) in files {
81 if !validate_ffs_file_header(file_header) {
82 return None;
83 }
84 if file_header.r#type == fv_file_type && &file_header.name == file_name.as_bytes() {
85 return Some(file_data);
86 }
87 }
88
89 None
90}
91
92fn get_image_from_sections(sections_data: &[u8], section_type: SectionType) -> Option<&[u8]> {
93 let sections = Sections::parse(sections_data, 0)?;
94
95 for (section_header, section_data) in sections {
96 if section_header.r#type == section_type {
97 return Some(section_data);
98 }
99 }
100
101 None
102}
103
104struct Sections<'a> {
105 buffer: &'a [u8],
106}
107
108impl<'a> Sections<'a> {
109 pub fn parse(sections_buffer: &'a [u8], offset: usize) -> Option<Self> {
110 if offset >= sections_buffer.len() {
111 return None;
112 }
113
114 Some(Sections {
115 buffer: §ions_buffer[offset..],
116 })
117 }
118}
119
120impl<'a> Iterator for Sections<'a> {
121 type Item = (CommonSectionHeader, &'a [u8]);
122
123 fn next(&mut self) -> Option<Self::Item> {
124 const HEADER_SIZE: usize = core::mem::size_of::<CommonSectionHeader>();
125 let header: CommonSectionHeader = self.buffer.pread(0).ok()?;
126 let section_size = header.size[0] as usize
127 + ((header.size[1] as usize) << 8)
128 + ((header.size[2] as usize) << 16);
129 section_size.checked_sub(HEADER_SIZE)?;
130 self.buffer.len().checked_sub(section_size)?;
131 let buf = &self.buffer[HEADER_SIZE..section_size];
132
133 let section_size = (section_size + 3) & !3;
135 if section_size < self.buffer.len() {
136 self.buffer = &self.buffer[section_size..];
137 } else {
138 self.buffer = &self.buffer[0..0];
139 }
140
141 Some((header, buf))
142 }
143}
144
145struct Files<'a> {
146 buffer: &'a [u8],
147}
148
149impl<'a> Files<'a> {
150 pub fn parse(fv_buffer: &'a [u8], fv_header_size: usize) -> Option<Self> {
151 if fv_header_size >= fv_buffer.len() {
152 return None;
153 }
154
155 Some(Files {
156 buffer: &fv_buffer[fv_header_size..],
157 })
158 }
159}
160
161impl<'a> Iterator for Files<'a> {
162 type Item = (FfsFileHeader, &'a [u8]);
163
164 fn next(&mut self) -> Option<Self::Item> {
165 const HEADER_SIZE: usize = core::mem::size_of::<FfsFileHeader>();
166
167 let header: FfsFileHeader = self.buffer.pread(0).ok()?;
168 let data_size = header.size[0] as usize
169 + ((header.size[1] as usize) << 8)
170 + ((header.size[2] as usize) << 16);
171 data_size.checked_sub(HEADER_SIZE)?;
172 self.buffer.len().checked_sub(data_size)?;
173 let buf = &self.buffer[HEADER_SIZE..data_size];
174
175 let data_size = (data_size + 7) & !7;
177 if data_size < self.buffer.len() {
178 self.buffer = &self.buffer[data_size..];
179 } else {
180 self.buffer = &self.buffer[0..0];
181 }
182
183 Some((header, buf))
184 }
185}
186
187#[cfg(test)]
188mod test {
189 use core::mem::size_of;
190
191 use super::*;
192
193 const TEST_GUID1: Guid = Guid::from_fields(
194 0x77a2742e,
195 0x9340,
196 0x4ac9,
197 0x8f,
198 0x85,
199 &[0xb7, 0xb9, 0x78, 0x58, 0x0, 0x21],
200 ); const TEST_GUID2: Guid = Guid::from_fields(
203 0x67a2742e,
204 0x9340,
205 0x4ac9,
206 0x8f,
207 0x85,
208 &[0xb7, 0xb9, 0x78, 0x58, 0x0, 0x21],
209 ); #[test]
212 fn test_get_image_from_fv() {
213 let bytes = include_bytes!("../../fuzz/seeds/payload_parser/fv_buffer");
214
215 let res = get_image_from_fv(bytes, FV_FILETYPE_DXE_CORE, SECTION_PE32);
216
217 assert_ne!(res, None);
218 }
219
220 #[test]
221 fn test_get_image_from_fv_with_wrong_signature() {
222 let bytes = FirmwareVolumeHeader {
223 zero_vector: [0; 16],
224 file_system_guid: [0; 16],
225 fv_length: 0,
226 signature: 0x4856465F, attributes: 0,
228 header_length: 0,
229 checksum: 0,
230 ext_header_offset: 0,
231 reserved: 0,
232 revision: 0,
233 };
234
235 let res = get_image_from_fv(bytes.as_bytes(), FV_FILETYPE_DXE_CORE, SECTION_PE32);
236
237 assert_eq!(res, None);
238 }
239
240 #[test]
241 fn test_get_image_from_fv_with_wrong_fv_file_type() {
242 let bytes = include_bytes!("../../fuzz/seeds/payload_parser/fv_buffer");
243
244 let res = get_image_from_fv(bytes, FV_FILETYPE_PEI_CORE, SECTION_PE32);
246
247 assert_eq!(res, None);
248 }
249
250 #[test]
251 fn test_get_image_from_fv_with_wrong_section_type() {
252 let bytes = include_bytes!("../../fuzz/seeds/payload_parser/fv_buffer");
253
254 let res = get_image_from_fv(bytes, FV_FILETYPE_DXE_CORE, SECTION_PIC);
256
257 assert_eq!(res, None);
258 }
259
260 #[test]
261 fn test_get_file_from_fv() {
262 let bytes = include_bytes!("../../fuzz/seeds/cfv_parser/cfv");
263
264 let res = get_file_from_fv(bytes, FV_FILETYPE_RAW, TEST_GUID1);
265
266 assert_ne!(res, None);
267 }
268
269 #[test]
270 fn test_get_file_from_fv_with_wrong_signature() {
271 let bytes = FirmwareVolumeHeader {
272 zero_vector: [0; 16],
273 file_system_guid: [0; 16],
274 fv_length: 0,
275 signature: 0x4856465F, attributes: 0,
277 header_length: 0,
278 checksum: 0,
279 ext_header_offset: 0,
280 reserved: 0,
281 revision: 0,
282 };
283
284 let res = get_file_from_fv(bytes.as_bytes(), FV_FILETYPE_RAW, TEST_GUID1);
285
286 assert_eq!(res, None);
287 }
288
289 #[test]
290 fn test_get_file_from_fv_with_wrong_guid() {
291 let bytes = include_bytes!("../../fuzz/seeds/cfv_parser/cfv");
292
293 let res = get_file_from_fv(bytes, FV_FILETYPE_RAW, TEST_GUID2);
295
296 assert_eq!(res, None);
297 }
298
299 #[test]
300 fn test_read_fvh() {
301 let mut fv = [0u8; 0x100];
302 let mut header = FirmwareVolumeHeader::default();
303
304 header.revision = FVH_REVISION;
306 header.signature = FVH_SIGNATURE;
307 header.header_length = size_of::<FirmwareVolumeHeader>() as u16;
308 header.fv_length = 0x100;
309 header.update_checksum();
310 fv[..size_of::<FirmwareVolumeHeader>()].copy_from_slice(header.as_bytes());
311 assert!(read_fv_header(&fv).is_some());
312
313 header.checksum = 0;
315 fv[..size_of::<FirmwareVolumeHeader>()].copy_from_slice(header.as_bytes());
316 assert!(!read_fv_header(&fv).is_some());
317
318 header.header_length = 0x200;
320 header.update_checksum();
321 fv[..size_of::<FirmwareVolumeHeader>()].copy_from_slice(header.as_bytes());
322 assert!(!read_fv_header(&fv).is_some());
323
324 header.header_length = size_of::<FirmwareVolumeHeader>() as u16;
326 header.zero_vector = [0x10u8; 16];
327 header.update_checksum();
328 fv[..size_of::<FirmwareVolumeHeader>()].copy_from_slice(header.as_bytes());
329 assert!(!read_fv_header(&fv).is_some());
330 }
331}