fix(context): add context finalization iteration limit

This prevents any possibility of an infinite loop during finalization.
This commit is contained in:
2025-10-24 18:44:28 -07:00
parent 2e3399f33f
commit 2a2aa74c09
3 changed files with 25 additions and 7 deletions

View File

@@ -1,5 +1,5 @@
use crate::context::SproutContext;
use anyhow::{Result, bail};
use anyhow::{Context, Result, bail};
use serde::{Deserialize, Serialize};
use std::rc::Rc;
@@ -50,7 +50,10 @@ pub fn execute(context: Rc<SproutContext>, name: impl AsRef<str>) -> Result<()>
bail!("unknown action '{}'", name.as_ref());
};
// Finalize the context and freeze it.
let context = context.finalize().freeze();
let context = context
.finalize()
.context("unable to finalize context")?
.freeze();
// Execute the action.
if let Some(chainload) = &action.chainload {

View File

@@ -1,11 +1,14 @@
use crate::actions::ActionDeclaration;
use crate::options::SproutOptions;
use anyhow::Result;
use anyhow::anyhow;
use anyhow::{Result, bail};
use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc;
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;
/// Declares a root context for Sprout.
/// This contains data that needs to be shared across Sprout.
#[derive(Default)]
@@ -151,11 +154,20 @@ impl SproutContext {
/// Finalizes a context by producing a context with no parent that contains all the values
/// of all parent contexts merged. This makes it possible to ensure [SproutContext] has no
/// inheritance with other [SproutContext]s. It will still contain a [RootContext] however.
pub fn finalize(&self) -> SproutContext {
pub fn finalize(&self) -> Result<SproutContext> {
// Collect all the values from the context and its parents.
let mut current_values = self.all_values();
// To ensure that there is no possible infinite loop, we need to check
// the number of iterations. If it exceeds 100, we bail.
let mut iterations: usize = 0;
loop {
iterations += 1;
if iterations > CONTEXT_FINALIZE_ITERATION_LIMIT {
bail!("infinite loop detected in context finalization");
}
let mut did_change = false;
let mut values = BTreeMap::new();
for (key, value) in &current_values {
@@ -176,11 +188,11 @@ impl SproutContext {
}
// Produce the final context.
Self {
Ok(Self {
root: self.root.clone(),
parent: None,
values: current_values,
}
})
}
/// Stamps the `text` value with the specified `values` map. The returned value indicates

View File

@@ -143,7 +143,10 @@ fn main() -> Result<()> {
// 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().freeze();
let context = context
.finalize()
.context("unable to finalize context")?
.freeze();
// Provide the new context to the bootable entry.
entry.swap_context(context);
// Restamp the title with any values.