Skip to content

Commit aa63272

Browse files
Use shared_from_this to avoid lifetime issues with ModuleRunner
1 parent 28e849b commit aa63272

File tree

5 files changed

+66
-35
lines changed

5 files changed

+66
-35
lines changed

src/shell-interface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
148148
// Use a null instance because these are host functions.
149149
return Literal(
150150
std::make_shared<FuncData>(import->name,
151-
nullptr,
151+
/*self=*/0,
152152
[](const Literals& arguments) -> Flow {
153153
for (auto argument : arguments) {
154154
std::cout << argument << " : "
@@ -159,7 +159,7 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
159159
import->type);
160160
} else if (import->module == ENV && import->base == EXIT) {
161161
return Literal(std::make_shared<FuncData>(import->name,
162-
nullptr,
162+
/*self=*/0,
163163
[](const Literals&) -> Flow {
164164
// XXX hack for torture tests
165165
std::cout << "exit()\n";

src/tools/execution-results.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ struct LoggingExternalInterface : public ShellExternalInterface {
210210
return {};
211211
};
212212
// Use a null instance because this is a host function.
213-
return Literal(std::make_shared<FuncData>(import->name, nullptr, f),
213+
return Literal(std::make_shared<FuncData>(import->name, /*self=*/0, f),
214214
import->type);
215215
}
216216

src/tools/wasm-ctor-eval.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,12 @@ class EvallingModuleRunner : public ModuleRunnerBase<EvallingModuleRunner> {
104104

105105
// This needs to be duplicated from ModuleRunner, unfortunately.
106106
Literal makeFuncData(Name name, Type type) {
107-
auto allocation =
108-
std::make_shared<FuncData>(name, this, [this, name](Literals arguments) {
109-
return callFunction(name, arguments);
107+
auto allocation = std::make_shared<FuncData>(
108+
name,
109+
reinterpret_cast<uintptr_t>(this),
110+
[self = shared_from_this(), name](Literals arguments) {
111+
return self->callFunction(name, arguments);
110112
});
111-
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
112-
__lsan_ignore_object(allocation.get());
113-
#endif
114113
return Literal(allocation, type);
115114
}
116115
};
@@ -320,7 +319,7 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
320319
// Use a null instance because these are either host functions or imported
321320
// from unknown sources.
322321
// TODO: Be more precise about the types we allow these imports to have.
323-
return Literal(std::make_shared<FuncData>(import->name, nullptr, f),
322+
return Literal(std::make_shared<FuncData>(import->name, /*self=*/0, f),
324323
import->type);
325324
}
326325

src/wasm-interpreter.h

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Flow {
8686
}
8787

8888
Literals values;
89-
Name breakTo; // if non-null, a break is going on
89+
Name breakTo; // if non-null, a break is going on
9090
Tag* suspendTag = nullptr; // if non-null, breakTo must be SUSPEND_FLOW, and
9191
// this is the tag being suspended
9292

@@ -137,17 +137,16 @@ struct FuncData {
137137

138138
// The interpreter instance this function closes over, if any. (There might
139139
// not be an interpreter instance if this is a host function or an import from
140-
// an unknown source.) This is only used for equality comparisons, as two
141-
// functions are equal iff they have the same name and are defined by the same
142-
// instance (in particular, we do *not* compare the |call| field below, which
143-
// is an execution detail).
144-
void* self;
140+
// an unknown source.) Two functions are equal iff they have the same name and
141+
// are defined by the same instance (in particular, we do *not* compare the
142+
// |call| field below, which is an execution detail).
143+
uintptr_t self;
145144

146145
// A way to execute this function. We use this when it is called.
147146
using Call = std::function<Flow(const Literals&)>;
148147
Call call;
149148

150-
FuncData(Name name, void* self = nullptr, Call call = {})
149+
FuncData(Name name, uintptr_t self = 0, Call call = {})
151150
: name(name), self(self), call(call) {}
152151

153152
bool operator==(const FuncData& other) const {
@@ -362,10 +361,8 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
362361
Literal makeFuncData(Name name, Type type) {
363362
// Identify the interpreter, but do not provide a way to actually call the
364363
// function.
365-
auto allocation = std::make_shared<FuncData>(name, this);
366-
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
367-
__lsan_ignore_object(allocation.get());
368-
#endif
364+
auto allocation =
365+
std::make_shared<FuncData>(name, reinterpret_cast<uintptr_t>(this));
369366
return Literal(allocation, type);
370367
}
371368

@@ -2917,7 +2914,9 @@ using GlobalValueSet = std::map<Name, Literals>;
29172914
//
29182915

29192916
template<typename SubType>
2920-
class ModuleRunnerBase : public ExpressionRunner<SubType> {
2917+
class ModuleRunnerBase
2918+
: public ExpressionRunner<SubType>,
2919+
public std::enable_shared_from_this<ModuleRunnerBase<SubType>> {
29212920
public:
29222921
//
29232922
// You need to implement one of these to create a concrete interpreter. The
@@ -3207,9 +3206,10 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
32073206
}
32083207
return Literal(std::make_shared<FuncData>(
32093208
func->name,
3210-
this,
3211-
[this, func](const Literals& arguments) -> Flow {
3212-
return callFunction(func->name, arguments);
3209+
reinterpret_cast<uintptr_t>(this),
3210+
[self = this->shared_from_this(),
3211+
func](const Literals& arguments) -> Flow {
3212+
return self->callFunction(func->name, arguments);
32133213
}),
32143214
func->type);
32153215
}
@@ -4774,7 +4774,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
47744774
auto func = entry[0];
47754775
auto data = func.getFuncData();
47764776
// We must be in the right module to do the call using that name.
4777-
if (data->self != self()) {
4777+
if (data->self != (uintptr_t)self()) {
47784778
// Restore the entry to the resume stack, as the other module's
47794779
// callFunction() will read it. Then call into the other module. This
47804780
// sets this up as if we called into the proper module in the first
@@ -4866,10 +4866,9 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
48664866
flow.values.pop_back();
48674867
arguments = flow.values;
48684868

4869-
if (nextData->self != this) {
4869+
if (nextData->self != (uintptr_t)self()) {
48704870
// This function is in another module. Call from there.
4871-
auto other = (decltype(this))nextData->self;
4872-
flow = other->callFunction(name, arguments);
4871+
flow = nextData->doCall(arguments);
48734872
break;
48744873
}
48754874
}
@@ -5010,13 +5009,16 @@ class ModuleRunner : public ModuleRunnerBase<ModuleRunner> {
50105009
Literal makeFuncData(Name name, Type type) {
50115010
// As the super's |makeFuncData|, but here we also provide a way to
50125011
// actually call the function.
5013-
auto allocation =
5014-
std::make_shared<FuncData>(name, this, [this, name](Literals arguments) {
5015-
return callFunction(name, arguments);
5012+
auto allocation = std::make_shared<FuncData>(
5013+
name,
5014+
reinterpret_cast<uintptr_t>(this),
5015+
[moduleRunner = std::static_pointer_cast<ModuleRunner>(
5016+
std::enable_shared_from_this<
5017+
ModuleRunnerBase<ModuleRunner>>::shared_from_this()),
5018+
name](Literals arguments) {
5019+
return moduleRunner->callFunction(name, arguments);
50165020
});
5017-
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
5018-
__lsan_ignore_object(allocation.get());
5019-
#endif
5021+
50205022
return Literal(allocation, type);
50215023
}
50225024
};

test/spec/linking0.wast

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
;; adapted from test/spec/testsuite/linking0.wast, with some assertions removed
2+
3+
(module $Mt
4+
(type (func (result i32)))
5+
(type (func))
6+
7+
(table (export "tab") 10 funcref)
8+
(elem (i32.const 2) $g $g $g $g)
9+
(func $g (result i32) (i32.const 4))
10+
(func (export "h") (result i32) (i32.const -4))
11+
12+
(func (export "call") (param i32) (result i32)
13+
(call_indirect (type 0) (local.get 0))
14+
)
15+
)
16+
(register "Mt" $Mt)
17+
18+
(assert_trap
19+
(module
20+
(table (import "Mt" "tab") 10 funcref)
21+
(func $f (result i32) (i32.const 0))
22+
(elem (i32.const 7) $f)
23+
(memory 0)
24+
(memory $m 1)
25+
(memory 0)
26+
(data $m (i32.const 0x10000) "d") ;; out of bounds
27+
)
28+
"out of bounds memory access"
29+
)
30+
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))

0 commit comments

Comments
 (0)