Skip to content

Commit 0ccf280

Browse files
authored
Fuzzer: Fix cross-module call_ref from JS (#7969)
The "call_ref" import was getting the function in the current module from the reference, but the target might be in another. To fix this, make `callFunctionAsJS` receive a lambda that does the call, and do the right thing in both cases.
1 parent 3a717cf commit 0ccf280

File tree

3 files changed

+55
-8
lines changed

3 files changed

+55
-8
lines changed

src/tools/execution-results.h

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,25 +236,34 @@ struct LoggingExternalInterface : public ShellExternalInterface {
236236
// No callable export.
237237
throwJSException();
238238
}
239-
return callFunctionAsJS(*exp->getInternalName());
239+
auto funcName = *exp->getInternalName();
240+
return callFunctionAsJS(
241+
[&](Literals arguments) {
242+
return instance->callFunction(funcName, arguments);
243+
},
244+
wasm.getFunction(funcName)->type.getHeapType());
240245
}
241246

242247
Literals callRefAsJS(Literal ref) {
243248
if (!ref.isFunction()) {
244249
// Not a callable ref.
245250
throwJSException();
246251
}
247-
return callFunctionAsJS(ref.getFunc());
252+
return callFunctionAsJS(
253+
[&](Literals arguments) { return ref.getFuncData()->doCall(arguments); },
254+
ref.type.getHeapType());
248255
}
249256

250257
// Call a function in a "JS-ey" manner, adding arguments as needed, and
251-
// throwing if necessary, the same way JS does.
252-
Literals callFunctionAsJS(Name name) {
253-
auto* func = wasm.getFunction(name);
258+
// throwing if necessary, the same way JS does. We are given a method that
259+
// does the actual call, and the type we are calling.
260+
Literals callFunctionAsJS(std::function<Flow(Literals)> doCall,
261+
HeapType type) {
262+
auto sig = type.getSignature();
254263

255264
// Send default values as arguments, or error if we need anything else.
256265
Literals arguments;
257-
for (const auto& param : func->getParams()) {
266+
for (const auto& param : sig.params) {
258267
// An i64 param can work from JS, but fuzz_shell provides 0, which errors
259268
// on attempts to convert it to BigInt. v128 and exnref are disalloewd.
260269
if (param == Type::i64 || param == Type::v128 || param.isExn()) {
@@ -268,7 +277,7 @@ struct LoggingExternalInterface : public ShellExternalInterface {
268277

269278
// Error on illegal results. Note that this happens, as per JS semantics,
270279
// *before* the call.
271-
for (const auto& result : func->getResults()) {
280+
for (const auto& result : sig.results) {
272281
// An i64 result is fine: a BigInt will be provided. But v128 and exnref
273282
// still error.
274283
if (result == Type::v128 || result.isExn()) {
@@ -277,7 +286,7 @@ struct LoggingExternalInterface : public ShellExternalInterface {
277286
}
278287

279288
// Call the function.
280-
auto flow = instance->callFunction(func->name, arguments);
289+
auto flow = doCall(arguments);
281290
// Suspending through JS is not valid.
282291
if (flow.suspendTag) {
283292
throwJSException();
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
;; RUN: wasm-opt %s -all --fuzz-exec-before --fuzz-exec-second=%s.second -q -o /dev/null 2>&1 | filecheck %s
2+
3+
;; Test for confusion regarding internal function names when using call_ref from
4+
;; JS. This module returns a funcref, which the other module will get, then
5+
;; send to JS to be call_ref'd.
6+
7+
(module $primary
8+
(import "fuzzing-support" "log-i32" (func $log (param i32)))
9+
10+
(func $get (export "get") (result funcref)
11+
(call $log (i32.const 10))
12+
(ref.func $get)
13+
)
14+
)
15+
16+
;; First we call $get from this module. When we run the second module, we call
17+
;; it once to get a ref to itself, then call_ref it, logging 10 twice.
18+
19+
;; CHECK: [fuzz-exec] calling get
20+
;; CHECK-NEXT: [LoggingExternalInterface logging 10]
21+
;; CHECK-NEXT: [fuzz-exec] note result: get => function
22+
;; CHECK-NEXT: [fuzz-exec] running second module
23+
;; CHECK-NEXT: [fuzz-exec] calling run
24+
;; CHECK-NEXT: [LoggingExternalInterface logging 10]
25+
;; CHECK-NEXT: [LoggingExternalInterface logging 10]
26+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(module
2+
(import "fuzzing-support" "call-ref" (func $call-ref (param funcref i32)))
3+
(import "primary" "get" (func $primary-get (result funcref)))
4+
5+
(func $run (export "run")
6+
(call $call-ref
7+
(call $primary-get)
8+
(i32.const 0)
9+
)
10+
)
11+
)
12+

0 commit comments

Comments
 (0)