fix(chainload): ensure that load options are always set, even if it is to an empty string

This commit is contained in:
2025-11-02 17:47:36 -05:00
parent 524d0871f3
commit 3febca5797

View File

@@ -53,30 +53,25 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
let options = let options =
utils::combine_options(configuration.options.iter().map(|item| context.stamp(item))); utils::combine_options(configuration.options.iter().map(|item| context.stamp(item)));
// Pass the options to the image, if any are provided. // Pass the load options to the image.
// The holder must drop at the end of this function to ensure the options are not leaked, // If no options are provided, the resulting string will be empty.
// and the holder here ensures it outlives the if block here, as a pointer has to be // The options are pinned and boxed to ensure that they are valid for the lifetime of this
// passed to the image. // function, which ensures the lifetime of the options for the image runtime.
// SAFETY: The options outlive the usage of the image, and the image is not used after this. let options = Box::pin(
let mut options_holder: Option<Box<CString16>> = None; CString16::try_from(&options[..])
if !options.is_empty() { .context("unable to convert chainloader options to CString16")?,
let options = Box::new( );
CString16::try_from(&options[..])
.context("unable to convert chainloader options to CString16")?,
);
if options.num_bytes() > u32::MAX as usize { if options.num_bytes() > u32::MAX as usize {
bail!("chainloader options too large"); bail!("chainloader options too large");
} }
// SAFETY: option size is checked to validate it is safe to pass. // SAFETY: option size is checked to validate it is safe to pass.
// Additionally, the pointer is allocated and retained on heap, which makes // Additionally, the pointer is allocated and retained on heap, which makes
// passing the `options` pointer safe to the next image. // passing the `options` pointer safe to the next image.
unsafe { unsafe {
loaded_image_protocol loaded_image_protocol
.set_load_options(options.as_ptr() as *const u8, options.num_bytes() as u32); .set_load_options(options.as_ptr() as *const u8, options.num_bytes() as u32);
}
options_holder = Some(options);
} }
// Stamp the initrd path, if provided. // Stamp the initrd path, if provided.
@@ -118,8 +113,9 @@ pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfigurat
// Assert there was no error starting the image. // Assert there was no error starting the image.
result.context("unable to start image")?; result.context("unable to start image")?;
// Explicitly drop the option holder to clarify the lifetime.
drop(options_holder); // Explicitly drop the options to clarify the lifetime.
drop(options);
// Return control to sprout. // Return control to sprout.
Ok(()) Ok(())