From 7439a07f71f5854877c31eff64ad8eb3bfd62eb4 Mon Sep 17 00:00:00 2001 From: Souyama Date: Wed, 4 Nov 2026 01:13:52 +0530 Subject: [PATCH 1/2] wit-parser: don't allocate the size a section claims Decoding read each section into a buffer sized up front by the length the input gives for it. A 14-byte input claiming a 4 GiB section made it allocate 4 GiB, which aborts the process where that fails. The buffer now grows with what is actually read. --- crates/wit-parser/src/decoding.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index aeddc85eb9..f056c8d8f3 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -62,15 +62,10 @@ impl ComponentInfo { Chunk::NeedMoreData(hint) => { assert!(!eof); // otherwise an error would be returned - // Use the hint to preallocate more space, then read - // some more data into our buffer. - // - // Note that the buffer management here is not ideal, - // but it's compact enough to fit in an example! - let len = buffer.len(); - buffer.extend((0..hint).map(|_| 0u8)); - let n = reader.read(&mut buffer[len..])?; - buffer.truncate(len + n); + // Read up to the hint into our buffer, which grows with + // what is read: the hint comes from the input, so it may + // be far more than there is. + let n = (&mut reader).take(hint as u64).read_to_end(&mut buffer)?; eof = n == 0; continue; } From ad8073d5e1d024e5024646ae43a600fe9194bd6d Mon Sep 17 00:00:00 2001 From: Souyama Date: Tue, 10 Nov 2026 22:35:51 +0530 Subject: [PATCH 2/2] wit-parser: error on types nested 100 types deep WIT already rejected nesting 100 types deep within one type, such as `list>`, but not through named types and aliases. So `record r1 { x: r0 }` repeated 200,000 times parsed, and then overflowed the stack in code that recurses through types, such as `component embed --dummy`. The same limit now applies once a package is resolved, counting named types and aliases. --- crates/wit-parser/src/ast.rs | 7 +- crates/wit-parser/src/resolve/mod.rs | 40 +++++++ .../type-nesting-too-deep-aliases.wit | 108 ++++++++++++++++++ .../type-nesting-too-deep-aliases.wit.result | 5 + .../type-nesting-too-deep-records.wit | 108 ++++++++++++++++++ .../type-nesting-too-deep-records.wit.result | 5 + 6 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit create mode 100644 crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit.result create mode 100644 crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit create mode 100644 crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit.result diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index b88b673146..0450342197 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -1436,9 +1436,10 @@ fn parse_docs<'a>(tokens: &mut Tokenizer<'a>) -> Result, lex::Error> { Ok(docs) } -/// Maximum nesting depth of types parsed with `Type::parse`, e.g. -/// `list>>`. -const MAX_TYPE_DEPTH: usize = 100; +/// Maximum nesting depth of types, both within one type parsed with +/// `Type::parse`, e.g. `list>>`, and through named types and +/// aliases once a package is resolved. +pub(crate) const MAX_TYPE_DEPTH: usize = 100; impl<'a> Type<'a> { fn parse(tokens: &mut Tokenizer<'a>) -> ParseResult { diff --git a/crates/wit-parser/src/resolve/mod.rs b/crates/wit-parser/src/resolve/mod.rs index 2680c1a011..96f0e0de0e 100644 --- a/crates/wit-parser/src/resolve/mod.rs +++ b/crates/wit-parser/src/resolve/mod.rs @@ -3535,6 +3535,7 @@ impl Remap { // order should be sufficient. Also note though that the interface // owner of a type isn't updated here due to interfaces not being known // yet. + let first_new_type = resolve.types.len(); for (id, mut ty) in unresolved.types.into_iter().skip(foreign_types) { let span = ty.span; if !resolve.include_stability(&ty.stability, &pkgid, span)? { @@ -3568,6 +3569,8 @@ impl Remap { self.types.push(Some(new_id)); } + check_type_depth(resolve, first_new_type)?; + // Next transfer all interfaces into `Resolve`, updating type ids // referenced along the way. for (id, mut iface) in unresolved.interfaces.into_iter().skip(foreign_interfaces) { @@ -4495,6 +4498,43 @@ impl Remap { } } +/// Errors if a type at index `first_new` or later in `resolve.types` is +/// nested `MAX_TYPE_DEPTH` or more types deep, counting aliases. +/// +/// Types are topologically sorted, so each type's depth follows from the +/// depths of the earlier types it refers to in one pass, without recursion. +fn check_type_depth(resolve: &Resolve, first_new: usize) -> ResolveResult<()> { + struct Deepest<'a> { + depths: &'a [usize], + deepest: usize, + } + impl TypeIdVisitor for Deepest<'_> { + fn before_visit_type_id(&mut self, id: TypeId) -> bool { + self.deepest = self.deepest.max(self.depths[id.index()]); + // Only look at the types referred to directly. + false + } + } + let mut depths = Vec::with_capacity(resolve.types.len()); + for (id, ty) in resolve.types.iter() { + let mut deepest = Deepest { + depths: &depths, + deepest: 0, + }; + deepest.visit_type_def(resolve, ty); + let depth = deepest.deepest + 1; + if id.index() >= first_new && depth >= crate::ast::MAX_TYPE_DEPTH { + let msg = match &ty.name { + Some(name) => format!("type `{name}` is nested too deep"), + None => "type nesting too deep".to_string(), + }; + return Err(ResolveError::new_semantic(ty.span, msg)); + } + depths.push(depth); + } + Ok(()) +} + struct MergeMap<'a> { /// A map of package ids in `from` to those in `into` for those that are /// found to be equivalent. diff --git a/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit new file mode 100644 index 0000000000..b440d97fbe --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit @@ -0,0 +1,108 @@ +// parse-fail +// +// Each alias names the previous one, so `t99` is 100 types deep. + +package a:b; + +interface i { + type t0 = u32; + type t1 = t0; + type t2 = t1; + type t3 = t2; + type t4 = t3; + type t5 = t4; + type t6 = t5; + type t7 = t6; + type t8 = t7; + type t9 = t8; + type t10 = t9; + type t11 = t10; + type t12 = t11; + type t13 = t12; + type t14 = t13; + type t15 = t14; + type t16 = t15; + type t17 = t16; + type t18 = t17; + type t19 = t18; + type t20 = t19; + type t21 = t20; + type t22 = t21; + type t23 = t22; + type t24 = t23; + type t25 = t24; + type t26 = t25; + type t27 = t26; + type t28 = t27; + type t29 = t28; + type t30 = t29; + type t31 = t30; + type t32 = t31; + type t33 = t32; + type t34 = t33; + type t35 = t34; + type t36 = t35; + type t37 = t36; + type t38 = t37; + type t39 = t38; + type t40 = t39; + type t41 = t40; + type t42 = t41; + type t43 = t42; + type t44 = t43; + type t45 = t44; + type t46 = t45; + type t47 = t46; + type t48 = t47; + type t49 = t48; + type t50 = t49; + type t51 = t50; + type t52 = t51; + type t53 = t52; + type t54 = t53; + type t55 = t54; + type t56 = t55; + type t57 = t56; + type t58 = t57; + type t59 = t58; + type t60 = t59; + type t61 = t60; + type t62 = t61; + type t63 = t62; + type t64 = t63; + type t65 = t64; + type t66 = t65; + type t67 = t66; + type t68 = t67; + type t69 = t68; + type t70 = t69; + type t71 = t70; + type t72 = t71; + type t73 = t72; + type t74 = t73; + type t75 = t74; + type t76 = t75; + type t77 = t76; + type t78 = t77; + type t79 = t78; + type t80 = t79; + type t81 = t80; + type t82 = t81; + type t83 = t82; + type t84 = t83; + type t85 = t84; + type t86 = t85; + type t87 = t86; + type t88 = t87; + type t89 = t88; + type t90 = t89; + type t91 = t90; + type t92 = t91; + type t93 = t92; + type t94 = t93; + type t95 = t94; + type t96 = t95; + type t97 = t96; + type t98 = t97; + type t99 = t98; +} diff --git a/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit.result b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit.result new file mode 100644 index 0000000000..7f38647b93 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-aliases.wit.result @@ -0,0 +1,5 @@ +type `t99` is nested too deep + --> tests/ui/parse-fail/type-nesting-too-deep-aliases.wit:107:8 + | + 107 | type t99 = t98; + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit new file mode 100644 index 0000000000..c3ae1e714f --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit @@ -0,0 +1,108 @@ +// parse-fail +// +// Each record holds the previous one, so `r99` is 100 types deep. + +package a:b; + +interface i { + record r0 { x: u32 } + record r1 { x: r0 } + record r2 { x: r1 } + record r3 { x: r2 } + record r4 { x: r3 } + record r5 { x: r4 } + record r6 { x: r5 } + record r7 { x: r6 } + record r8 { x: r7 } + record r9 { x: r8 } + record r10 { x: r9 } + record r11 { x: r10 } + record r12 { x: r11 } + record r13 { x: r12 } + record r14 { x: r13 } + record r15 { x: r14 } + record r16 { x: r15 } + record r17 { x: r16 } + record r18 { x: r17 } + record r19 { x: r18 } + record r20 { x: r19 } + record r21 { x: r20 } + record r22 { x: r21 } + record r23 { x: r22 } + record r24 { x: r23 } + record r25 { x: r24 } + record r26 { x: r25 } + record r27 { x: r26 } + record r28 { x: r27 } + record r29 { x: r28 } + record r30 { x: r29 } + record r31 { x: r30 } + record r32 { x: r31 } + record r33 { x: r32 } + record r34 { x: r33 } + record r35 { x: r34 } + record r36 { x: r35 } + record r37 { x: r36 } + record r38 { x: r37 } + record r39 { x: r38 } + record r40 { x: r39 } + record r41 { x: r40 } + record r42 { x: r41 } + record r43 { x: r42 } + record r44 { x: r43 } + record r45 { x: r44 } + record r46 { x: r45 } + record r47 { x: r46 } + record r48 { x: r47 } + record r49 { x: r48 } + record r50 { x: r49 } + record r51 { x: r50 } + record r52 { x: r51 } + record r53 { x: r52 } + record r54 { x: r53 } + record r55 { x: r54 } + record r56 { x: r55 } + record r57 { x: r56 } + record r58 { x: r57 } + record r59 { x: r58 } + record r60 { x: r59 } + record r61 { x: r60 } + record r62 { x: r61 } + record r63 { x: r62 } + record r64 { x: r63 } + record r65 { x: r64 } + record r66 { x: r65 } + record r67 { x: r66 } + record r68 { x: r67 } + record r69 { x: r68 } + record r70 { x: r69 } + record r71 { x: r70 } + record r72 { x: r71 } + record r73 { x: r72 } + record r74 { x: r73 } + record r75 { x: r74 } + record r76 { x: r75 } + record r77 { x: r76 } + record r78 { x: r77 } + record r79 { x: r78 } + record r80 { x: r79 } + record r81 { x: r80 } + record r82 { x: r81 } + record r83 { x: r82 } + record r84 { x: r83 } + record r85 { x: r84 } + record r86 { x: r85 } + record r87 { x: r86 } + record r88 { x: r87 } + record r89 { x: r88 } + record r90 { x: r89 } + record r91 { x: r90 } + record r92 { x: r91 } + record r93 { x: r92 } + record r94 { x: r93 } + record r95 { x: r94 } + record r96 { x: r95 } + record r97 { x: r96 } + record r98 { x: r97 } + record r99 { x: r98 } +} diff --git a/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit.result b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit.result new file mode 100644 index 0000000000..301f5d559c --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/type-nesting-too-deep-records.wit.result @@ -0,0 +1,5 @@ +type `r99` is nested too deep + --> tests/ui/parse-fail/type-nesting-too-deep-records.wit:107:10 + | + 107 | record r99 { x: r98 } + | ^-- \ No newline at end of file