Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/ir/import-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ class ImportResolver {
// as long as the ImportResolver instance.
virtual RuntimeTable* getTableOrNull(ImportNames name,
const Table& type) const = 0;

virtual Tag* getTagOrNull(ImportNames name, const Signature& type) const = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're also going to need some kind of RuntimeTag abstraction, since multiple instances of the same modules generate distinct instantiations of the same tag definition.

Would that be better handled now or in a follow-up?

};

// Looks up imports from the given `linkedInstances`.
Expand Down Expand Up @@ -168,6 +170,16 @@ class LinkedInstancesImportResolver : public ImportResolver {
return instance->getExportedTableOrNull(name.name);
}

Tag* getTagOrNull(ImportNames name, const Signature& type) const override {
auto it = linkedInstances.find(name.module);
if (it == linkedInstances.end()) {
return nullptr;
}

ModuleRunnerType* instance = it->second.get();
return instance->getExportedTagOrNull(name.name);
}

private:
const std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances;
};
Expand Down
4 changes: 0 additions & 4 deletions src/shell-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
return Literal::makeFunc(import->name, import->type);
}

Tag* getImportedTag(Tag* tag) override {
WASM_UNREACHABLE("missing imported tag");
}

int8_t load8s(Address addr, Name memoryName) override {
auto it = memories.find(memoryName);
assert(it != memories.end());
Expand Down
89 changes: 63 additions & 26 deletions src/tools/execution-results.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,37 @@

namespace wasm {

namespace {

using Loggings = std::vector<Literal>;

Tag& getWasmTag() {
static Tag tag = []() {
Tag tag;
tag.module = "fuzzing-support";
tag.base = "wasmtag";
tag.name = "imported-wasm-tag";
tag.type = Signature(Type::i32, Type::none);

return tag;
}();
return tag;
}

Tag& getJsTag() {
static Tag tag = []() {
Tag tag;
tag.module = "fuzzing-support";
tag.base = "jstag";
tag.name = "imported-js-tag";
tag.type = Signature(Type(HeapType::ext, Nullable), Type::none);
return tag;
}();
return tag;
}

} // namespace

// Logs every relevant import call parameter.
struct LoggingExternalInterface : public ShellExternalInterface {
private:
Expand All @@ -43,10 +72,10 @@ struct LoggingExternalInterface : public ShellExternalInterface {
Module& wasm;

// The imported fuzzing tag for wasm.
Tag wasmTag;
const Tag& wasmTag;

// The imported tag for js exceptions.
Tag jsTag;
const Tag& jsTag;

// The ModuleRunner and this ExternalInterface end up needing links both ways,
// so we cannot init this in the constructor.
Expand All @@ -57,34 +86,14 @@ struct LoggingExternalInterface : public ShellExternalInterface {
Loggings& loggings,
Module& wasm,
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances_ = {})
: ShellExternalInterface(linkedInstances_), loggings(loggings), wasm(wasm) {
: ShellExternalInterface(linkedInstances_), loggings(loggings), wasm(wasm),
wasmTag(getWasmTag()), jsTag(getJsTag()) {
for (auto& exp : wasm.exports) {
if (exp->kind == ExternalKind::Table && exp->name == "table") {
exportedTable = *exp->getInternalName();
break;
}
}

// Set up tags. (Setting these values is useful for debugging - making the
// Tag objects valid - and also appears in fuzz-exec logging.)
wasmTag.module = "fuzzing-support";
wasmTag.base = "wasmtag";
wasmTag.name = "imported-wasm-tag";
wasmTag.type = Signature(Type::i32, Type::none);

jsTag.module = "fuzzing-support";
jsTag.base = "jstag";
jsTag.name = "imported-js-tag";
jsTag.type = Signature(Type(HeapType::ext, Nullable), Type::none);
}

Tag* getImportedTag(Tag* tag) override {
for (auto* imported : {&wasmTag, &jsTag}) {
if (imported->module == tag->module && imported->base == tag->base) {
return imported;
}
}
Fatal() << "missing host tag " << tag->module << '.' << tag->base;
}

Literal getImportedFunction(Function* import) override {
Expand Down Expand Up @@ -299,6 +308,27 @@ struct LoggingExternalInterface : public ShellExternalInterface {
void setModuleRunner(ModuleRunner* instance_) { instance = instance_; }
};

class FuzzerImportResolver
: public LinkedInstancesImportResolver<ModuleRunner> {
using LinkedInstancesImportResolver::LinkedInstancesImportResolver;
Tag* getTagOrNull(ImportNames name, const Signature& type) const override {
if (name.module == "fuzzing-support") {
if (name.name == "wasmtag") {
return &wasmTag;
}
if (name.name == "jstag") {
return &jsTag;
}
}

return LinkedInstancesImportResolver::getTagOrNull(name, type);
}

private:
Tag& wasmTag = getWasmTag();
Tag& jsTag = getJsTag();
};

// gets execution results from a wasm module. this is useful for fuzzing
//
// we can only get results when there are no imports. we then call each method
Expand All @@ -319,12 +349,19 @@ struct ExecutionResults {
try {
// Instantiate the first module.
LoggingExternalInterface interface(loggings, wasm);
auto instance = std::make_shared<ModuleRunner>(wasm, &interface);

// `linkedInstances` is empty at this point and the below constructors
// make copies.
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
auto instance = std::make_shared<ModuleRunner>(
wasm,
&interface,
linkedInstances,
std::make_shared<FuzzerImportResolver>(linkedInstances));
instantiate(*instance, interface);

// Instantiate the second, if there is one (we instantiate both before
// running anything, so that we match the behavior of fuzz_shell.js).
std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
std::unique_ptr<LoggingExternalInterface> secondInterface;
std::shared_ptr<ModuleRunner> secondInstance;
if (second) {
Expand Down
10 changes: 6 additions & 4 deletions src/tools/wasm-ctor-eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class EvallingImportResolver : public ImportResolver {
throw FailToEvalException{"Imported table access."};
}

Tag* getTagOrNull(ImportNames name,
const Signature& signature) const override {
Fatal() << "Not implemented.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fatal() << "Not implemented.";
Fatal() << "getTagOrNull not implemented in ctor-eval.";

Without this, "Not implemented" doesn't clarify well enough where the error happened, I think.

return nullptr;
}

private:
mutable Literals stubLiteral;
};
Expand Down Expand Up @@ -393,10 +399,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
import->type);
}

Tag* getImportedTag(Tag* tag) override {
WASM_UNREACHABLE("missing imported tag");
}

int8_t load8s(Address addr, Name memoryName) override {
return doLoad<int8_t>(addr, memoryName);
}
Expand Down
Loading
Loading