diff --git a/.gitignore b/.gitignore index a9d37c5..acbde85 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +kcov_output* diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 04594a7..a298c00 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 88.0, + "coverage_score": 87.8, "exclude_path": "", "crate_features": "long_running_test" } diff --git a/src/writer.rs b/src/writer.rs index c3d0974..ca8bee0 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -41,6 +41,8 @@ pub enum Error { InvalidNodeName, /// Invalid property name. InvalidPropertyName, + /// Node depth exceeds FDT_MAX_NODE_DEPTH + NodeDepthTooLarge, } impl fmt::Display for Error { @@ -65,6 +67,7 @@ impl fmt::Display for Error { } Error::InvalidNodeName => write!(f, "Invalid node name"), Error::InvalidPropertyName => write!(f, "Invalid property name"), + Error::NodeDepthTooLarge => write!(f, "Node depth exceeds FDT_MAX_NODE_DEPTH"), } } } @@ -77,6 +80,8 @@ pub type Result = std::result::Result; const FDT_HEADER_SIZE: usize = 40; const FDT_VERSION: u32 = 17; const FDT_LAST_COMP_VERSION: u32 = 16; +/// The same max depth as in the Linux kernel. +const FDT_MAX_NODE_DEPTH: usize = 64; /// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (DTB). #[derive(Debug)] @@ -331,6 +336,10 @@ impl FdtWriter { /// /// `name` - name of the node; must not contain any NUL bytes. pub fn begin_node(&mut self, name: &str) -> Result { + if self.node_depth >= FDT_MAX_NODE_DEPTH { + return Err(Error::NodeDepthTooLarge); + } + let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; // The unit adddress part of the node name, if present, is not fully validated // since the exact requirements depend on the bus mapping. @@ -341,6 +350,8 @@ impl FdtWriter { self.append_u32(FDT_BEGIN_NODE); self.data.extend(name_cstr.to_bytes_with_nul()); self.align(4); + // This can not overflow due to the `if` at the beginning of the function + // where the current depth is checked against FDT_MAX_NODE_DEPTH. self.node_depth += 1; self.node_ended = false; Ok(FdtWriterNode { @@ -1095,4 +1106,16 @@ mod tests { // Name too long. assert!(!property_name_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); } + + #[test] + fn depth_overflow() { + let mut fdt = FdtWriter::new().unwrap(); + for _ in 1..=FDT_MAX_NODE_DEPTH { + fdt.begin_node("test").unwrap(); + } + assert_eq!( + fdt.begin_node("test").unwrap_err(), + Error::NodeDepthTooLarge + ); + } }