From 3febca579785f6977dcb04b64dc55dad087f3c68 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sun, 2 Nov 2025 17:47:36 -0500 Subject: [PATCH] fix(chainload): ensure that load options are always set, even if it is to an empty string --- src/actions/chainload.rs | 44 ++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/actions/chainload.rs b/src/actions/chainload.rs index c23985d..43186bf 100644 --- a/src/actions/chainload.rs +++ b/src/actions/chainload.rs @@ -53,30 +53,25 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat let options = utils::combine_options(configuration.options.iter().map(|item| context.stamp(item))); - // Pass the options to the image, if any are provided. - // The holder must drop at the end of this function to ensure the options are not leaked, - // and the holder here ensures it outlives the if block here, as a pointer has to be - // passed to the image. - // SAFETY: The options outlive the usage of the image, and the image is not used after this. - let mut options_holder: Option> = None; - if !options.is_empty() { - let options = Box::new( - CString16::try_from(&options[..]) - .context("unable to convert chainloader options to CString16")?, - ); + // Pass the load options to the image. + // If no options are provided, the resulting string will be empty. + // The options are pinned and boxed to ensure that they are valid for the lifetime of this + // function, which ensures the lifetime of the options for the image runtime. + let options = Box::pin( + CString16::try_from(&options[..]) + .context("unable to convert chainloader options to CString16")?, + ); - if options.num_bytes() > u32::MAX as usize { - bail!("chainloader options too large"); - } + if options.num_bytes() > u32::MAX as usize { + bail!("chainloader options too large"); + } - // SAFETY: option size is checked to validate it is safe to pass. - // Additionally, the pointer is allocated and retained on heap, which makes - // passing the `options` pointer safe to the next image. - unsafe { - loaded_image_protocol - .set_load_options(options.as_ptr() as *const u8, options.num_bytes() as u32); - } - options_holder = Some(options); + // SAFETY: option size is checked to validate it is safe to pass. + // Additionally, the pointer is allocated and retained on heap, which makes + // passing the `options` pointer safe to the next image. + unsafe { + loaded_image_protocol + .set_load_options(options.as_ptr() as *const u8, options.num_bytes() as u32); } // Stamp the initrd path, if provided. @@ -118,8 +113,9 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat // Assert there was no error starting the 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. Ok(())