@@ -10,6 +10,7 @@ use alloc::string::String;
1010use alloc:: vec:: Vec ;
1111use core:: cmp:: { Ord , Ordering } ;
1212use core:: convert:: TryInto ;
13+ use core:: ffi:: CStr ;
1314use core:: fmt;
1415use core:: mem:: size_of_val;
1516#[ cfg( feature = "std" ) ]
@@ -216,21 +217,28 @@ fn node_name_valid_first_char(c: char) -> bool {
216217// Check if `name` is a valid property name.
217218// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
218219fn 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- }
220+ !( name. is_empty ( )
221+ || name. len ( ) > PROPERTY_NAME_MAX_LEN
222+ || name. contains ( |c| !property_name_valid_char ( c) ) )
223+ }
226224
227- true
225+ // Check if `name` is a valid property name.
226+ // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names
227+ fn property_cstr_name_valid ( name : & CStr ) -> bool {
228+ let name_bytes = name. to_bytes ( ) ;
229+ !( name_bytes. is_empty ( )
230+ || name_bytes. len ( ) > PROPERTY_NAME_MAX_LEN
231+ || name_bytes. iter ( ) . any ( |& c| !property_name_valid_byte ( c) ) )
228232}
229233
230234fn property_name_valid_char ( c : char ) -> bool {
231235 matches ! ( c, '0' ..='9' | 'a' ..='z' | 'A' ..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-' )
232236}
233237
238+ fn property_name_valid_byte ( c : u8 ) -> bool {
239+ matches ! ( c, b'0' ..=b'9' | b'a' ..=b'z' | b'A' ..=b'Z' | b',' | b'.' | b'_' | b'+' | b'?' | b'#' | b'-' )
240+ }
241+
234242/// Handle to an open node created by `FdtWriter::begin_node`.
235243///
236244/// This must be passed back to `FdtWriter::end_node` to close the nodes.
@@ -390,19 +398,19 @@ impl FdtWriter {
390398
391399 // Find an existing instance of a string `s`, or add it to the strings block.
392400 // 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)
401+ fn intern_string ( & mut self , s : & CStr ) -> Result < u32 > {
402+ Ok ( if let Some ( off) = self . string_offsets . get ( s) {
403+ * off
396404 } else {
397405 let off = self
398406 . strings
399407 . len ( )
400408 . try_into ( )
401409 . map_err ( |_| Error :: TotalSizeTooLarge ) ?;
402410 self . strings . extend_from_slice ( s. to_bytes_with_nul ( ) ) ;
403- self . string_offsets . insert ( s, off) ;
404- Ok ( off)
405- }
411+ self . string_offsets . insert ( s. to_owned ( ) , off) ;
412+ off
413+ } )
406414 }
407415
408416 /// Write a property.
@@ -431,11 +439,45 @@ impl FdtWriter {
431439 . try_into ( )
432440 . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
433441
434- let nameoff = self . intern_string ( name_cstr) ?;
442+ let nameoff = self . intern_string ( & name_cstr) ?;
443+ self . append_u32 ( FDT_PROP ) ;
444+ self . append_u32 ( len) ;
445+ self . append_u32 ( nameoff) ;
446+ self . data . extend_from_slice ( val) ;
447+ self . align ( 4 ) ;
448+ Ok ( ( ) )
449+ }
450+
451+ /// Write a property, but add a NUL byte after `val` and make sure that `val` doesn't contain NUL bytes.
452+ /// Otherwise identical to [`FdtWriter::property`].
453+ ///
454+ /// The purpose of this is to avoid unnecessary intermediate allocations.
455+ fn property_and_nul ( & mut self , name : & CStr , val : & [ u8 ] ) -> Result < ( ) > {
456+ if self . node_ended {
457+ return Err ( Error :: PropertyAfterEndNode ) ;
458+ }
459+
460+ if self . node_depth == 0 {
461+ return Err ( Error :: PropertyBeforeBeginNode ) ;
462+ }
463+
464+ if !property_cstr_name_valid ( name) {
465+ return Err ( Error :: InvalidPropertyName ) ;
466+ }
467+ if val. contains ( & 0x00 ) {
468+ return Err ( Error :: InvalidString ) ;
469+ }
470+
471+ let len = ( val. len ( ) + 1 )
472+ . try_into ( )
473+ . map_err ( |_| Error :: PropertyValueTooLarge ) ?;
474+
475+ let nameoff = self . intern_string ( name) ?;
435476 self . append_u32 ( FDT_PROP ) ;
436477 self . append_u32 ( len) ;
437478 self . append_u32 ( nameoff) ;
438479 self . data . extend_from_slice ( val) ;
480+ self . data . push ( 0x00 ) ;
439481 self . align ( 4 ) ;
440482 Ok ( ( ) )
441483 }
@@ -447,16 +489,22 @@ impl FdtWriter {
447489
448490 /// Write a string property.
449491 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 ( ) )
492+ self . property_and_nul (
493+ & CString :: new ( name) . map_err ( |_| Error :: InvalidString ) ?,
494+ val. as_bytes ( ) ,
495+ )
452496 }
453497
454498 /// Write a stringlist property.
455499 pub fn property_string_list ( & mut self , name : & str , values : Vec < String > ) -> Result < ( ) > {
456500 let mut bytes = Vec :: new ( ) ;
457501 for s in values {
458- let cstr = CString :: new ( s) . map_err ( |_| Error :: InvalidString ) ?;
459- bytes. extend_from_slice ( cstr. to_bytes_with_nul ( ) ) ;
502+ let s = s. as_bytes ( ) ;
503+ if s. contains ( & 0x00 ) {
504+ return Err ( Error :: InvalidString ) ;
505+ }
506+ bytes. extend_from_slice ( s) ;
507+ bytes. push ( 0x00 ) ;
460508 }
461509 self . property ( name, & bytes)
462510 }
0 commit comments