mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 15:50:18 +00:00
fix(context): add context finalization iteration limit
This prevents any possibility of an infinite loop during finalization.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use crate::context::SproutContext;
|
use crate::context::SproutContext;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::rc::Rc;
|
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());
|
bail!("unknown action '{}'", name.as_ref());
|
||||||
};
|
};
|
||||||
// Finalize the context and freeze it.
|
// Finalize the context and freeze it.
|
||||||
let context = context.finalize().freeze();
|
let context = context
|
||||||
|
.finalize()
|
||||||
|
.context("unable to finalize context")?
|
||||||
|
.freeze();
|
||||||
|
|
||||||
// Execute the action.
|
// Execute the action.
|
||||||
if let Some(chainload) = &action.chainload {
|
if let Some(chainload) = &action.chainload {
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use crate::actions::ActionDeclaration;
|
use crate::actions::ActionDeclaration;
|
||||||
use crate::options::SproutOptions;
|
use crate::options::SproutOptions;
|
||||||
use anyhow::Result;
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use anyhow::{Result, bail};
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use uefi::proto::device_path::DevicePath;
|
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.
|
/// Declares a root context for Sprout.
|
||||||
/// This contains data that needs to be shared across Sprout.
|
/// This contains data that needs to be shared across Sprout.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -151,11 +154,20 @@ impl SproutContext {
|
|||||||
/// Finalizes a context by producing a context with no parent that contains all the values
|
/// 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
|
/// 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.
|
/// 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.
|
// Collect all the values from the context and its parents.
|
||||||
let mut current_values = self.all_values();
|
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 {
|
loop {
|
||||||
|
iterations += 1;
|
||||||
|
|
||||||
|
if iterations > CONTEXT_FINALIZE_ITERATION_LIMIT {
|
||||||
|
bail!("infinite loop detected in context finalization");
|
||||||
|
}
|
||||||
|
|
||||||
let mut did_change = false;
|
let mut did_change = false;
|
||||||
let mut values = BTreeMap::new();
|
let mut values = BTreeMap::new();
|
||||||
for (key, value) in ¤t_values {
|
for (key, value) in ¤t_values {
|
||||||
@@ -176,11 +188,11 @@ impl SproutContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Produce the final context.
|
// Produce the final context.
|
||||||
Self {
|
Ok(Self {
|
||||||
root: self.root.clone(),
|
root: self.root.clone(),
|
||||||
parent: None,
|
parent: None,
|
||||||
values: current_values,
|
values: current_values,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stamps the `text` value with the specified `values` map. The returned value indicates
|
/// Stamps the `text` value with the specified `values` map. The returned value indicates
|
||||||
|
|||||||
@@ -143,7 +143,10 @@ fn main() -> Result<()> {
|
|||||||
// Insert the values from the entry configuration into the
|
// Insert the values from the entry configuration into the
|
||||||
// sprout context to use with the entry itself.
|
// sprout context to use with the entry itself.
|
||||||
context.insert(&entry.declaration().values);
|
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.
|
// Provide the new context to the bootable entry.
|
||||||
entry.swap_context(context);
|
entry.swap_context(context);
|
||||||
// Restamp the title with any values.
|
// Restamp the title with any values.
|
||||||
|
|||||||
Reference in New Issue
Block a user