Winch typed select can omit a live GC reference from stack maps
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 62/100
調査の方向性
Run the provided public-embedding reproducer with both null and copying collectors, then inspect visit_typed_select and visit_select in winch/codegen/src/visitor.rs alongside needs_stack_map and calculate_stack_map_offsets. Confirm the typed-select result retains a reference shadow type, the call-site stack map includes it, and the copying-collector case survives.
索引モデルが issue の本文から書いたものです。
説明
Summary
When Winch compiles a typed select whose result is a GC reference, the result's shadow type can incorrectly come from the second operand rather than the declared result type.
If the first operand is a non-null externref, the second operand is ref.null extern, and the condition selects the first operand, the runtime value is a real GC reference but its shadow type becomes I32.
At a later call site this causes the reference to be omitted from the stack map. With the copying collector, the referenced object can then be reclaimed or relocated without the stack copy being updated.
This was reproduced on the latest commit.
Technical Details
The missing check is the shadow-type filter used to decide whether a value needs a stack map. needs_stack_map (winch/codegen/src/stack.rs:315-327) is unconditionally false for I32, so a live GC reference whose shadow type is I32 neither increments gc_ref_count nor satisfies the collection condition in CodeGenContext::calculate_stack_map_offsets (winch/codegen/src/codegen/context.rs:663-695).
Because that function then returns an empty table, FnCall::emit (winch/codegen/src/codegen/call.rs:108-113) skips emitting any stack map.
The relevant data flow is:
visit_ref_null(winch/codegen/src/visitor.rs:2232-2252) pushesref.null extern/exnasVal::i32(0).visit_typed_select(winch/codegen/src/visitor.rs:2228-2230) ignores the instruction's declared result type_tyand dispatches tovisit_select.visit_select(winch/codegen/src/visitor.rs:2208-2226) pushes the result using the shadow type of the second operand (stack.push(val2.into())).- When operand 1 is a real GC reference, operand 2 is
ref.null, and the condition is non-zero, the runtime result is the reference but its shadow type isI32. - At the next call,
FnCall::emitspills the value, butcalculate_stack_map_offsetsreturns an empty table, so no stack map is emitted. - During collection,
Store::trace_wasm_stack_frame(crates/wasmtime/src/runtime/store/gc.rs:771-786) only treats slots present in the stack map as Wasm stack roots.
Reproduction
Tested at commit:
ff7896b6d97a424aed430a48a186773fd163667f
The reproducer uses the public embedding API with Winch and compares a normal reference path with the typed-select path.
Guest input:
(module
(import "" "make" (func $make (result externref)))
(import "" "gc" (func $gc))
(func (export "control") (result externref)
call $make
call $gc)
(func (export "bug") (result externref)
call $make
ref.null extern
i32.const 1
select (result externref)
call $gc)
)
With the null collector, both cases survive.
With the copying collector, the control case survives while the typed-select case loses the referenced object:
collector=null
[control] data=Ok(0xdecaf) SURVIVED
[bug] data=Ok(0xdecaf) SURVIVED
collector=copying
[control] data=Ok(0xdecaf) SURVIVED
[bug] data=Err(BUG: invalid `ExternRefHostDataId`) HOST-DATA-SWEPT
SUMMARY null_bug=Survived copying_control=Survived copying_bug=Swept
PROBE_RESULT: reproduced
The two cases differ only in whether the reference passes through the typed select, which appears to isolate the problem to the shadow type used for the select result.
Suggested Fix
visit_typed_select (winch/codegen/src/visitor.rs:2228-2230) should honor the instruction's declared result type instead of discarding _ty.
visit_select (winch/codegen/src/visitor.rs:2208-2226) should not derive the pushed shadow type from the second operand when the declared result is a reference type. The pushed value needs to retain a reference shadow type so that needs_stack_map returns true and the live reference is included in the call-site stack map.
- 主要言語
- Rust
- スター
- 18.7k
- フォーク
- 1.8k
- 平均マージ
- 1日 7時間
- マージ済み PR(30日)
- 140
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
bytecodealliance/wasmtime のほかの issue
-
cranelift cranelift:documentation
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
bytecodealliance/wasmtime#14433 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
cranelift
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
bytecodealliance/wasmtime#14200 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
bug wasm-proposal:exceptions wasmtime:debugging
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
bytecodealliance/wasmtime#14102 ·
メンテナーはふだん 1 日以内に返信
-
wasm-proposal:gc
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
bytecodealliance/wasmtime#13808 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
cranelift cranelift:area:clif cranelift:goal:optimize-speed enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
bytecodealliance/wasmtime#1598 · コメント 9 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
bytecodealliance/wasmtime の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 3 日以内に返信
-
state:triage-needed
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
Automattic/harper#4503 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
メンテナーはふだん 2 日以内に返信