1
1
use crate :: tag_type:: { Tag , TagIter , TagType } ;
2
2
use core:: fmt:: { Debug , Formatter } ;
3
+ use core:: str:: Utf8Error ;
3
4
4
5
/// This tag indicates to the kernel what boot module was loaded along with
5
6
/// the kernel image, and where it can be found.
@@ -10,25 +11,24 @@ pub struct ModuleTag {
10
11
size : u32 ,
11
12
mod_start : u32 ,
12
13
mod_end : u32 ,
13
- /// Begin of the command line string.
14
+ /// Null-terminated UTF-8 string
14
15
cmdline_str : u8 ,
15
16
}
16
17
17
18
impl ModuleTag {
18
- // The multiboot specification defines the module str as valid utf-8 (zero terminated string),
19
- // therefore this function produces defined behavior
20
- /// Get the cmdline of the module. If the GRUB configuration contains
21
- /// `module2 /foobar/some_boot_module --test cmdline-option`, then this method
19
+ /// Returns the cmdline of the module.
20
+ /// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
21
+ /// is invalid or the bootloader doesn't follow the spec.
22
+ ///
23
+ /// For example: If the GRUB configuration contains
24
+ /// `module2 /foobar/some_boot_module --test cmdline-option` then this method
22
25
/// will return `--test cmdline-option`.
23
- pub fn cmdline ( & self ) -> & str {
26
+ pub fn cmdline ( & self ) -> Result < & str , Utf8Error > {
24
27
use core:: { mem, slice, str} ;
28
+ // strlen without null byte
25
29
let strlen = self . size as usize - mem:: size_of :: < ModuleTag > ( ) ;
26
- unsafe {
27
- str:: from_utf8_unchecked ( slice:: from_raw_parts (
28
- & self . cmdline_str as * const u8 ,
29
- strlen,
30
- ) )
31
- }
30
+ let bytes = unsafe { slice:: from_raw_parts ( ( & self . cmdline_str ) as * const u8 , strlen) } ;
31
+ str:: from_utf8 ( bytes)
32
32
}
33
33
34
34
/// Start address of the module.
@@ -89,3 +89,39 @@ impl<'a> Debug for ModuleIter<'a> {
89
89
list. finish ( )
90
90
}
91
91
}
92
+
93
+ #[ cfg( test) ]
94
+ mod tests {
95
+ use crate :: TagType ;
96
+
97
+ const MSG : & str = "hello" ;
98
+
99
+ /// Returns the tag structure in bytes in native endian format.
100
+ fn get_bytes ( ) -> std:: vec:: Vec < u8 > {
101
+ // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string
102
+ // 4 bytes mod_start + 4 bytes mod_end
103
+ let size = ( 4 + 4 + 4 + 4 + MSG . as_bytes ( ) . len ( ) + 1 ) as u32 ;
104
+ [
105
+ & ( ( TagType :: Module as u32 ) . to_ne_bytes ( ) ) ,
106
+ & size. to_ne_bytes ( ) ,
107
+ & 0_u32 . to_ne_bytes ( ) ,
108
+ & 0_u32 . to_ne_bytes ( ) ,
109
+ MSG . as_bytes ( ) ,
110
+ // Null Byte
111
+ & [ 0 ] ,
112
+ ]
113
+ . iter ( )
114
+ . flat_map ( |bytes| bytes. iter ( ) )
115
+ . copied ( )
116
+ . collect ( )
117
+ }
118
+
119
+ /// Tests to parse a string with a terminating null byte from the tag (as the spec defines).
120
+ #[ test]
121
+ fn test_parse_str ( ) {
122
+ let tag = get_bytes ( ) ;
123
+ let tag = unsafe { tag. as_ptr ( ) . cast :: < super :: ModuleTag > ( ) . as_ref ( ) . unwrap ( ) } ;
124
+ assert_eq ! ( { tag. typ } , TagType :: Module ) ;
125
+ assert_eq ! ( tag. cmdline( ) . expect( "must be valid UTF-8" ) , MSG ) ;
126
+ }
127
+ }
0 commit comments