Skip to content

Commit e16f1e7

Browse files
authored
[NominalFuzzing] SignatureRefining: Ignore exported functions (#4601)
This hits the fuzzer when it tries to call reference exports with a null.
1 parent 2ab19d9 commit e16f1e7

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

src/ir/export-utils.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2022 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_ir_export_h
18+
#define wasm_ir_export_h
19+
20+
#include "wasm.h"
21+
22+
namespace wasm::ExportUtils {
23+
24+
inline std::vector<Function*> getExportedFunctions(Module& wasm) {
25+
std::vector<Function*> ret;
26+
for (auto& ex : wasm.exports) {
27+
if (ex->kind == ExternalKind::Function) {
28+
ret.push_back(wasm.getFunction(ex->value));
29+
}
30+
}
31+
return ret;
32+
}
33+
34+
} // namespace wasm::ExportUtils
35+
36+
#endif // wasm_ir_export_h

src/passes/SignatureRefining.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
// type, and all call_refs using it).
2727
//
2828

29+
#include "ir/export-utils.h"
2930
#include "ir/find_all.h"
3031
#include "ir/lubs.h"
3132
#include "ir/module-utils.h"
@@ -69,6 +70,10 @@ struct SignatureRefining : public Pass {
6970

7071
// A possibly improved LUB for the results.
7172
LUBFinder resultsLUB;
73+
74+
// Normally we can optimize, but some cases prevent a particular signature
75+
// type from being changed at all, see below.
76+
bool canModify = true;
7277
};
7378

7479
ModuleUtils::ParallelFunctionAnalysis<Info> analysis(
@@ -106,6 +111,20 @@ struct SignatureRefining : public Pass {
106111
allInfo[func->type].resultsLUB.combine(info.resultsLUB);
107112
}
108113

114+
// We cannot alter the signature of an exported function, as the outside may
115+
// notice us doing so. For example, if we turn a parameter from nullable
116+
// into non-nullable then callers sending a null will break. Put another
117+
// way, we need to see all callers to refine types, and for exports we
118+
// cannot do so.
119+
// TODO If a function type is passed we should also mark the types used
120+
// there, etc., recursively. For now this code just handles the top-
121+
// level type, which is enough to keep the fuzzer from erroring. More
122+
// generally, we need to decide about adding a "closed-world" flag of
123+
// some kind.
124+
for (auto* exportedFunc : ExportUtils::getExportedFunctions(*module)) {
125+
allInfo[exportedFunc->type].canModify = false;
126+
}
127+
109128
bool refinedResults = false;
110129

111130
// Compute optimal LUBs.
@@ -116,6 +135,11 @@ struct SignatureRefining : public Pass {
116135
continue;
117136
}
118137

138+
auto& info = allInfo[type];
139+
if (!info.canModify) {
140+
continue;
141+
}
142+
119143
auto sig = type.getSignature();
120144

121145
auto numParams = sig.params.size();
@@ -127,7 +151,6 @@ struct SignatureRefining : public Pass {
127151
}
128152
};
129153

130-
auto& info = allInfo[type];
131154
for (auto* call : info.calls) {
132155
updateLUBs(call->operands);
133156
}

test/lit/passes/signature-refining.wast

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,3 +624,34 @@
624624
(unreachable)
625625
)
626626
)
627+
628+
;; Exports prevent optimization, so $func's type will not change here.
629+
(module
630+
;; CHECK: (type $sig (func_subtype (param anyref) func))
631+
632+
;; CHECK: (type $none_=>_none (func_subtype func))
633+
634+
;; CHECK: (type $struct (struct_subtype data))
635+
(type $struct (struct_subtype data))
636+
637+
(type $sig (func_subtype (param anyref) func))
638+
639+
;; CHECK: (export "prevent-opts" (func $func))
640+
641+
;; CHECK: (func $func (type $sig) (param $x anyref)
642+
;; CHECK-NEXT: (nop)
643+
;; CHECK-NEXT: )
644+
(func $func (export "prevent-opts") (type $sig) (param $x anyref)
645+
)
646+
647+
;; CHECK: (func $caller (type $none_=>_none)
648+
;; CHECK-NEXT: (call $func
649+
;; CHECK-NEXT: (struct.new_default $struct)
650+
;; CHECK-NEXT: )
651+
;; CHECK-NEXT: )
652+
(func $caller
653+
(call $func
654+
(struct.new $struct)
655+
)
656+
)
657+
)

0 commit comments

Comments
 (0)