Repository: bytecodealliance/wasmtime
Pull request #8323 (2024-04-10 18:47:22)

Handle out-of-bounds component sections
This check should really be part of `wasmparser`'s validation, but this is an immediate fix for #8322 that doesn't need to wait on fixing upstream and then doing releases and then updating our deps.


Patch:
diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs
index 3334b1736..873105b34 100644
--- a/crates/environ/src/component/translate.rs
+++ b/crates/environ/src/component/translate.rs
@@ -4,6 +4,7 @@ use crate::{
     EntityIndex, ModuleEnvironment, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, Tunables,
     TypeConvert, WasmHeapType, WasmValType,
 };
+use anyhow::anyhow;
 use anyhow::{bail, Result};
 use indexmap::IndexMap;
 use std::collections::HashMap;
@@ -534,7 +535,18 @@ impl<'a, 'data> Translator<'a, 'data> {
                     self.validator,
                     self.types.module_types_builder(),
                 )
-                .translate(parser, &component[range.start..range.end])?;
+                .translate(
+                    parser,
+                    component.get(range.start..range.end).ok_or_else(|| {
+                        anyhow!(
+                            "section range {}..{} is out of bounds (bound = {})",
+                            range.start,
+                            range.end,
+                            component.len()
+                        )
+                        .context("wasm component contains an invalid module section")
+                    })?,
+                )?;
                 let static_idx = self.static_modules.push(translation);
                 self.result
                     .initializers


Test patch:
diff --git a/tests/all/component_model/aot.rs b/tests/all/component_model/aot.rs
index b94e8f085..676de39d1 100644
--- a/tests/all/component_model/aot.rs
+++ b/tests/all/component_model/aot.rs
@@ -164,3 +164,46 @@ fn reflect_resource_import() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn truncated_component_binaries_dont_panic() -> Result<()> {
+    let engine = super::engine();
+
+    let binary = wat::parse_str(
+        r#"
+        (component
+            (import "a" (core module $m0
+                (import "" "" (func))
+            ))
+
+            (core module $m1
+                (func (export ""))
+            )
+            (core instance $i1 (instantiate (module $m1)))
+            (func $f (canon lift (core func $i1 "f")))
+
+            (component $c1
+                (import "f" (func))
+                (core module $m2
+                    (func (export "g"))
+                )
+                (core instance $i2 (instantiate $m2))
+                (func (export "g")
+                    (canon lift (core func $i2 "g"))
+                )
+            )
+            (instance $i3 (instantiate $c1 (with "f" (func $f))))
+            (func (export "g") (alias export $i3 "g"))
+        )
+        "#,
+    )?;
+
+    // Check that if we feed each truncation of the component binary into
+    // `Component::new` we don't get any panics.
+    for i in 1..binary.len() - 1 {
+        let _ = Component::from_binary(&engine, &binary[0..i]);
+    }
+
+    Ok(())
+}