From 9967a7ab804667fabd8f9fca45b8ec19638322c7 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 26 Sep 2026 18:13:20 -0700 Subject: [PATCH] fix(chainload): release LoadedImage before starting the image The chainload action held the child image's LoadedImage protocol open exclusively while calling start_image. Any image that opens its own LoadedImage protocol exclusively, such as another uefi-rs application or Sprout itself, then failed with ACCESS_DENIED. Scope the protocol to setting the load options. --- crates/boot/src/actions/chainload.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/boot/src/actions/chainload.rs b/crates/boot/src/actions/chainload.rs index 2998ea5..ffe874c 100644 --- a/crates/boot/src/actions/chainload.rs +++ b/crates/boot/src/actions/chainload.rs @@ -32,11 +32,6 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat // It will determine if the image needs to be loaded via the shim or can be loaded directly. let image = ImageLoader::load(request)?; - // Open the LoadedImage protocol of the image to chainload. - let mut loaded_image_protocol = - uefi::boot::open_protocol_exclusive::(*image.handle()) - .context("unable to open loaded image protocol")?; - // Stamp and combine the options to pass to the image. let options = combine_options(context.stamp_iter(configuration.options.iter())); @@ -54,12 +49,21 @@ pub fn chainload(context: Rc, configuration: &ChainloadConfigurat 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); + // Open the LoadedImage protocol of the image to chainload to pass the load options. + // This is done in a block to release the protocol before the image is started, as the + // image may itself need to open its LoadedImage protocol exclusively. + { + let mut loaded_image_protocol = + uefi::boot::open_protocol_exclusive::(*image.handle()) + .context("unable to open loaded image protocol")?; + + // 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.