8
8
//
9
9
//! Helper for creating valid kernel command line strings.
10
10
11
+ use std:: ffi:: CString ;
11
12
use std:: fmt;
12
13
use std:: result;
13
14
@@ -16,6 +17,8 @@ use vm_memory::{Address, GuestAddress, GuestUsize};
16
17
/// The error type for command line building operations.
17
18
#[ derive( Debug , PartialEq , Eq ) ]
18
19
pub enum Error {
20
+ /// Null terminator identified in the command line.
21
+ NullTerminator ,
19
22
/// Operation would have resulted in a non-printable ASCII character.
20
23
InvalidAscii ,
21
24
/// Invalid device passed to the kernel command line builder.
@@ -35,6 +38,9 @@ pub enum Error {
35
38
impl fmt:: Display for Error {
36
39
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
37
40
match * self {
41
+ Error :: NullTerminator => {
42
+ write ! ( f, "Null terminator detected in the command line structure." )
43
+ }
38
44
Error :: InvalidAscii => write ! ( f, "String contains a non-printable ASCII character." ) ,
39
45
Error :: InvalidDevice ( ref dev) => write ! (
40
46
f,
@@ -156,11 +162,9 @@ impl Cmdline {
156
162
///
157
163
/// ```rust
158
164
/// # use linux_loader::cmdline::*;
159
- /// # use std::ffi::CString;
160
165
/// let mut cl = Cmdline::new(100);
161
166
/// cl.insert("foo", "bar");
162
- /// let cl_cstring = CString::new(cl).unwrap();
163
- /// assert_eq!(cl_cstring.to_str().unwrap(), "foo=bar");
167
+ /// assert_eq!(cl.as_cstring().unwrap().as_bytes_with_nul(), b"foo=bar\0");
164
168
/// ```
165
169
pub fn insert < T : AsRef < str > > ( & mut self , key : T , val : T ) -> Result < ( ) > {
166
170
let k = key. as_ref ( ) ;
@@ -248,18 +252,23 @@ impl Cmdline {
248
252
Ok ( ( ) )
249
253
}
250
254
251
- /// Returns the string representation of the command line without the nul terminator.
255
+ /// Returns a C compatible representation of the command line
256
+ /// The Linux kernel expects a null terminated cmdline according to the source:
257
+ /// https://elixir.bootlin.com/linux/v5.10.139/source/kernel/params.c#L179
258
+ ///
259
+ /// To get bytes of the cmdline to be written in guest's memory (including the
260
+ /// null terminator) from this representation, use CString::as_bytes_with_nul()
252
261
///
253
262
/// # Examples
254
263
///
255
264
/// ```rust
256
265
/// # use linux_loader::cmdline::*;
257
266
/// let mut cl = Cmdline::new(10);
258
267
/// cl.insert_str("foobar");
259
- /// assert_eq!(cl.as_str(), "foobar");
268
+ /// assert_eq!(cl.as_cstring().unwrap().as_bytes_with_nul(), b "foobar\0 ");
260
269
/// ```
261
- pub fn as_str ( & self ) -> & str {
262
- self . line . as_str ( )
270
+ pub fn as_cstring ( & self ) -> Result < CString > {
271
+ CString :: new ( self . line . to_string ( ) ) . map_err ( |_| Error :: NullTerminator )
263
272
}
264
273
265
274
/// Adds a virtio MMIO device to the kernel command line.
@@ -356,9 +365,12 @@ mod tests {
356
365
#[ test]
357
366
fn test_insert_hello_world ( ) {
358
367
let mut cl = Cmdline :: new ( 100 ) ;
359
- assert_eq ! ( cl. as_str ( ) , " ") ;
368
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b" \0 ") ;
360
369
assert ! ( cl. insert( "hello" , "world" ) . is_ok( ) ) ;
361
- assert_eq ! ( cl. as_str( ) , "hello=world" ) ;
370
+ assert_eq ! (
371
+ cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) ,
372
+ b"hello=world\0 "
373
+ ) ;
362
374
363
375
let s = CString :: new ( cl) . expect ( "failed to create CString from Cmdline" ) ;
364
376
assert_eq ! ( s, CString :: new( "hello=world" ) . unwrap( ) ) ;
@@ -369,7 +381,10 @@ mod tests {
369
381
let mut cl = Cmdline :: new ( 100 ) ;
370
382
assert ! ( cl. insert( "hello" , "world" ) . is_ok( ) ) ;
371
383
assert ! ( cl. insert( "foo" , "bar" ) . is_ok( ) ) ;
372
- assert_eq ! ( cl. as_str( ) , "hello=world foo=bar" ) ;
384
+ assert_eq ! (
385
+ cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) ,
386
+ b"hello=world foo=bar\0 "
387
+ ) ;
373
388
}
374
389
375
390
#[ test]
@@ -379,7 +394,7 @@ mod tests {
379
394
assert_eq ! ( cl. insert( "a" , "b " ) , Err ( Error :: HasSpace ) ) ;
380
395
assert_eq ! ( cl. insert( "a " , "b " ) , Err ( Error :: HasSpace ) ) ;
381
396
assert_eq ! ( cl. insert( " a" , "b" ) , Err ( Error :: HasSpace ) ) ;
382
- assert_eq ! ( cl. as_str ( ) , " ") ;
397
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b" \0 ") ;
383
398
}
384
399
385
400
#[ test]
@@ -390,25 +405,28 @@ mod tests {
390
405
assert_eq ! ( cl. insert( "a=" , "b " ) , Err ( Error :: HasEquals ) ) ;
391
406
assert_eq ! ( cl. insert( "=a" , "b" ) , Err ( Error :: HasEquals ) ) ;
392
407
assert_eq ! ( cl. insert( "a" , "=b" ) , Err ( Error :: HasEquals ) ) ;
393
- assert_eq ! ( cl. as_str ( ) , " ") ;
408
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b" \0 ") ;
394
409
}
395
410
396
411
#[ test]
397
412
fn test_insert_emoji ( ) {
398
413
let mut cl = Cmdline :: new ( 100 ) ;
399
414
assert_eq ! ( cl. insert( "heart" , "💖" ) , Err ( Error :: InvalidAscii ) ) ;
400
415
assert_eq ! ( cl. insert( "💖" , "love" ) , Err ( Error :: InvalidAscii ) ) ;
401
- assert_eq ! ( cl. as_str ( ) , " ") ;
416
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b" \0 ") ;
402
417
}
403
418
404
419
#[ test]
405
420
fn test_insert_string ( ) {
406
421
let mut cl = Cmdline :: new ( 13 ) ;
407
- assert_eq ! ( cl. as_str ( ) , " ") ;
422
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b" \0 ") ;
408
423
assert ! ( cl. insert_str( "noapic" ) . is_ok( ) ) ;
409
- assert_eq ! ( cl. as_str ( ) , "noapic" ) ;
424
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b "noapic\0 ") ;
410
425
assert ! ( cl. insert_str( "nopci" ) . is_ok( ) ) ;
411
- assert_eq ! ( cl. as_str( ) , "noapic nopci" ) ;
426
+ assert_eq ! (
427
+ cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) ,
428
+ b"noapic nopci\0 "
429
+ ) ;
412
430
}
413
431
414
432
#[ test]
@@ -420,7 +438,7 @@ mod tests {
420
438
assert ! ( cl. insert( "a" , "b" ) . is_ok( ) ) ;
421
439
assert_eq ! ( cl. insert( "a" , "b" ) , Err ( Error :: TooLarge ) ) ;
422
440
assert_eq ! ( cl. insert_str( "a" ) , Err ( Error :: TooLarge ) ) ;
423
- assert_eq ! ( cl. as_str ( ) , "a=b" ) ;
441
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b "a=b\0 ") ;
424
442
425
443
let mut cl = Cmdline :: new ( 10 ) ;
426
444
assert ! ( cl. insert( "ab" , "ba" ) . is_ok( ) ) ; // adds 5 length
@@ -445,25 +463,37 @@ mod tests {
445
463
. add_virtio_mmio_device( 1 , GuestAddress ( 0 ) , 1 , None )
446
464
. is_ok( ) ) ;
447
465
let mut expected_str = "virtio_mmio.device=1@0x0:1" . to_string ( ) ;
448
- assert_eq ! ( cl. as_str( ) , & expected_str) ;
466
+ assert_eq ! (
467
+ cl. as_cstring( ) . unwrap( ) ,
468
+ CString :: new( expected_str. as_bytes( ) ) . unwrap( )
469
+ ) ;
449
470
450
471
assert ! ( cl
451
472
. add_virtio_mmio_device( 2 << 10 , GuestAddress ( 0x100 ) , 2 , None )
452
473
. is_ok( ) ) ;
453
474
expected_str. push_str ( " virtio_mmio.device=2K@0x100:2" ) ;
454
- assert_eq ! ( cl. as_str( ) , & expected_str) ;
475
+ assert_eq ! (
476
+ cl. as_cstring( ) . unwrap( ) ,
477
+ CString :: new( expected_str. as_bytes( ) ) . unwrap( )
478
+ ) ;
455
479
456
480
assert ! ( cl
457
481
. add_virtio_mmio_device( 3 << 20 , GuestAddress ( 0x1000 ) , 3 , None )
458
482
. is_ok( ) ) ;
459
483
expected_str. push_str ( " virtio_mmio.device=3M@0x1000:3" ) ;
460
- assert_eq ! ( cl. as_str( ) , & expected_str) ;
484
+ assert_eq ! (
485
+ cl. as_cstring( ) . unwrap( ) ,
486
+ CString :: new( expected_str. as_bytes( ) ) . unwrap( )
487
+ ) ;
461
488
462
489
assert ! ( cl
463
490
. add_virtio_mmio_device( 4 << 30 , GuestAddress ( 0x0001_0000 ) , 4 , Some ( 42 ) )
464
491
. is_ok( ) ) ;
465
492
expected_str. push_str ( " virtio_mmio.device=4G@0x10000:4:42" ) ;
466
- assert_eq ! ( cl. as_str( ) , & expected_str) ;
493
+ assert_eq ! (
494
+ cl. as_cstring( ) . unwrap( ) ,
495
+ CString :: new( expected_str. as_bytes( ) ) . unwrap( )
496
+ ) ;
467
497
}
468
498
469
499
#[ test]
@@ -484,11 +514,14 @@ mod tests {
484
514
485
515
let mut cl = Cmdline :: new ( 100 ) ;
486
516
assert ! ( cl. insert_multiple( "foo" , & [ "bar" ] ) . is_ok( ) ) ;
487
- assert_eq ! ( cl. as_str ( ) , "foo=bar" ) ;
517
+ assert_eq ! ( cl. as_cstring ( ) . unwrap ( ) . as_bytes_with_nul ( ) , b "foo=bar\0 ") ;
488
518
489
519
let mut cl = Cmdline :: new ( 100 ) ;
490
520
assert ! ( cl. insert_multiple( "foo" , & [ "bar" , "baz" ] ) . is_ok( ) ) ;
491
- assert_eq ! ( cl. as_str( ) , "foo=bar,baz" ) ;
521
+ assert_eq ! (
522
+ cl. as_cstring( ) . unwrap( ) . as_bytes_with_nul( ) ,
523
+ b"foo=bar,baz\0 "
524
+ ) ;
492
525
}
493
526
494
527
#[ test]
0 commit comments