Skip to content

Commit c3e45d4

Browse files
authored
Limit interpreter depth in precompute, but not when running whole modules (#2191)
Keep limiting in precompute as before: that is useful since that pass is run as part of normal compilation, and we want to avoid native stack limits on all platforms. Also that pass is not likely to find any pattern of size 50 of higher that it can't precompute as a sum of smaller pieces. Restore the 250 limit from before for interpreting entire modules, as without that the fuzzer will sometimes hit the limit and cause a false positive.
1 parent be3135c commit c3e45d4

2 files changed

Lines changed: 33 additions & 17 deletions

File tree

src/passes/Precompute.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ namespace wasm {
4141

4242
static const Name NOTPRECOMPUTABLE_FLOW("Binaryen|notprecomputable");
4343

44+
// Limit evaluation depth for 2 reasons: first, it is highly unlikely
45+
// that we can do anything useful to precompute a hugely nested expression
46+
// (we should succed at smaller parts of it first). Second, a low limit is
47+
// helpful to avoid platform differences in native stack sizes.
48+
static const Index MAX_DEPTH = 50;
49+
4450
typedef std::unordered_map<LocalGet*, Literal> GetValues;
4551

4652
// Precomputes an expression. Errors if we hit anything that can't be
@@ -63,8 +69,8 @@ class PrecomputingExpressionRunner
6369
PrecomputingExpressionRunner(Module* module,
6470
GetValues& getValues,
6571
bool replaceExpression)
66-
: module(module), getValues(getValues),
67-
replaceExpression(replaceExpression) {}
72+
: ExpressionRunner<PrecomputingExpressionRunner>(MAX_DEPTH), module(module),
73+
getValues(getValues), replaceExpression(replaceExpression) {}
6874

6975
struct NonstandaloneException {
7076
}; // TODO: use a flow with a special name, as this is likely very slow

src/wasm-interpreter.h

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ using namespace cashew;
4545

4646
extern Name WASM, RETURN_FLOW;
4747

48-
enum { maxInterpreterDepth = 50 };
49-
5048
// Stuff that flows around during executing expressions: a literal, or a change
5149
// in control flow.
5250
class Flow {
@@ -129,13 +127,16 @@ class Indenter {
129127
template<typename SubType>
130128
class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
131129
protected:
132-
// Keep a record of call depth, to guard against excessive recursion.
133-
size_t depth = 0;
130+
Index maxDepth;
131+
132+
Index depth = 0;
134133

135134
public:
135+
ExpressionRunner(Index maxDepth) : maxDepth(maxDepth) {}
136+
136137
Flow visit(Expression* curr) {
137138
depth++;
138-
if (depth > maxInterpreterDepth) {
139+
if (depth > maxDepth) {
139140
trap("interpreter recursion limit");
140141
}
141142
auto ret = OverriddenVisitor<SubType, Flow>::visit(curr);
@@ -1056,7 +1057,9 @@ class ConstantExpressionRunner
10561057
GlobalManager& globals;
10571058

10581059
public:
1059-
ConstantExpressionRunner(GlobalManager& globals) : globals(globals) {}
1060+
ConstantExpressionRunner(GlobalManager& globals, Index maxDepth)
1061+
: ExpressionRunner<ConstantExpressionRunner<GlobalManager>>(maxDepth),
1062+
globals(globals) {}
10601063

10611064
Flow visitGlobalGet(GlobalGet* curr) { return Flow(globals[curr->name]); }
10621065
};
@@ -1232,9 +1235,10 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
12321235
memorySize = wasm.memory.initial;
12331236
// generate internal (non-imported) globals
12341237
ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) {
1235-
globals[global->name] = ConstantExpressionRunner<GlobalManager>(globals)
1236-
.visit(global->init)
1237-
.value;
1238+
globals[global->name] =
1239+
ConstantExpressionRunner<GlobalManager>(globals, maxDepth)
1240+
.visit(global->init)
1241+
.value;
12381242
});
12391243

12401244
// initialize the rest of the external interface
@@ -1297,7 +1301,7 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
12971301
void initializeTableContents() {
12981302
for (auto& segment : wasm.table.segments) {
12991303
Address offset =
1300-
(uint32_t)ConstantExpressionRunner<GlobalManager>(globals)
1304+
(uint32_t)ConstantExpressionRunner<GlobalManager>(globals, maxDepth)
13011305
.visit(segment.offset)
13021306
.value.geti32();
13031307
if (offset + segment.data.size() > wasm.table.initial) {
@@ -1340,7 +1344,7 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
13401344
// the memory.init and data.drop instructions.
13411345
Function dummyFunc;
13421346
FunctionScope dummyScope(&dummyFunc, {});
1343-
RuntimeExpressionRunner runner(*this, dummyScope);
1347+
RuntimeExpressionRunner runner(*this, dummyScope, maxDepth);
13441348
runner.visit(&init);
13451349
runner.visit(&drop);
13461350
}
@@ -1387,8 +1391,11 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
13871391
FunctionScope& scope;
13881392

13891393
public:
1390-
RuntimeExpressionRunner(ModuleInstanceBase& instance, FunctionScope& scope)
1391-
: instance(instance), scope(scope) {}
1394+
RuntimeExpressionRunner(ModuleInstanceBase& instance,
1395+
FunctionScope& scope,
1396+
Index maxDepth)
1397+
: ExpressionRunner<RuntimeExpressionRunner>(maxDepth), instance(instance),
1398+
scope(scope) {}
13921399

13931400
Flow generateArguments(const ExpressionList& operands,
13941401
LiteralList& arguments) {
@@ -1788,7 +1795,7 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
17881795
// Internal function call. Must be public so that callTable implementations
17891796
// can use it (refactor?)
17901797
Literal callFunctionInternal(Name name, const LiteralList& arguments) {
1791-
if (callDepth > maxInterpreterDepth) {
1798+
if (callDepth > maxDepth) {
17921799
externalInterface->trap("stack limit");
17931800
}
17941801
auto previousCallDepth = callDepth;
@@ -1807,7 +1814,8 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
18071814
}
18081815
#endif
18091816

1810-
Flow flow = RuntimeExpressionRunner(*this, scope).visit(function->body);
1817+
Flow flow =
1818+
RuntimeExpressionRunner(*this, scope, maxDepth).visit(function->body);
18111819
// cannot still be breaking, it means we missed our stop
18121820
assert(!flow.breaking() || flow.breakTo == RETURN_FLOW);
18131821
Literal ret = flow.value;
@@ -1831,6 +1839,8 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
18311839
protected:
18321840
Address memorySize; // in pages
18331841

1842+
static const Index maxDepth = 250;
1843+
18341844
void trapIfGt(uint64_t lhs, uint64_t rhs, const char* msg) {
18351845
if (lhs > rhs) {
18361846
std::stringstream ss;

0 commit comments

Comments
 (0)