44//! This module writes Flattened Devicetree blobs as defined here:
55//! <https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html>
66
7+ use alloc:: borrow:: Cow ;
78use alloc:: collections:: BTreeMap ;
89use alloc:: ffi:: CString ;
910use alloc:: string:: String ;
1011use alloc:: vec:: Vec ;
1112use core:: cmp:: { Ord , Ordering } ;
1213use core:: convert:: TryInto ;
14+ use core:: ffi:: CStr ;
1315use core:: fmt;
1416use core:: mem:: size_of_val;
1517#[ cfg( feature = "std" ) ]
@@ -215,20 +217,15 @@ fn node_name_valid_first_char(c: char) -> bool {
215217
216218// Check if `name` is a valid property name.
217219// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
218- fn property_name_valid ( name : & str ) -> bool {
219- if name. is_empty ( ) || name. len ( ) > PROPERTY_NAME_MAX_LEN {
220- return false ;
221- }
222-
223- if name. contains ( |c : char | !property_name_valid_char ( c) ) {
224- return false ;
225- }
226-
227- true
220+ fn property_name_valid ( name : & CStr ) -> bool {
221+ let name_bytes = name. to_bytes ( ) ;
222+ !( name_bytes. is_empty ( )
223+ || name_bytes. len ( ) > PROPERTY_NAME_MAX_LEN
224+ || name_bytes. iter ( ) . any ( |& c| !property_name_valid_byte ( c) ) )
228225}
229226
230- fn property_name_valid_char ( c : char ) -> bool {
231- matches ! ( c, '0' ..='9' | 'a' ..='z' | 'A' ..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-' )
227+ fn property_name_valid_byte ( c : u8 ) -> bool {
228+ matches ! ( c, b '0' ..=b '9' | b 'a' ..=b 'z' | b 'A' ..=b 'Z' | b ',' | b '.' | b '_' | b '+' | b '?' | b '#' | b '-')
232229}
233230
234231/// Handle to an open node created by `FdtWriter::begin_node`.
@@ -390,19 +387,19 @@ impl FdtWriter {
390387
391388 // Find an existing instance of a string `s`, or add it to the strings block.
392389 // Returns the offset into the strings block.
393- fn intern_string ( & mut self , s : CString ) -> Result < u32 > {
394- if let Some ( off) = self . string_offsets . get ( & s) {
395- Ok ( * off)
390+ fn intern_string ( & mut self , s : Cow < ' _ , CStr > ) -> Result < u32 > {
391+ Ok ( if let Some ( off) = self . string_offsets . get ( & * s) {
392+ * off
396393 } else {
397394 let off = self
398395 . strings
399396 . len ( )
400397 . try_into ( )
401398 . map_err ( |_| Error :: TotalSizeTooLarge ) ?;
402399 self . strings . extend_from_slice ( s. to_bytes_with_nul ( ) ) ;
403- self . string_offsets . insert ( s, off) ;
404- Ok ( off)
405- }
400+ self . string_offsets . insert ( s. into_owned ( ) , off) ;
401+ off
402+ } )
406403 }
407404
408405 /// Write a property.
@@ -422,7 +419,7 @@ impl FdtWriter {
422419
423420 let name_cstr = CString :: new ( name) . map_err ( |_| Error :: InvalidString ) ?;
424421
425- if !property_name_valid ( name ) {
422+ if !property_name_valid ( & name_cstr ) {
426423 return Err ( Error :: InvalidPropertyName ) ;
427424 }
428425
@@ -431,11 +428,45 @@ impl FdtWriter {
431428 . try_into ( )
432429 . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
433430
434- let nameoff = self . intern_string ( name_cstr) ?;
431+ let nameoff = self . intern_string ( name_cstr. into ( ) ) ?;
432+ self . append_u32 ( FDT_PROP ) ;
433+ self . append_u32 ( len) ;
434+ self . append_u32 ( nameoff) ;
435+ self . data . extend_from_slice ( val) ;
436+ self . align ( 4 ) ;
437+ Ok ( ( ) )
438+ }
439+
440+ /// Write a property, but add a NUL byte after `val` and make sure that `val` doesn't contain NUL bytes.
441+ /// Otherwise identical to [`FdtWriter::property`].
442+ ///
443+ /// The purpose of this is to avoid unnecessary intermediate allocations.
444+ fn property_and_nul ( & mut self , name : Cow < ' _ , CStr > , val : & [ u8 ] ) -> Result < ( ) > {
445+ if self . node_ended {
446+ return Err ( Error :: PropertyAfterEndNode ) ;
447+ }
448+
449+ if self . node_depth == 0 {
450+ return Err ( Error :: PropertyBeforeBeginNode ) ;
451+ }
452+
453+ if !property_name_valid ( & name) {
454+ return Err ( Error :: InvalidPropertyName ) ;
455+ }
456+ if val. contains ( & 0x00 ) {
457+ return Err ( Error :: InvalidString ) ;
458+ }
459+
460+ let len = ( val. len ( ) + 1 )
461+ . try_into ( )
462+ . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
463+
464+ let nameoff = self . intern_string ( name) ?;
435465 self . append_u32 ( FDT_PROP ) ;
436466 self . append_u32 ( len) ;
437467 self . append_u32 ( nameoff) ;
438468 self . data . extend_from_slice ( val) ;
469+ self . data . push ( 0x00 ) ;
439470 self . align ( 4 ) ;
440471 Ok ( ( ) )
441472 }
@@ -447,16 +478,22 @@ impl FdtWriter {
447478
448479 /// Write a string property.
449480 pub fn property_string ( & mut self , name : & str , val : & str ) -> Result < ( ) > {
450- let cstr_value = CString :: new ( val) . map_err ( |_| Error :: InvalidString ) ?;
451- self . property ( name, cstr_value. to_bytes_with_nul ( ) )
481+ self . property_and_nul (
482+ CString :: new ( name) . map_err ( |_| Error :: InvalidString ) ?. into ( ) ,
483+ val. as_bytes ( ) ,
484+ )
452485 }
453486
454487 /// Write a stringlist property.
455488 pub fn property_string_list ( & mut self , name : & str , values : Vec < String > ) -> Result < ( ) > {
456489 let mut bytes = Vec :: new ( ) ;
457490 for s in values {
458- let cstr = CString :: new ( s) . map_err ( |_| Error :: InvalidString ) ?;
459- bytes. extend_from_slice ( cstr. to_bytes_with_nul ( ) ) ;
491+ let s = s. as_bytes ( ) ;
492+ if s. contains ( & 0x00 ) {
493+ return Err ( Error :: InvalidString ) ;
494+ }
495+ bytes. extend_from_slice ( s) ;
496+ bytes. push ( 0x00 ) ;
460497 }
461498 self . property ( name, & bytes)
462499 }
@@ -1119,17 +1156,18 @@ mod tests {
11191156
11201157 #[ test]
11211158 fn test_property_name_valid ( ) {
1122- assert ! ( property_name_valid( "abcdef" ) ) ;
1123- assert ! ( property_name_valid( "01234" ) ) ;
1124- assert ! ( property_name_valid( "azAZ09,._+?#-" ) ) ;
1125- assert ! ( property_name_valid( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) ) ;
1159+ let f = |x : & [ u8 ] | property_name_valid ( CStr :: from_bytes_with_nul ( x) . unwrap ( ) ) ;
1160+ assert ! ( f( b"abcdef\0 " ) ) ;
1161+ assert ! ( f( b"01234\0 " ) ) ;
1162+ assert ! ( f( b"azAZ09,._+?#-\0 " ) ) ;
1163+ assert ! ( f( b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0 " ) ) ;
11261164
11271165 // Name contains invalid characters.
1128- assert ! ( !property_name_valid ( "abc!def" ) ) ;
1129- assert ! ( !property_name_valid ( "abc@1234" ) ) ;
1166+ assert ! ( !f ( b "abc!def\0 ") ) ;
1167+ assert ! ( !f ( b "abc@1234\0 ") ) ;
11301168
11311169 // Name too long.
1132- assert ! ( !property_name_valid ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ) ) ;
1170+ assert ! ( !f ( b "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0 ") ) ;
11331171 }
11341172
11351173 #[ test]
0 commit comments