diff --git a/crates/boot/src/context.rs b/crates/boot/src/context.rs index a2a8588..2b54a47 100644 --- a/crates/boot/src/context.rs +++ b/crates/boot/src/context.rs @@ -14,6 +14,11 @@ use uefi::proto::device_path::DevicePath; /// The maximum number of iterations that can be performed in [SproutContext::finalize]. const CONTEXT_FINALIZE_ITERATION_LIMIT: usize = 100; +/// The maximum length of a value produced by [SproutContext::finalize]. +/// A value that refers to itself more than once doubles in length on every iteration, +/// so this stops it from using up all memory before the iteration limit is reached. +const CONTEXT_FINALIZE_VALUE_LENGTH_LIMIT: usize = 64 * 1024; + /// Declares a root context for Sprout. /// This contains data that needs to be shared across Sprout. pub struct RootContext { @@ -201,6 +206,10 @@ impl SproutContext { // If the value changed, we need to re-stamp it. did_change = true; } + // Ensure the value has not grown without bound. + if result.len() > CONTEXT_FINALIZE_VALUE_LENGTH_LIMIT { + bail!("value {} is too long while finalizing context", key); + } // Insert the new value into the value map. values.insert(key.clone(), result); } diff --git a/crates/boot/src/main.rs b/crates/boot/src/main.rs index 730906a..aafb225 100644 --- a/crates/boot/src/main.rs +++ b/crates/boot/src/main.rs @@ -260,15 +260,24 @@ fn run() -> Result<()> { } } - for entry in &mut entries { + // Entries whose context can't be finalized are skipped, as one broken entry + // should not prevent booting any of the other entries. + entries.retain_mut(|entry| { let mut context = entry.context().fork(); // Insert the values from the entry configuration into the // sprout context to use with the entry itself. context.insert(&entry.declaration().values); - let context = context - .finalize() - .context("unable to finalize context")? - .freeze(); + let context = match context.finalize() { + Ok(context) => context.freeze(), + Err(error) => { + warn!( + "skipping entry {}, unable to finalize context: {:#}", + entry.name(), + error + ); + return false; + } + }; // Provide the new context to the bootable entry. entry.swap_context(context); // Restamp the title and sort key with any values. @@ -282,7 +291,9 @@ fn run() -> Result<()> { entry.mark_default(); } } - } + + true + }); // Sort the entries by their sort key, finalizing the order to show entries. This happens // in reverse order so that entries that would come last show up first in the menu.