summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSoni L. <EnderMoneyMod@gmail.com>2024-09-23 17:01:18 -0300
committerGitHub <noreply@github.com>2024-09-23 13:01:18 -0700
commit0a2a59fae573c2d793501720a2faf986118ee4f4 (patch)
treea95fd371a30945944121f1ae816491b1daf1031e
parentb287470b53ef074332c4b0056cfc05486a85c4a7 (diff)
downloadwabt-0a2a59fae573c2d793501720a2faf986118ee4f4.tar.gz
wabt-0a2a59fae573c2d793501720a2faf986118ee4f4.tar.bz2
wabt-0a2a59fae573c2d793501720a2faf986118ee4f4.zip
wasm2c: Use wrappers for function references (#2465)
Clang 17(?) tightened UBSAN checks, so that you now get this: ``` - test/wasm2c/spec/call_indirect.txt expected error code 0, got 1. STDERR MISMATCH: --- expected +++ actual @@ -0,0 +1,3 @@ +out/test/wasm2c/spec/call_indirect/call_indirect.0.c:2144:12: runtime error: call to function w2c_call__indirect__0__wasm_f0 through pointer to incorrect function type 'unsigned int (*)(void *)' +/home/runner/work/wabt/wabt/out/test/wasm2c/spec/call_indirect/call_indirect.0.c:1925: note: w2c_call__indirect__0__wasm_f0 defined here +SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior out/test/wasm2c/spec/call_indirect/call_indirect.0.c:2144:12 STDOUT MISMATCH: --- expected +++ actual @@ -1 +0,0 @@ -134/134 tests passed. ``` This happens because emitted functions use a typed module instance, while function references use a `void*` instance. It is UB in C to call the former with the latter, so clang is correct here. We had to pick one of two ways to fix this: either emit `void*` wrapper functions that do the appropriate downcasting for any module functions that go into a table (potentially including imported functions), or the approach that takes significantly less effort of changing everything to `void*` and downcasting internally. ~~We obviously chose the latter.~~ We eventually started emitting wrapper functions.
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--include/wabt/ir.h7
-rw-r--r--src/binary-reader-ir.cc1
-rw-r--r--src/c-writer.cc70
-rw-r--r--test/wasm2c/check-imports.txt6
-rw-r--r--test/wasm2c/hello.txt6
-rw-r--r--test/wasm2c/tail-calls.txt6
7 files changed, 88 insertions, 10 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 92b5a088..99ffb6eb 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -99,7 +99,7 @@ jobs:
sanitize:
name: sanitize
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
env:
USE_NINJA: "1"
CC: "clang"
diff --git a/include/wabt/ir.h b/include/wabt/ir.h
index 49b7e2b0..80e1c2a8 100644
--- a/include/wabt/ir.h
+++ b/include/wabt/ir.h
@@ -25,6 +25,7 @@
#include <string_view>
#include <type_traits>
#include <vector>
+#include <set>
#include "wabt/binding-hash.h"
#include "wabt/common.h"
@@ -1280,6 +1281,12 @@ struct Module {
bool exceptions = false;
bool threads = false;
} features_used;
+
+ // The BinaryReaderIR tracks function references used by the module, whether
+ // in element segment initializers, global initializers, or functions. wasm2c
+ // needs to emit wrappers for any functions that might get used as function
+ // references, and uses this information to limit its output.
+ std::set<Index> used_func_refs;
};
enum class ScriptModuleType {
diff --git a/src/binary-reader-ir.cc b/src/binary-reader-ir.cc
index 8c79cd52..0b3e6f4d 100644
--- a/src/binary-reader-ir.cc
+++ b/src/binary-reader-ir.cc
@@ -1119,6 +1119,7 @@ Result BinaryReaderIR::OnTableFillExpr(Index table_index) {
}
Result BinaryReaderIR::OnRefFuncExpr(Index func_index) {
+ module_->used_func_refs.insert(func_index);
return AppendExpr(
std::make_unique<RefFuncExpr>(Var(func_index, GetLocation())));
}
diff --git a/src/c-writer.cc b/src/c-writer.cc
index cbffa1d4..9704f0e2 100644
--- a/src/c-writer.cc
+++ b/src/c-writer.cc
@@ -103,6 +103,11 @@ struct ExternalRef : GlobalName {
using GlobalName::GlobalName;
};
+struct WrapperRef : GlobalName {
+ explicit WrapperRef(const std::string& name)
+ : GlobalName(ModuleFieldType::Func, name) {}
+};
+
struct TailCallRef : GlobalName {
explicit TailCallRef(const std::string& name)
: GlobalName(ModuleFieldType::Func, name) {}
@@ -342,6 +347,7 @@ class CWriter {
void Write(const GlobalName&);
void Write(const TagSymbol&);
void Write(const ExternalRef&);
+ void Write(const WrapperRef&);
void Write(const TailCallRef&);
void Write(const ExternalInstancePtr&);
void Write(const ExternalInstanceRef&);
@@ -379,6 +385,7 @@ class CWriter {
void WriteImportFuncDeclaration(const FuncDeclaration&,
const std::string& module_name,
const std::string&);
+ void WriteFuncRefWrapper(const Func* func);
void WriteCallIndirectFuncDeclaration(const FuncDeclaration&,
const std::string&);
void ComputeSimdScope();
@@ -402,6 +409,7 @@ class CWriter {
void WriteDataInitializers();
void WriteElemInitializerDecls();
void WriteElemInitializers();
+ void WriteFuncRefWrappers();
void WriteElemTableInit(bool, const ElemSegment*, const Table*);
bool IsSingleUnsharedMemory();
void InstallSegueBase(Memory* memory, bool save_old_value);
@@ -544,6 +552,7 @@ static constexpr char kParamSuffix =
static constexpr char kLabelSuffix = kParamSuffix + 1;
static constexpr char kGlobalSymbolPrefix[] = "w2c_";
+static constexpr char kWrapperSymbolPrefix[] = "wrap_";
static constexpr char kLocalSymbolPrefix[] = "var_";
static constexpr char kAdminSymbolPrefix[] = "wasm2c_";
static constexpr char kTailCallSymbolPrefix[] = "wasm_tailcall_";
@@ -1125,6 +1134,10 @@ void CWriter::Write(const ExternalRef& name) {
}
}
+void CWriter::Write(const WrapperRef& name) {
+ Write(kWrapperSymbolPrefix, GlobalName(name));
+}
+
void CWriter::Write(const TailCallRef& name) {
Write(GetTailCallRef(name.name));
}
@@ -1446,8 +1459,7 @@ void CWriter::WriteInitExprTerminal(const Expr* expr) {
const FuncType* func_type = module_->GetFuncType(decl.type_var);
Write("(wasm_rt_funcref_t){", FuncTypeExpr(func_type), ", ",
- "(wasm_rt_function_ptr_t)",
- ExternalRef(ModuleFieldType::Func, func->name), ", {");
+ "(wasm_rt_function_ptr_t)", WrapperRef(func->name), ", {");
if (options_.features.tail_call_enabled() &&
(IsImport(func->name) || func->features_used.tailcall)) {
Write(TailCallRef(func->name));
@@ -2286,6 +2298,53 @@ void CWriter::WriteElemInitializerDecls() {
}
}
+void CWriter::WriteFuncRefWrapper(const Func* func) {
+ Write("static ", func->decl.sig.result_types, " ", WrapperRef(func->name));
+ Write("(void *instance");
+ if (func->GetNumParams() != 0) {
+ Indent(4);
+ for (Index i = 0; i < func->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0 && (i % 8) == 0) {
+ Write(Newline());
+ }
+ Write(func->GetParamType(i), " ", kLocalSymbolPrefix);
+ Writef("%d", i);
+ }
+ Dedent(4);
+ }
+ Write(") ", OpenBrace());
+ Write("return ");
+ Write(ExternalRef(ModuleFieldType::Func, func->name));
+ Write("(instance");
+ if (func->GetNumParams() != 0) {
+ Indent(4);
+ for (Index i = 0; i < func->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0 && (i % 8) == 0) {
+ Write(Newline());
+ }
+ Write(kLocalSymbolPrefix);
+ Writef("%d", i);
+ }
+ Dedent(4);
+ }
+ Write(");", Newline(), CloseBrace(), Newline());
+}
+
+void CWriter::WriteFuncRefWrappers() {
+ std::set<std::string> unique_func_wrappers;
+
+ for (Index index : module_->used_func_refs) {
+ assert(index < module_->funcs.size());
+ const Func* func = module_->funcs[index];
+ if (unique_func_wrappers.count(func->name) == 0) {
+ WriteFuncRefWrapper(func);
+ unique_func_wrappers.insert(func->name);
+ }
+ }
+}
+
void CWriter::WriteElemInitializers() {
if (module_->tables.empty()) {
return;
@@ -2315,8 +2374,7 @@ void CWriter::WriteElemInitializers() {
const Func* func = module_->GetFunc(cast<RefFuncExpr>(&expr)->var);
const FuncType* func_type = module_->GetFuncType(func->decl.type_var);
Write("{RefFunc, ", FuncTypeExpr(func_type),
- ", (wasm_rt_function_ptr_t)",
- ExternalRef(ModuleFieldType::Func, func->name), ", {");
+ ", (wasm_rt_function_ptr_t)", WrapperRef(func->name), ", {");
if (options_.features.tail_call_enabled() &&
(IsImport(func->name) || func->features_used.tailcall)) {
Write(TailCallRef(func->name));
@@ -3740,8 +3798,7 @@ void CWriter::Write(const ExprList& exprs) {
const FuncType* func_type = module_->GetFuncType(decl.type_var);
Write(StackVar(0), " = (wasm_rt_funcref_t){", FuncTypeExpr(func_type),
- ", (wasm_rt_function_ptr_t)",
- ExternalRef(ModuleFieldType::Func, func->name), ", {");
+ ", (wasm_rt_function_ptr_t)", WrapperRef(func->name), ", {");
if (options_.features.tail_call_enabled() &&
(IsImport(func->name) || func->features_used.tailcall)) {
Write(TailCallRef(func->name));
@@ -5858,6 +5915,7 @@ void CWriter::WriteCSource() {
WriteMultiCTop();
WriteFuncTypes();
WriteTags();
+ WriteFuncRefWrappers();
WriteGlobalInitializers();
WriteDataInitializers();
WriteElemInitializers();
diff --git a/test/wasm2c/check-imports.txt b/test/wasm2c/check-imports.txt
index f2fe508e..0947a201 100644
--- a/test/wasm2c/check-imports.txt
+++ b/test/wasm2c/check-imports.txt
@@ -802,11 +802,15 @@ FUNC_TYPE_T(w2c_test_t0) = "\x07\x80\x96\x7a\x42\xf7\x3e\xe6\x70\x5c\x2f\xac\x83
FUNC_TYPE_T(w2c_test_t1) = "\x72\xab\x00\xdf\x20\x3d\xce\xa1\xf2\x29\xc7\x9d\x13\x40\x7e\x98\xac\x7d\x41\x4a\x53\x2e\x42\x42\x61\x55\x2e\xaa\xeb\xbe\xc6\x35";
FUNC_TYPE_T(w2c_test_t2) = "\x92\xfb\x6a\xdf\x49\x07\x0a\x83\xbe\x08\x02\x68\xcd\xf6\x95\x27\x4a\xc2\xf3\xe5\xe4\x7d\x29\x49\xe8\xed\x42\x92\x6a\x9d\xda\xf0";
+static u32 wrap_w2c_test_f1(void *instance) {
+ return w2c_test_f1(instance);
+}
+
static void init_memories(w2c_test* instance) {
}
static const wasm_elem_segment_expr_t elem_segment_exprs_w2c_test_e0[] = {
- {RefFunc, w2c_test_t1, (wasm_rt_function_ptr_t)w2c_test_f1, {NULL}, 0},
+ {RefFunc, w2c_test_t1, (wasm_rt_function_ptr_t)wrap_w2c_test_f1, {NULL}, 0},
};
static void init_tables(w2c_test* instance) {
diff --git a/test/wasm2c/hello.txt b/test/wasm2c/hello.txt
index 2a0d79f0..918efe8c 100644
--- a/test/wasm2c/hello.txt
+++ b/test/wasm2c/hello.txt
@@ -808,6 +808,10 @@ FUNC_TYPE_T(w2c_test_t0) = "\xf6\x98\x1b\xc6\x10\xda\xb7\xb2\x63\x37\xcd\xdc\x72
FUNC_TYPE_T(w2c_test_t1) = "\x89\x3a\x3d\x2c\x8f\x4d\x7f\x6d\x6c\x9d\x62\x67\x29\xaf\x3d\x44\x39\x8e\xc3\xf3\xe8\x51\xc1\x99\xb9\xdd\x9f\xd5\x3d\x1f\xd3\xe4";
FUNC_TYPE_T(w2c_test_t2) = "\x36\xa9\xe7\xf1\xc9\x5b\x82\xff\xb9\x97\x43\xe0\xc5\xc4\xce\x95\xd8\x3c\x9a\x43\x0a\xac\x59\xf8\x4e\xf3\xcb\xfa\xb6\x14\x50\x68";
+static u32 wrap_w2c_wasi__snapshot__preview1_fd_write(void *instance, u32 var_0, u32 var_1, u32 var_2, u32 var_3) {
+ return w2c_wasi__snapshot__preview1_fd_write(instance, var_0, var_1, var_2, var_3);
+}
+
static const u8 data_segment_data_w2c_test_d0[] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0x2e, 0x0a,
@@ -822,7 +826,7 @@ static void init_data_instances(w2c_test *instance) {
}
static const wasm_elem_segment_expr_t elem_segment_exprs_w2c_test_e0[] = {
- {RefFunc, w2c_test_t0, (wasm_rt_function_ptr_t)w2c_wasi__snapshot__preview1_fd_write, {NULL}, offsetof(w2c_test, w2c_wasi__snapshot__preview1_instance)},
+ {RefFunc, w2c_test_t0, (wasm_rt_function_ptr_t)wrap_w2c_wasi__snapshot__preview1_fd_write, {NULL}, offsetof(w2c_test, w2c_wasi__snapshot__preview1_instance)},
};
static void init_tables(w2c_test* instance) {
diff --git a/test/wasm2c/tail-calls.txt b/test/wasm2c/tail-calls.txt
index b27d7417..bbc02c07 100644
--- a/test/wasm2c/tail-calls.txt
+++ b/test/wasm2c/tail-calls.txt
@@ -811,8 +811,12 @@ FUNC_TYPE_T(w2c_test_i32_f32) = "\x98\x89\x5c\xbd\x28\xfd\x0e\x4d\xc5\xdc\x68\x2
FUNC_TYPE_T(w2c_test_t1) = "\x36\xa9\xe7\xf1\xc9\x5b\x82\xff\xb9\x97\x43\xe0\xc5\xc4\xce\x95\xd8\x3c\x9a\x43\x0a\xac\x59\xf8\x4e\xf3\xcb\xfa\xb6\x14\x50\x68";
FUNC_TYPE_T(w2c_test_t2) = "\xe5\x11\x86\xc7\x24\xdb\x44\x80\xbe\xd1\xe0\x89\xbc\xc0\x20\xea\xfb\x1c\x9a\x27\xa5\xc3\xdb\xca\x5d\xb0\x05\x0f\x7c\x03\x74\x0a";
+static void wrap_w2c_spectest_print_i32_f32(void *instance, u32 var_0, f32 var_1) {
+ return w2c_spectest_print_i32_f32(instance, var_0, var_1);
+}
+
static const wasm_elem_segment_expr_t elem_segment_exprs_w2c_test_e0[] = {
- {RefFunc, w2c_test_i32_f32, (wasm_rt_function_ptr_t)w2c_spectest_print_i32_f32, {wasm_tailcall_w2c_spectest_print_i32_f32}, offsetof(w2c_test, w2c_spectest_instance)},
+ {RefFunc, w2c_test_i32_f32, (wasm_rt_function_ptr_t)wrap_w2c_spectest_print_i32_f32, {wasm_tailcall_w2c_spectest_print_i32_f32}, offsetof(w2c_test, w2c_spectest_instance)},
};
static void init_tables(w2c_test* instance) {