Stream: git-wasmtime

Topic: wasmtime / issue #14354 Component adapter recurses indefi...


view this post on Zulip Wasmtime GitHub notifications bot (Sep 18 2026 at 08:56):

ahirner opened issue #14354:

Linked records, tuples, variants, enums, and fixed lists exceeding 1,000 items trap with call stack exhausted.
Wasmtime charges each type layer against a new 1,000-unit adapter budget recursively.

Test Case

see generated WAT below.

Steps to Reproduce

Standalone reproducer. Requires Node and Wasmtime on PATH.
Run: node wasmtime-adapter-recursion.mjs [output-directory]

Both should return a0. The 1,000-case variant succeeds and 1,001 cases trap.

import { mkdirSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { spawnSync } from 'node:child_process';

const output = resolve(process.argv[2] ?? 'out/wasmtime-adapter-repro');
mkdirSync(output, { recursive: true });
const version = spawnSync('wasmtime', ['--version'], { encoding: 'utf8', timeout: 30_000 });
if (version.error || version.status !== 0) throw version.error ?? new Error(version.stderr);
const report = { engine: version.stdout.trim(), platform: process.platform, arch: process.arch, cases: [] };

for (const count of [1000, 1001]) {
  const variant = `(variant ${Array.from({ length: count }, (_, i) => `(case "a${i}")`).join(' ')})`;
  const wat = `(component
    (type $original ${variant})
    (export $v "value" (type $original))
    (component $provider
      (type $original ${variant})
      (import "value" (type $v (eq $original)))
      (core module $m (func (export "id") (param i32) (result i32) local.get 0))
      (core instance $m (instantiate $m))
      (func $id (param "x" $v) (result $v) (canon lift (core func $m "id")))
      (export "id" (func $id)))
    (instance $provider (instantiate $provider (with "value" (type $v))))
    (alias export $provider "id" (func $id))
    (component $consumer
      (type $original ${variant})
      (import "value" (type $v (eq $original)))
      (import "id" (func $id (param "x" $v) (result $v)))
      (core func $id (canon lower (func $id)))
      (core module $m
        (import "host" "id" (func $id (param i32) (result i32)))
        (func (export "run") (param i32) (result i32) local.get 0 call $id))
      (core instance $host (export "id" (func $id)))
      (core instance $m (instantiate $m (with "host" (instance $host))))
      (func $run (param "x" $v) (result $v) (canon lift (core func $m "run")))
      (export "run" (func $run)))
    (instance $consumer (instantiate $consumer (with "value" (type $v)) (with "id" (func $id))))
    (alias export $consumer "run" (func $run))
    (export "run" (func $run)))
`;
  const path = resolve(output, `adapter-${count}.wat`);
  writeFileSync(path, wat);
  const args = ['run', '-W', 'gc=n', '--invoke', 'run(a0)', path];
  const result = spawnSync('wasmtime', args, { encoding: 'utf8', timeout: 30_000, maxBuffer: 4 * 1024 * 1024 });
  writeFileSync(path + '.stderr', result.stderr ?? '');
  report.cases.push({ count, args, status: result.status, signal: result.signal,
    stdout: result.stdout?.trim(), error: result.error?.message, stderrFile: path + '.stderr' });
  if (result.error || result.status !== 0 || result.stdout.trim() !== 'a0') process.exitCode = 1;
}

writeFileSync(resolve(output, 'results.json'), JSON.stringify(report, null, 2) + '\n');
console.log(JSON.stringify(report, null, 2));

Expected Results

TODO: What do you expect to happen?

Actual Results

TODO: What actually happens? Panic? Segfault? Incorrect result?

Versions and Environment

Wasmtime version or commit: 48.0.2 probably, main and earlier also

Operating system: Mac OS

Architecture: aarch64

Extra Info

Since that traps wasm execution, it should not be a DoS vector in most cases.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 18 2026 at 08:56):

ahirner added the bug label to Issue #14354.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 18 2026 at 17:29):

alexcrichton closed issue #14354:

Linked records, tuples, variants, enums, and fixed lists exceeding 1,000 items trap with call stack exhausted.
Wasmtime charges each type layer against a new 1,000-unit adapter budget recursively.

Test Case

see generated WAT below.

Steps to Reproduce

Standalone reproducer. Requires Node and Wasmtime on PATH.
Run: node wasmtime-adapter-recursion.mjs [output-directory]

Both should return a0. The 1,000-case variant succeeds and 1,001 cases trap.

import { mkdirSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { spawnSync } from 'node:child_process';

const output = resolve(process.argv[2] ?? 'out/wasmtime-adapter-repro');
mkdirSync(output, { recursive: true });
const version = spawnSync('wasmtime', ['--version'], { encoding: 'utf8', timeout: 30_000 });
if (version.error || version.status !== 0) throw version.error ?? new Error(version.stderr);
const report = { engine: version.stdout.trim(), platform: process.platform, arch: process.arch, cases: [] };

for (const count of [1000, 1001]) {
  const variant = `(variant ${Array.from({ length: count }, (_, i) => `(case "a${i}")`).join(' ')})`;
  const wat = `(component
    (type $original ${variant})
    (export $v "value" (type $original))
    (component $provider
      (type $original ${variant})
      (import "value" (type $v (eq $original)))
      (core module $m (func (export "id") (param i32) (result i32) local.get 0))
      (core instance $m (instantiate $m))
      (func $id (param "x" $v) (result $v) (canon lift (core func $m "id")))
      (export "id" (func $id)))
    (instance $provider (instantiate $provider (with "value" (type $v))))
    (alias export $provider "id" (func $id))
    (component $consumer
      (type $original ${variant})
      (import "value" (type $v (eq $original)))
      (import "id" (func $id (param "x" $v) (result $v)))
      (core func $id (canon lower (func $id)))
      (core module $m
        (import "host" "id" (func $id (param i32) (result i32)))
        (func (export "run") (param i32) (result i32) local.get 0 call $id))
      (core instance $host (export "id" (func $id)))
      (core instance $m (instantiate $m (with "host" (instance $host))))
      (func $run (param "x" $v) (result $v) (canon lift (core func $m "run")))
      (export "run" (func $run)))
    (instance $consumer (instantiate $consumer (with "value" (type $v)) (with "id" (func $id))))
    (alias export $consumer "run" (func $run))
    (export "run" (func $run)))
`;
  const path = resolve(output, `adapter-${count}.wat`);
  writeFileSync(path, wat);
  const args = ['run', '-W', 'gc=n', '--invoke', 'run(a0)', path];
  const result = spawnSync('wasmtime', args, { encoding: 'utf8', timeout: 30_000, maxBuffer: 4 * 1024 * 1024 });
  writeFileSync(path + '.stderr', result.stderr ?? '');
  report.cases.push({ count, args, status: result.status, signal: result.signal,
    stdout: result.stdout?.trim(), error: result.error?.message, stderrFile: path + '.stderr' });
  if (result.error || result.status !== 0 || result.stdout.trim() !== 'a0') process.exitCode = 1;
}

writeFileSync(resolve(output, 'results.json'), JSON.stringify(report, null, 2) + '\n');
console.log(JSON.stringify(report, null, 2));

Expected Results

TODO: What do you expect to happen?

Actual Results

TODO: What actually happens? Panic? Segfault? Incorrect result?

Versions and Environment

Wasmtime version or commit: 48.0.2 probably, main and earlier also

Operating system: Mac OS

Architecture: aarch64

Extra Info

Since that traps wasm execution, it should not be a DoS vector in most cases.


Last updated: Sep 20 2026 at 18:08 UTC