td_shim_interface/td_uefi_pi/
fv.rs

1// Copyright © 2019 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Functions to access UEFI-PI defined `Firmware Volumes`.
16
17use r_efi::efi::Guid;
18use scroll::Pread;
19
20use crate::pi::fv::*;
21
22// Read FV header from slice and validate its integrity
23pub fn read_fv_header(fv_data: &[u8]) -> Option<FirmwareVolumeHeader> {
24    let header: FirmwareVolumeHeader = fv_data.pread(0).ok()?;
25
26    // Do the sanity check for FV header.
27    // Verify the header signature, ffsguid, zerovetor, revision,
28    // fvlength and checksum
29    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
41// Validate Ffs File header
42pub fn validate_ffs_file_header(header: FfsFileHeader) -> bool {
43    // Do the sanity check for Ffs header.
44    // Verify the header integrity,
45    //
46    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: &sections_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        // Align to 4 bytes.
134        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        // Align to 8 bytes.
176        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    ); // {77A2742E-9340-4AC9-8F85-B7B978580021}
201
202    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    ); // {67A2742E-9340-4AC9-8F85-B7B978580021}
210
211    #[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, // Incorrect signature
227            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        // Cannot find fv file type FV_FILETYPE_PEI_CORE
245        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        // Cannot find section type SECTION_PIC
255        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, // Incorrect signature
276            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        // Cannot find this GUID
294        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        // Valide header
305        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        // Fail to verify checksum
314        header.checksum = 0;
315        fv[..size_of::<FirmwareVolumeHeader>()].copy_from_slice(header.as_bytes());
316        assert!(!read_fv_header(&fv).is_some());
317
318        // Fail to verify header length
319        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        // Fail to verify zero vector
325        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}