[wasm-interpreter] Coerce JS call results against the callee signature [v8/v8 : main]

0 views
Skip to first unread message

Daniel Lehmann (Gerrit)

unread,
Jun 3, 2026, 11:08:50 AM (7 days ago) Jun 3
to Paolo Severini, v8-s...@luci-project-accounts.iam.gserviceaccount.com, v8-re...@googlegroups.com, was...@google.com
Attention needed from Paolo Severini

Daniel Lehmann voted and added 5 comments

Votes added by Daniel Lehmann

Code-Review+1

5 comments

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Daniel Lehmann . resolved

LGTM with nits. I didn't really follow the changes in `wasm-interpreter-runtime.cc` in detail (as usual for interpreter-only changes), but some background questions/nits regardless.

File src/wasm/interpreter/wasm-interpreter-runtime.h
Line 384, Patchset 1 (Latest): DCHECK(mode != Mode::kInternalCall ||
func_index_ != kInvalidFunctionIndex);
DCHECK(mode != Mode::kExternalCall ||
func_index_ == kInvalidFunctionIndex);
}
Daniel Lehmann . unresolved
Just to double check/clarify: `mode` can either be `kInternalCall` or `kExternalCall` (i.e., calling an import?), and in the latter case, `func_index_` must be `kInvalidFunctionIndex`, since there's no associated local/internal function, right?
In other words, these DCHECKs would be equivalent:
```
DCHECK_NE(mode, Mode::kInvalid);
DCHECK_EQ(mode == Mode::kExternalCall,
func_index_ == kInvalidFunctionIndex);
```

Or does `internal` mean Wasm? Maybe the enum values should be renamed, or at least the enum itself be documented.

Line 364, Patchset 1 (Parent): static const uint32_t kInlineSignatureSentinel = UINT_MAX;
Daniel Lehmann . unresolved

So IIUC, this CL removes the signature caching optimization completely. Out of interest, how much did that affect performance?

File src/wasm/interpreter/wasm-interpreter-runtime.cc
Line 2094, Patchset 1 (Latest): internal->sig()->index());
Daniel Lehmann . unresolved

This could be `actual_sig_index` IIUC.

File test/mjsunit/wasm/wasm-interpreter.js
Line 3561, Patchset 1 (Latest):// Regression test for a call_indirect bug. The interpreter cached the
Daniel Lehmann . unresolved

Is there precedent for adding regression test cases to this (already very long) file? I'd prefer the usual convention, i.e., a file in `test/mjsunit/wasm/regress-<bugid>.js`, this way it's also more likely to be picked up by fuzzers and mutated in the future.

Open in Gerrit

Related details

Attention is currently required from:
  • Paolo Severini
Submit Requirements:
  • requirement satisfiedCode-Owners
  • requirement satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: v8/v8
Gerrit-Branch: main
Gerrit-Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
Gerrit-Change-Number: 7894820
Gerrit-PatchSet: 1
Gerrit-Owner: Paolo Severini <paol...@microsoft.com>
Gerrit-Reviewer: Daniel Lehmann <dleh...@chromium.org>
Gerrit-Reviewer: Paolo Severini <paol...@microsoft.com>
Gerrit-Attention: Paolo Severini <paol...@microsoft.com>
Gerrit-Comment-Date: Wed, 03 Jun 2026 15:08:46 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Paolo Severini (Gerrit)

unread,
Jun 4, 2026, 9:46:50 AM (6 days ago) Jun 4
to Daniel Lehmann, v8-s...@luci-project-accounts.iam.gserviceaccount.com, v8-re...@googlegroups.com, was...@google.com
Attention needed from Daniel Lehmann

Paolo Severini added 4 comments

File src/wasm/interpreter/wasm-interpreter-runtime.h
Line 384, Patchset 1: DCHECK(mode != Mode::kInternalCall ||

func_index_ != kInvalidFunctionIndex);
DCHECK(mode != Mode::kExternalCall ||
func_index_ == kInvalidFunctionIndex);
}
Daniel Lehmann . resolved
Just to double check/clarify: `mode` can either be `kInternalCall` or `kExternalCall` (i.e., calling an import?), and in the latter case, `func_index_` must be `kInvalidFunctionIndex`, since there's no associated local/internal function, right?
In other words, these DCHECKs would be equivalent:
```
DCHECK_NE(mode, Mode::kInvalid);
DCHECK_EQ(mode == Mode::kExternalCall,
func_index_ == kInvalidFunctionIndex);
```

Or does `internal` mean Wasm? Maybe the enum values should be renamed, or at least the enum itself be documented.

Paolo Severini

Thanks, these DCHECKs are equivalent but clearer, changing them.

kExternalCall is not "JS import" specifically. The modes are about where dispatch goes, decided at cache-fill time:

 - kInternalCall: target is a Wasm function in the current instance
- kExternalCall: target leaves this interpreter/instance. Two sub-cases, both with func_index == kInvalidFunctionIndex: a Wasm function in a different instance → CallExternalWasmFunction (line 1946), or a JS import. In both cases, the runtime re-reads entry.function_index() fresh rather than the stored index, which is why caching kInvalidFunctionIndex is correct. I will try to document this better.
Line 364, Patchset 1 (Parent): static const uint32_t kInlineSignatureSentinel = UINT_MAX;
Daniel Lehmann . resolved

So IIUC, this CL removes the signature caching optimization completely. Out of interest, how much did that affect performance?

Paolo Severini

I have not taken perf measurements, but the caching of the signature was also affecting the external call_indirect path (cross-instance Wasm and JS imports) and the signature can be very efficiently retrieved with `module_->signature({sig_index});` -> `return types[index.index].function_sig;`. It was a very useless optimization.

File src/wasm/interpreter/wasm-interpreter-runtime.cc
Line 2094, Patchset 1: internal->sig()->index());
Daniel Lehmann . resolved

This could be `actual_sig_index` IIUC.

Paolo Severini

Good catch! Yes, `actual_sig_index` is `internal->sig()->index()` computed above. Switched to reuse it.

File test/mjsunit/wasm/wasm-interpreter.js
Line 3561, Patchset 1:// Regression test for a call_indirect bug. The interpreter cached the
Daniel Lehmann . resolved

Is there precedent for adding regression test cases to this (already very long) file? I'd prefer the usual convention, i.e., a file in `test/mjsunit/wasm/regress-<bugid>.js`, this way it's also more likely to be picked up by fuzzers and mutated in the future.

Paolo Severini

This was done in the past not to "pollute" the list of mjsunit tests with regression tests that were only related to the Wasm interpreter. Almost all test cases in this file are regression tests. Consider also that in most cases the bug was opened "downstream" in Edge (and there was actually already a bug downstream even for this specific issue) and in the past we didn't also open a corresponding bug upstream. I realize however that this test file is really becoming too long, and more importantly, that now that the interpreter os being used for tvOS it is important to document all the bugs upstream.

Open in Gerrit

Related details

Attention is currently required from:
  • Daniel Lehmann
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: v8/v8
Gerrit-Branch: main
Gerrit-Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
Gerrit-Change-Number: 7894820
Gerrit-PatchSet: 1
Gerrit-Owner: Paolo Severini <paol...@microsoft.com>
Gerrit-Reviewer: Daniel Lehmann <dleh...@chromium.org>
Gerrit-Reviewer: Paolo Severini <paol...@microsoft.com>
Gerrit-Attention: Daniel Lehmann <dleh...@chromium.org>
Gerrit-Comment-Date: Thu, 04 Jun 2026 13:46:46 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Daniel Lehmann <dleh...@chromium.org>
unsatisfied_requirement
open
diffy

Daniel Lehmann (Gerrit)

unread,
Jun 8, 2026, 4:53:18 AM (2 days ago) Jun 8
to Paolo Severini, v8-s...@luci-project-accounts.iam.gserviceaccount.com, v8-re...@googlegroups.com, was...@google.com
Attention needed from Paolo Severini

Daniel Lehmann voted and added 4 comments

Votes added by Daniel Lehmann

Code-Review+1

4 comments

Patchset-level comments
Daniel Lehmann . resolved

LGTM with nits. I didn't really follow the changes in `wasm-interpreter-runtime.cc` in detail (as usual for interpreter-only changes), but some background questions/nits regardless.

Daniel Lehmann

Still LGTM

File src/wasm/interpreter/wasm-interpreter-runtime.h
Line 384, Patchset 1: DCHECK(mode != Mode::kInternalCall ||
func_index_ != kInvalidFunctionIndex);
DCHECK(mode != Mode::kExternalCall ||
func_index_ == kInvalidFunctionIndex);
}
Daniel Lehmann . resolved
Just to double check/clarify: `mode` can either be `kInternalCall` or `kExternalCall` (i.e., calling an import?), and in the latter case, `func_index_` must be `kInvalidFunctionIndex`, since there's no associated local/internal function, right?
In other words, these DCHECKs would be equivalent:
```
DCHECK_NE(mode, Mode::kInvalid);
DCHECK_EQ(mode == Mode::kExternalCall,
func_index_ == kInvalidFunctionIndex);
```

Or does `internal` mean Wasm? Maybe the enum values should be renamed, or at least the enum itself be documented.

Paolo Severini

Thanks, these DCHECKs are equivalent but clearer, changing them.

kExternalCall is not "JS import" specifically. The modes are about where dispatch goes, decided at cache-fill time:

 - kInternalCall: target is a Wasm function in the current instance
- kExternalCall: target leaves this interpreter/instance. Two sub-cases, both with func_index == kInvalidFunctionIndex: a Wasm function in a different instance → CallExternalWasmFunction (line 1946), or a JS import. In both cases, the runtime re-reads entry.function_index() fresh rather than the stored index, which is why caching kInvalidFunctionIndex is correct. I will try to document this better.
Daniel Lehmann

Thanks for the background and additional comments.

Line 364, Patchset 1 (Parent): static const uint32_t kInlineSignatureSentinel = UINT_MAX;
Daniel Lehmann . resolved

So IIUC, this CL removes the signature caching optimization completely. Out of interest, how much did that affect performance?

Paolo Severini

I have not taken perf measurements, but the caching of the signature was also affecting the external call_indirect path (cross-instance Wasm and JS imports) and the signature can be very efficiently retrieved with `module_->signature({sig_index});` -> `return types[index.index].function_sig;`. It was a very useless optimization.

Daniel Lehmann

Sounds good, simplifying ftw.

File test/mjsunit/wasm/wasm-interpreter.js
Line 3561, Patchset 1:// Regression test for a call_indirect bug. The interpreter cached the
Daniel Lehmann . resolved

Is there precedent for adding regression test cases to this (already very long) file? I'd prefer the usual convention, i.e., a file in `test/mjsunit/wasm/regress-<bugid>.js`, this way it's also more likely to be picked up by fuzzers and mutated in the future.

Paolo Severini

This was done in the past not to "pollute" the list of mjsunit tests with regression tests that were only related to the Wasm interpreter. Almost all test cases in this file are regression tests. Consider also that in most cases the bug was opened "downstream" in Edge (and there was actually already a bug downstream even for this specific issue) and in the past we didn't also open a corresponding bug upstream. I realize however that this test file is really becoming too long, and more importantly, that now that the interpreter os being used for tvOS it is important to document all the bugs upstream.

Daniel Lehmann

Mhm, maybe for bugs where we already have a Chromium issue, we could have a separate regression test, although I'm not sure this is consensus in the team (so it's not too important either way).

Open in Gerrit

Related details

Attention is currently required from:
  • Paolo Severini
Submit Requirements:
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: v8/v8
    Gerrit-Branch: main
    Gerrit-Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
    Gerrit-Change-Number: 7894820
    Gerrit-PatchSet: 2
    Gerrit-Owner: Paolo Severini <paol...@microsoft.com>
    Gerrit-Reviewer: Daniel Lehmann <dleh...@chromium.org>
    Gerrit-Reviewer: Paolo Severini <paol...@microsoft.com>
    Gerrit-Attention: Paolo Severini <paol...@microsoft.com>
    Gerrit-Comment-Date: Mon, 08 Jun 2026 08:53:13 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    Comment-In-Reply-To: Daniel Lehmann <dleh...@chromium.org>
    Comment-In-Reply-To: Paolo Severini <paol...@microsoft.com>
    satisfied_requirement
    open
    diffy

    Paolo Severini (Gerrit)

    unread,
    Jun 9, 2026, 10:18:49 AM (19 hours ago) Jun 9
    to Daniel Lehmann, v8-s...@luci-project-accounts.iam.gserviceaccount.com, v8-re...@googlegroups.com, was...@google.com

    Paolo Severini voted Commit-Queue+2

    Commit-Queue+2
    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: v8/v8
    Gerrit-Branch: main
    Gerrit-Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
    Gerrit-Change-Number: 7894820
    Gerrit-PatchSet: 2
    Gerrit-Owner: Paolo Severini <paol...@microsoft.com>
    Gerrit-Reviewer: Daniel Lehmann <dleh...@chromium.org>
    Gerrit-Reviewer: Paolo Severini <paol...@microsoft.com>
    Gerrit-Comment-Date: Tue, 09 Jun 2026 14:18:45 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    open
    diffy

    v8-scoped@luci-project-accounts.iam.gserviceaccount.com (Gerrit)

    unread,
    Jun 9, 2026, 11:50:01 AM (18 hours ago) Jun 9
    to Paolo Severini, Daniel Lehmann, v8-re...@googlegroups.com, was...@google.com

    v8-s...@luci-project-accounts.iam.gserviceaccount.com submitted the change

    Change information

    Commit message:
    [wasm-interpreter] Coerce JS call results against the callee signature

    The interpreter cached a FunctionSig* on each indirect dispatch table
    entry, derived from the first call_indirect signature that populated the
    slot. A later call on the same entry through a subtype signature passed
    the canonical subtype check but then reused the stale cached signature
    to coerce the imported JS function's return values. As a result the
    JS<->Wasm boundary could convert return values against a wider supertype
    than the one the callee actually declared, so a value valid for the
    cached signature but not for the callee's signature could reach the
    typed value stack.

    Stop caching the FunctionSig* in IndirectCallValue. Instead look up the
    two signatures that are actually needed at the call site:

    - callsite_signature: drives the reference stack / slot layout, matching
    how arguments and results were laid out by the caller.
    - callee_canonical_sig: the callee's declared canonical signature, used
    to validate and coerce the JS return values, matching the behavior of
    compiled Wasm-to-JS wrappers.

    For direct imports the two coincide, so callee_canonical_sig stays null
    and behavior is unchanged. Also add DCHECK(IsWasmStruct(...)) guards to
    the struct.get/struct.set handlers.

    Adds a regression test that exercises a wide-then-narrow call_indirect
    sequence on the same table entry and checks that the narrow call still
    rejects a non-conforming JS return value.
    Bug: 518064995
    Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
    Commit-Queue: Paolo Severini <paol...@microsoft.com>
    Reviewed-by: Daniel Lehmann <dleh...@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#107861}
    Files:
    • M src/wasm/interpreter/wasm-interpreter-runtime.cc
    • M src/wasm/interpreter/wasm-interpreter-runtime.h
    • M src/wasm/interpreter/wasm-interpreter.cc
    • A test/mjsunit/regress/wasm/regress-518064995.js
    Change size: L
    Delta: 4 files changed, 242 insertions(+), 63 deletions(-)
    Branch: refs/heads/main
    Submit Requirements:
    • requirement satisfiedCode-Review: +1 by Daniel Lehmann
    Open in Gerrit
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: merged
    Gerrit-Project: v8/v8
    Gerrit-Branch: main
    Gerrit-Change-Id: I5975ad8cb60d90e76328e8d82b10d82b3579a45c
    Gerrit-Change-Number: 7894820
    Gerrit-PatchSet: 3
    Gerrit-Owner: Paolo Severini <paol...@microsoft.com>
    Gerrit-Reviewer: Daniel Lehmann <dleh...@chromium.org>
    Gerrit-Reviewer: Paolo Severini <paol...@microsoft.com>
    open
    diffy
    satisfied_requirement
    Reply all
    Reply to author
    Forward
    0 new messages