Skip to content

Commit 7d56fe9

Browse files
committed
Avoid unsafe when decoding
Also helps with big endian platforms like s390x Signed-off-by: Daniel Schaefer <dhs@frame.work>
1 parent 8414b7e commit 7d56fe9

File tree

6 files changed

+126
-81
lines changed

6 files changed

+126
-81
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

framework_lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ no-std-compat = { version = "0.4.1", features = [ "alloc" ] }
3636
hidapi = { version = "2.6.3", features = [ "windows-native" ], optional = true }
3737
rusb = { version = "0.9.4", optional = true }
3838
guid-create = { version = "0.5.0", default-features = false }
39+
zerocopy = { version = "0.8", default-features = false, features = ["derive"] }
3940

4041
[target.'cfg(target_os = "uefi")'.dependencies]
4142
uefi = { version = "0.36.1", features = ["alloc", "global_allocator", "panic_handler", "logger"] }

framework_lib/src/capsule.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use std::prelude::v1::*;
1212

1313
use core::prelude::rust_2021::derive;
14-
use guid_create::CGuid;
14+
use guid_create::{CGuid, GUID};
1515
#[cfg(not(feature = "uefi"))]
1616
use std::fs::File;
1717
#[cfg(not(feature = "uefi"))]
@@ -112,10 +112,22 @@ fn print_capsule_flags(flags: u32) {
112112
}
113113
}
114114

115+
fn read_capsule_header(data: &[u8]) -> EfiCapsuleHeader {
116+
EfiCapsuleHeader {
117+
capsule_guid: CGuid::from(GUID::build_from_components(
118+
u32::from_le_bytes(data[0..4].try_into().unwrap()),
119+
u16::from_le_bytes(data[4..6].try_into().unwrap()),
120+
u16::from_le_bytes(data[6..8].try_into().unwrap()),
121+
data[8..16].try_into().unwrap(),
122+
)),
123+
header_size: u32::from_le_bytes(data[16..20].try_into().unwrap()),
124+
flags: u32::from_le_bytes(data[20..24].try_into().unwrap()),
125+
capsule_image_size: u32::from_le_bytes(data[24..28].try_into().unwrap()),
126+
}
127+
}
128+
115129
pub fn parse_capsule_header(data: &[u8]) -> Option<EfiCapsuleHeader> {
116-
let header_len = std::mem::size_of::<EfiCapsuleHeader>();
117-
let header: EfiCapsuleHeader =
118-
unsafe { std::ptr::read(data[0..header_len].as_ptr() as *const _) };
130+
let header = read_capsule_header(data);
119131
if header.is_valid(data) {
120132
Some(header)
121133
} else {
@@ -141,10 +153,20 @@ pub fn print_capsule_header(header: &EfiCapsuleHeader) {
141153
}
142154

143155
pub fn parse_ux_header(data: &[u8]) -> DisplayCapsule {
144-
let header_len = std::mem::size_of::<DisplayCapsule>();
145-
let header: DisplayCapsule =
146-
unsafe { std::ptr::read(data[0..header_len].as_ptr() as *const _) };
147-
header
156+
let capsule_header = read_capsule_header(data);
157+
let p = &data[std::mem::size_of::<EfiCapsuleHeader>()..];
158+
DisplayCapsule {
159+
capsule_header,
160+
image_payload: DisplayPayload {
161+
version: p[0],
162+
checksum: p[1],
163+
image_type: p[2],
164+
reserved: p[3],
165+
mode: u32::from_le_bytes(p[4..8].try_into().unwrap()),
166+
offset_x: u32::from_le_bytes(p[8..12].try_into().unwrap()),
167+
offset_y: u32::from_le_bytes(p[12..16].try_into().unwrap()),
168+
},
169+
}
148170
}
149171
pub fn print_ux_header(header: &DisplayCapsule) {
150172
let header_len = std::mem::size_of::<DisplayCapsule>();

framework_lib/src/ccgx/binary.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ use alloc::vec::Vec;
3232
use core::prelude::rust_2021::derive;
3333

3434
use crate::ccgx::{AppVersion, BaseVersion};
35+
use zerocopy::byteorder::little_endian::{U16, U32};
36+
use zerocopy::{FromBytes, KnownLayout};
3537

3638
use super::*;
3739

@@ -45,12 +47,12 @@ const SMALL_ROW: usize = 0x80;
4547
const LARGE_ROW: usize = 0x100;
4648

4749
#[repr(C, packed)]
48-
#[derive(Debug, Copy, Clone)]
50+
#[derive(FromBytes, KnownLayout, Debug, Copy, Clone)]
4951
struct VersionInfo {
50-
base_version: u32,
51-
app_version: u32,
52-
silicon_id: u16,
53-
silicon_family: u16,
52+
base_version: U32,
53+
app_version: U32,
54+
silicon_id: U16,
55+
silicon_family: U16,
5456
}
5557

5658
pub const CCG5_PD_LEN: usize = 0x20_000;
@@ -147,15 +149,13 @@ fn read_version(
147149
trace!("First row of firmware: {:X?}", data);
148150
let data = &data[FW_VERSION_OFFSET..];
149151

150-
let version_len = std::mem::size_of::<VersionInfo>();
151-
let version_info: VersionInfo =
152-
unsafe { std::ptr::read(data[0..version_len].as_ptr() as *const _) };
152+
let (version_info, _) = VersionInfo::read_from_prefix(data).ok()?;
153153

154-
let base_version = BaseVersion::from(version_info.base_version);
155-
let app_version = AppVersion::from(version_info.app_version);
154+
let base_version = BaseVersion::from(version_info.base_version.get());
155+
let app_version = AppVersion::from(version_info.app_version.get());
156156

157-
let fw_silicon_id = version_info.silicon_id;
158-
let fw_silicon_family = version_info.silicon_family;
157+
let fw_silicon_id = version_info.silicon_id.get();
158+
let fw_silicon_family = version_info.silicon_family.get();
159159

160160
Some(PdFirmware {
161161
silicon_id: fw_silicon_id,

framework_lib/src/ccgx/mod.rs

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use alloc::vec::Vec;
99
use core::prelude::rust_2021::derive;
1010
use num_derive::FromPrimitive;
1111
use std::fmt;
12+
use zerocopy::byteorder::little_endian::{U16, U32};
13+
use zerocopy::{FromBytes, KnownLayout};
1214

1315
use crate::chromium_ec::{CrosEc, EcResult};
1416
use crate::smbios;
@@ -33,18 +35,18 @@ const METADATA_MAGIC: u16 = u16::from_le_bytes([b'Y', b'C']); // CY (Cypress)
3335
const CCG8_METADATA_MAGIC: u16 = u16::from_le_bytes([b'F', b'I']); // IF (Infineon)
3436

3537
#[repr(C, packed)]
36-
#[derive(Debug, Copy, Clone)]
38+
#[derive(FromBytes, KnownLayout, Debug, Copy, Clone)]
3739
struct CyAcdMetadata {
3840
/// Offset 00: Single Byte FW Checksum
3941
_fw_checksum: u8,
4042
/// Offset 01: FW Entry Address
4143
_fw_entry: u32,
4244
/// Offset 05: Last Flash row of Bootloader or previous firmware
43-
boot_last_row: u16,
45+
boot_last_row: U16,
4446
/// Offset 07: Reserved
4547
_reserved1: [u8; 2],
4648
/// Offset 09: Size of Firmware
47-
fw_size: u32,
49+
fw_size: U32,
4850
/// Offset 0D: Reserved
4951
_reserved2: [u8; 3],
5052
/// Offset 10: Creator specific field
@@ -56,7 +58,7 @@ struct CyAcdMetadata {
5658
/// Offset 14: Creator specific field
5759
_boot_app_id: u16,
5860
/// Offset 16: Metadata Valid field. Valid if contains "CY"
59-
metadata_valid: u16,
61+
metadata_valid: U16,
6062
/// Offset 18: Creator specific field
6163
_fw_version: u32,
6264
/// Offset 1C: Boot sequence number field. Boot-loader will load the valid
@@ -67,12 +69,12 @@ struct CyAcdMetadata {
6769

6870
// TODO: Would be nice to check the checksums
6971
#[repr(C, packed)]
70-
#[derive(Debug, Copy, Clone)]
72+
#[derive(FromBytes, KnownLayout, Debug, Copy, Clone)]
7173
struct CyAcd2Metadata {
7274
/// Offset 00: App Firmware Start
73-
fw_start: u32,
75+
fw_start: U32,
7476
/// Offset 04: App Firmware Size
75-
fw_size: u32,
77+
fw_size: U32,
7678
/// Offset 08: Boot wait time
7779
_boot_app_id: u16,
7880
/// Offset 0A: Last Flash row of Bootloader or previous firmware
@@ -89,9 +91,9 @@ struct CyAcd2Metadata {
8991
/// Offset 18: Reserved
9092
_reserved_1: [u32; 15],
9193
/// Offset 54: Version of the metadata structure
92-
metadata_version: u16,
94+
metadata_version: U16,
9395
/// Offset 56: Metadata Valid field. Valid if contains ASCII "IF"
94-
metadata_valid: u16,
96+
metadata_valid: U16,
9597
/// Offset 58: App Fw CRC32 checksum
9698
_fw_crc32: u32,
9799
/// Offset 5C: Reserved
@@ -267,12 +269,13 @@ pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult<PdVersions> {
267269

268270
fn parse_metadata_ccg3(buffer: &[u8]) -> Option<(u32, u32)> {
269271
let buffer = &buffer[CCG3_METADATA_OFFSET..];
270-
let metadata_len = std::mem::size_of::<CyAcdMetadata>();
271-
let metadata: CyAcdMetadata =
272-
unsafe { std::ptr::read(buffer[0..metadata_len].as_ptr() as *const _) };
272+
let (metadata, _) = CyAcdMetadata::read_from_prefix(buffer).ok()?;
273273
trace!("Metadata: {:X?}", metadata);
274-
if metadata.metadata_valid == METADATA_MAGIC {
275-
Some((1 + metadata.boot_last_row as u32, metadata.fw_size))
274+
if metadata.metadata_valid.get() == METADATA_MAGIC {
275+
Some((
276+
1 + metadata.boot_last_row.get() as u32,
277+
metadata.fw_size.get(),
278+
))
276279
} else {
277280
None
278281
}
@@ -281,26 +284,25 @@ fn parse_metadata_ccg3(buffer: &[u8]) -> Option<(u32, u32)> {
281284
//fn parse_metadata(buffer: &[u8; 256]) -> Option<(u32, u32)> {
282285
fn parse_metadata_cyacd(buffer: &[u8]) -> Option<(u32, u32)> {
283286
let buffer = &buffer[METADATA_OFFSET..];
284-
let metadata_len = std::mem::size_of::<CyAcdMetadata>();
285-
let metadata: CyAcdMetadata =
286-
unsafe { std::ptr::read(buffer[0..metadata_len].as_ptr() as *const _) };
287+
let (metadata, _) = CyAcdMetadata::read_from_prefix(buffer).ok()?;
287288
trace!("Metadata: {:X?}", metadata);
288-
if metadata.metadata_valid == METADATA_MAGIC {
289-
Some((1 + metadata.boot_last_row as u32, metadata.fw_size))
289+
if metadata.metadata_valid.get() == METADATA_MAGIC {
290+
Some((
291+
1 + metadata.boot_last_row.get() as u32,
292+
metadata.fw_size.get(),
293+
))
290294
} else {
291295
None
292296
}
293297
}
294298

295299
fn parse_metadata_cyacd2(buffer: &[u8]) -> Option<(u32, u32)> {
296300
let buffer = &buffer[CCG8_METADATA_OFFSET..];
297-
let metadata_len = std::mem::size_of::<CyAcd2Metadata>();
298-
let metadata: CyAcd2Metadata =
299-
unsafe { std::ptr::read(buffer[0..metadata_len].as_ptr() as *const _) };
301+
let (metadata, _) = CyAcd2Metadata::read_from_prefix(buffer).ok()?;
300302
trace!("Metadata: {:X?}", metadata);
301-
if metadata.metadata_valid == CCG8_METADATA_MAGIC {
302-
if metadata.metadata_version == 1 {
303-
Some((metadata.fw_start, metadata.fw_size))
303+
if metadata.metadata_valid.get() == CCG8_METADATA_MAGIC {
304+
if metadata.metadata_version.get() == 1 {
305+
Some((metadata.fw_start.get(), metadata.fw_size.get()))
304306
} else {
305307
println!("Unknown CCG8 metadata version");
306308
None

framework_lib/src/ec_binary.rs

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,21 @@ const EC_RW_VER_OFFSET_ZEPHYR: usize = 0x40140;
1616
pub const EC_LEN: usize = 0x8_0000;
1717

1818
use regex;
19+
use zerocopy::byteorder::little_endian::U32;
20+
use zerocopy::{FromBytes, KnownLayout};
1921

2022
#[cfg(feature = "uefi")]
2123
use core::prelude::rust_2021::derive;
2224

2325
// Defined in EC code as `struct image_data` in include/cros_version.h
24-
#[derive(Clone, Copy, Debug)]
26+
#[derive(FromBytes, KnownLayout, Clone, Copy, Debug)]
2527
#[repr(C, packed)]
2628
struct _ImageVersionData {
27-
cookie1: u32,
29+
cookie1: U32,
2830
version: [u8; 32],
29-
size: u32,
30-
rollback_version: u32,
31-
cookie2: u32,
31+
size: U32,
32+
rollback_version: U32,
33+
cookie2: U32,
3234
}
3335
/// Version Information about an EC FW binary
3436
#[derive(Debug, PartialEq)]
@@ -79,8 +81,8 @@ fn parse_ec_version(data: &_ImageVersionData) -> Option<ImageVersionData> {
7981
.trim_end_matches(char::from(0));
8082
Some(ImageVersionData {
8183
version: version.to_string(),
82-
size: data.size,
83-
rollback_version: data.rollback_version,
84+
size: data.size.get(),
85+
rollback_version: data.rollback_version.get(),
8486
details: parse_ec_version_str(version)?,
8587
})
8688
}
@@ -151,18 +153,17 @@ pub fn read_ec_version(data: &[u8], ro: bool) -> Option<ImageVersionData> {
151153
} else {
152154
EC_RW_VER_OFFSET
153155
};
154-
if data.len() < offset + core::mem::size_of::<_ImageVersionData>() {
155-
return None;
156-
}
157-
let v: _ImageVersionData = unsafe { std::ptr::read(data[offset..].as_ptr() as *const _) };
158-
if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 {
159-
debug!("Failed to find legacy Cookie 1. Found: {:X?}", {
160-
v.cookie1
161-
});
162-
} else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 {
163-
debug!("Failed to find legacy Cookie 2. Found: {:X?}", {
164-
v.cookie2
165-
});
156+
let (v, _) = _ImageVersionData::read_from_prefix(data.get(offset..)?).ok()?;
157+
if v.cookie1.get() != CROS_EC_IMAGE_DATA_COOKIE1 {
158+
debug!(
159+
"Failed to find legacy Cookie 1. Found: {:X?}",
160+
v.cookie1.get()
161+
);
162+
} else if v.cookie2.get() != CROS_EC_IMAGE_DATA_COOKIE2 {
163+
debug!(
164+
"Failed to find legacy Cookie 2. Found: {:X?}",
165+
v.cookie2.get()
166+
);
166167
} else {
167168
return parse_ec_version(&v);
168169
}
@@ -173,19 +174,17 @@ pub fn read_ec_version(data: &[u8], ro: bool) -> Option<ImageVersionData> {
173174
} else {
174175
EC_RW_VER_OFFSET_ZEPHYR
175176
};
176-
if data.len() < offset_zephyr + core::mem::size_of::<_ImageVersionData>() {
177-
return None;
178-
}
179-
let v: _ImageVersionData =
180-
unsafe { std::ptr::read(data[offset_zephyr..].as_ptr() as *const _) };
181-
if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 {
182-
debug!("Failed to find Zephyr Cookie 1. Found: {:X?}", {
183-
v.cookie1
184-
});
185-
} else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 {
186-
debug!("Failed to find Zephyr Cookie 2. Found: {:X?}", {
187-
v.cookie2
188-
});
177+
let (v, _) = _ImageVersionData::read_from_prefix(data.get(offset_zephyr..)?).ok()?;
178+
if v.cookie1.get() != CROS_EC_IMAGE_DATA_COOKIE1 {
179+
debug!(
180+
"Failed to find Zephyr Cookie 1. Found: {:X?}",
181+
v.cookie1.get()
182+
);
183+
} else if v.cookie2.get() != CROS_EC_IMAGE_DATA_COOKIE2 {
184+
debug!(
185+
"Failed to find Zephyr Cookie 2. Found: {:X?}",
186+
v.cookie2.get()
187+
);
189188
} else {
190189
return parse_ec_version(&v);
191190
}
@@ -204,11 +203,11 @@ mod tests {
204203
fn can_parse() {
205204
let ver_chars: &[u8] = b"hx30_v0.0.1-7a61a89\0\0\0\0\0\0\0\0\0\0\0\0\0";
206205
let data = _ImageVersionData {
207-
cookie1: CROS_EC_IMAGE_DATA_COOKIE1,
206+
cookie1: U32::new(CROS_EC_IMAGE_DATA_COOKIE1),
208207
version: ver_chars.try_into().unwrap(),
209-
size: 2868,
210-
rollback_version: 0,
211-
cookie2: CROS_EC_IMAGE_DATA_COOKIE1,
208+
size: U32::new(2868),
209+
rollback_version: U32::new(0),
210+
cookie2: U32::new(CROS_EC_IMAGE_DATA_COOKIE1),
212211
};
213212
debug_assert_eq!(
214213
parse_ec_version(&data),

0 commit comments

Comments
 (0)