Skip to content

Commit 6da01b1

Browse files
authored
Add test for native to wasm value conversions (#525)
This test explicitly tests the bounds of various native and wasm types to ensure passing data across the ABI boundary is not lossy. --------- Signed-off-by: Matt Leon <mattleon@google.com>
1 parent 92108ac commit 6da01b1

12 files changed

Lines changed: 419 additions & 7 deletions

File tree

include/proxy-wasm/wasm_vm.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,21 @@ template <size_t N>
6767
using WasmCallVoid = std::function<WasmCallInFuncType<N, void, ContextBase *, Word>>;
6868
template <size_t N>
6969
using WasmCallWord = std::function<WasmCallInFuncType<N, Word, ContextBase *, Word>>;
70+
// Callback used to test arg passing from host to wasm.
71+
using WasmCall_WWlfd = std::function<Word(ContextBase *, Word, uint64_t, float, double)>;
72+
// Types used to test return values. Floats are passed as parameters as these
73+
// do not conflict with ProxyWasm ABI signatures.
74+
using WasmCall_lf = std::function<uint64_t(ContextBase *, float)>;
75+
using WasmCall_fff = std::function<float(ContextBase *, float, float)>;
76+
using WasmCall_dfff = std::function<double(ContextBase *, float, float, float)>;
7077

7178
#define FOR_ALL_WASM_VM_EXPORTS(_f) \
7279
_f(proxy_wasm::WasmCallVoid<0>) _f(proxy_wasm::WasmCallVoid<1>) _f(proxy_wasm::WasmCallVoid<2>) \
7380
_f(proxy_wasm::WasmCallVoid<3>) _f(proxy_wasm::WasmCallVoid<5>) \
7481
_f(proxy_wasm::WasmCallWord<0>) _f(proxy_wasm::WasmCallWord<1>) \
75-
_f(proxy_wasm::WasmCallWord<2>) _f(proxy_wasm::WasmCallWord<3>)
82+
_f(proxy_wasm::WasmCallWord<2>) _f(proxy_wasm::WasmCallWord<3>) \
83+
_f(proxy_wasm::WasmCall_WWlfd) _f(proxy_wasm::WasmCall_lf) \
84+
_f(proxy_wasm::WasmCall_fff) _f(proxy_wasm::WasmCall_dfff)
7685

7786
// These are templates and its helper for constructing signatures of functions callbacks from Wasm
7887
// VMs.

include/proxy-wasm/word.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,16 @@ namespace proxy_wasm {
2424
// Use byteswap functions only when compiling for big-endian platforms.
2525
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
2626
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
27-
#define htowasm(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? __builtin_bswap32(x) : (x))
28-
#define wasmtoh(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? __builtin_bswap32(x) : (x))
27+
static inline float bswap(float x) {
28+
return std::bit_cast<float>(__builtin_bswap32(std::bit_cast<int32_t>(x)));
29+
}
30+
static inline double bswap(double x) {
31+
return std::bit_cast<double>(__builtin_bswap64(std::bit_cast<int64_t>(x)));
32+
}
33+
static inline uint32_t bswap(uint32_t x) { return __builtin_bswap32(x); }
34+
static inline auto bswap(auto x) { return __builtin_bswap64(x); }
35+
#define htowasm(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? bswap(x) : (x))
36+
#define wasmtoh(x, vm_uses_wasm_byte_order) ((vm_uses_wasm_byte_order) ? bswap(x) : (x))
2937
#else
3038
#define htowasm(x, vm_uses_wasm_byte_order) (x)
3139
#define wasmtoh(x, vm_uses_wasm_byte_order) (x)

src/v8/v8.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ template <> constexpr auto convertArgToValKind<Word>() { return wasm::ValKind::I
244244
template <> constexpr auto convertArgToValKind<uint32_t>() { return wasm::ValKind::I32; };
245245
template <> constexpr auto convertArgToValKind<int64_t>() { return wasm::ValKind::I64; };
246246
template <> constexpr auto convertArgToValKind<uint64_t>() { return wasm::ValKind::I64; };
247+
template <> constexpr auto convertArgToValKind<float>() { return wasm::ValKind::F32; };
247248
template <> constexpr auto convertArgToValKind<double>() { return wasm::ValKind::F64; };
248249

249250
template <typename T, std::size_t... I>

src/wamr/wamr.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) {
462462
val.kind = WASM_I64;
463463
val.of.i64 = static_cast<int64_t>(t);
464464
}
465+
template <> void assignVal(float t, wasm_val_t &val) {
466+
val.kind = WASM_F32;
467+
val.of.f32 = static_cast<float>(t);
468+
}
465469
template <> void assignVal(double t, wasm_val_t &val) {
466470
val.kind = WASM_F64;
467471
val.of.f64 = t;
@@ -485,17 +489,21 @@ template <> auto convertArgToValTypePtr<Word>() { return wasm_valtype_new_i32();
485489
template <> auto convertArgToValTypePtr<uint32_t>() { return wasm_valtype_new_i32(); };
486490
template <> auto convertArgToValTypePtr<int64_t>() { return wasm_valtype_new_i64(); };
487491
template <> auto convertArgToValTypePtr<uint64_t>() { return wasm_valtype_new_i64(); };
492+
template <> auto convertArgToValTypePtr<float>() { return wasm_valtype_new_f32(); };
488493
template <> auto convertArgToValTypePtr<double>() { return wasm_valtype_new_f64(); };
489494

490495
template <typename T> T convertValueTypeToArg(wasm_val_t val);
491496
template <> uint32_t convertValueTypeToArg<uint32_t>(wasm_val_t val) {
492497
return static_cast<uint32_t>(val.of.i32);
493498
}
494-
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) { return val.of.i32; }
499+
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) {
500+
return static_cast<uint32_t>(val.of.i32);
501+
}
495502
template <> int64_t convertValueTypeToArg<int64_t>(wasm_val_t val) { return val.of.i64; }
496503
template <> uint64_t convertValueTypeToArg<uint64_t>(wasm_val_t val) {
497504
return static_cast<uint64_t>(val.of.i64);
498505
}
506+
template <> float convertValueTypeToArg<float>(wasm_val_t val) { return val.of.f32; }
499507
template <> double convertValueTypeToArg<double>(wasm_val_t val) { return val.of.f64; }
500508

501509
template <typename T, typename U, std::size_t... I>

src/wasmedge/wasmedge.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ template <> WasmEdge_Value makeVal(uint32_t t) {
4848
template <> WasmEdge_Value makeVal(uint64_t t) {
4949
return WasmEdge_ValueGenI64(static_cast<int64_t>(t));
5050
}
51+
template <> WasmEdge_Value makeVal(float t) { return WasmEdge_ValueGenF32(t); }
5152
template <> WasmEdge_Value makeVal(double t) { return WasmEdge_ValueGenF64(t); }
5253

5354
// Helper function to print values.
@@ -143,20 +144,24 @@ template <> WasmEdge_ValType convArgToValType<Word>() { return WasmEdge_ValTypeG
143144
template <> WasmEdge_ValType convArgToValType<uint32_t>() { return WasmEdge_ValTypeGenI32(); }
144145
template <> WasmEdge_ValType convArgToValType<int64_t>() { return WasmEdge_ValTypeGenI64(); }
145146
template <> WasmEdge_ValType convArgToValType<uint64_t>() { return WasmEdge_ValTypeGenI64(); }
147+
template <> WasmEdge_ValType convArgToValType<float>() { return WasmEdge_ValTypeGenF32(); }
146148
template <> WasmEdge_ValType convArgToValType<double>() { return WasmEdge_ValTypeGenF64(); }
147149

148150
// Helper templates to convert valtype to arg.
149151
template <typename T> T convValTypeToArg(WasmEdge_Value val);
150152
template <> uint32_t convValTypeToArg<uint32_t>(WasmEdge_Value val) {
151153
return static_cast<uint32_t>(WasmEdge_ValueGetI32(val));
152154
}
153-
template <> Word convValTypeToArg<Word>(WasmEdge_Value val) { return WasmEdge_ValueGetI32(val); }
155+
template <> Word convValTypeToArg<Word>(WasmEdge_Value val) {
156+
return static_cast<uint32_t>(WasmEdge_ValueGetI32(val));
157+
}
154158
template <> int64_t convValTypeToArg<int64_t>(WasmEdge_Value val) {
155159
return WasmEdge_ValueGetI64(val);
156160
}
157161
template <> uint64_t convValTypeToArg<uint64_t>(WasmEdge_Value val) {
158162
return static_cast<uint64_t>(WasmEdge_ValueGetI64(val));
159163
}
164+
template <> float convValTypeToArg<float>(WasmEdge_Value val) { return WasmEdge_ValueGetF32(val); }
160165
template <> double convValTypeToArg<double>(WasmEdge_Value val) {
161166
return WasmEdge_ValueGetF64(val);
162167
}

src/wasmtime/wasmtime.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,10 @@ template <> void assignVal(uint64_t t, wasm_val_t &val) {
435435
val.kind = WASM_I64;
436436
val.of.i64 = static_cast<int64_t>(t);
437437
}
438+
template <> void assignVal(float t, wasm_val_t &val) {
439+
val.kind = WASM_F32;
440+
val.of.f32 = t;
441+
}
438442
template <> void assignVal(double t, wasm_val_t &val) {
439443
val.kind = WASM_F64;
440444
val.of.f64 = t;
@@ -458,17 +462,21 @@ template <> auto convertArgToValTypePtr<Word>() { return wasm_valtype_new_i32();
458462
template <> auto convertArgToValTypePtr<uint32_t>() { return wasm_valtype_new_i32(); };
459463
template <> auto convertArgToValTypePtr<int64_t>() { return wasm_valtype_new_i64(); };
460464
template <> auto convertArgToValTypePtr<uint64_t>() { return wasm_valtype_new_i64(); };
465+
template <> auto convertArgToValTypePtr<float>() { return wasm_valtype_new_f32(); };
461466
template <> auto convertArgToValTypePtr<double>() { return wasm_valtype_new_f64(); };
462467

463468
template <typename T> T convertValueTypeToArg(wasm_val_t val);
464469
template <> uint32_t convertValueTypeToArg<uint32_t>(wasm_val_t val) {
465470
return static_cast<uint32_t>(val.of.i32);
466471
}
467-
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) { return val.of.i32; }
472+
template <> Word convertValueTypeToArg<Word>(wasm_val_t val) {
473+
return static_cast<uint32_t>(val.of.i32);
474+
}
468475
template <> int64_t convertValueTypeToArg<int64_t>(wasm_val_t val) { return val.of.i64; }
469476
template <> uint64_t convertValueTypeToArg<uint64_t>(wasm_val_t val) {
470477
return static_cast<uint64_t>(val.of.i64);
471478
}
479+
template <> float convertValueTypeToArg<float>(wasm_val_t val) { return val.of.f32; }
472480
template <> double convertValueTypeToArg<double>(wasm_val_t val) { return val.of.f64; }
473481

474482
template <typename T, typename U, std::size_t... I>

test/BUILD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,22 @@ cc_test(
8484
],
8585
)
8686

87+
cc_test(
88+
name = "endianness_test",
89+
timeout = "long",
90+
srcs = ["endianness_test.cc"],
91+
data = [
92+
"//test/test_data:endianness.wasm",
93+
],
94+
linkstatic = 1,
95+
deps = [
96+
":utility_lib",
97+
"//:lib",
98+
"@com_google_googletest//:gtest",
99+
"@com_google_googletest//:gtest_main",
100+
],
101+
)
102+
87103
cc_test(
88104
name = "exports_test",
89105
srcs = ["exports_test.cc"],

test/endianness_test.cc

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "gtest/gtest.h"
16+
#include "gmock/gmock.h"
17+
18+
#include <optional>
19+
20+
#include "include/proxy-wasm/context.h"
21+
#include "include/proxy-wasm/wasm.h"
22+
23+
#include "test/utility.h"
24+
25+
namespace proxy_wasm {
26+
namespace {
27+
28+
class EndiannessContext : public TestContext {
29+
public:
30+
using TestContext::TestContext;
31+
WasmResult getHeaderMapPairs(WasmHeaderMapType /* type */, Pairs * /* result */) override {
32+
// GetHeaderMapPairs passes this value as the hostcall return value as
33+
// opposed to output parameter.
34+
return static_cast<WasmResult>(3333333333U);
35+
}
36+
};
37+
38+
class EndiannessWasm : public TestWasm {
39+
public:
40+
using TestWasm::TestWasm;
41+
ContextBase *createVmContext() override { return new EndiannessContext(this); };
42+
};
43+
44+
class EndiannessTest : public TestVm {
45+
public:
46+
void SetUp() {
47+
auto source = readTestWasmFile("endianness.wasm");
48+
ASSERT_FALSE(source.empty());
49+
wasm_.emplace(std::move(vm_));
50+
ASSERT_TRUE(wasm_->load(source, false));
51+
ASSERT_TRUE(wasm_->initialize());
52+
context_ = dynamic_cast<EndiannessContext *>(wasm_->vm_context());
53+
ASSERT_NE(context_, nullptr);
54+
}
55+
56+
std::optional<EndiannessWasm> wasm_;
57+
EndiannessContext *context_;
58+
};
59+
60+
INSTANTIATE_TEST_SUITE_P(WasmEngines, EndiannessTest, testing::ValuesIn(getWasmEngines()),
61+
[](const testing::TestParamInfo<std::string> &info) {
62+
return info.param;
63+
});
64+
65+
TEST_P(EndiannessTest, WasmCallReturnsWordValue) {
66+
WasmCallWord<0> test_return_u32;
67+
wasm_->wasm_vm()->getFunction("test_return_u32", &test_return_u32);
68+
69+
EXPECT_EQ(test_return_u32(context_).u32(), 3333333333U) << context_->getLog();
70+
}
71+
72+
TEST_P(EndiannessTest, WasmCallReturnsNegativeWordValue) {
73+
WasmCallWord<0> test_return_i32;
74+
wasm_->wasm_vm()->getFunction("test_return_i32", &test_return_i32);
75+
76+
EXPECT_EQ(test_return_i32(context_).u32(), -1111111111) << context_->getLog();
77+
}
78+
79+
TEST_P(EndiannessTest, WasmCallReturnsUnsignedLongValue) {
80+
WasmCall_lf test_return_u64;
81+
wasm_->wasm_vm()->getFunction("test_return_u64", &test_return_u64);
82+
83+
EXPECT_EQ(test_return_u64(context_, 1.0), 11111111111111111111UL) << context_->getLog();
84+
}
85+
86+
TEST_P(EndiannessTest, WasmCallReturnsNegativeLongValue) {
87+
WasmCall_lf test_return_i64;
88+
wasm_->wasm_vm()->getFunction("test_return_i64", &test_return_i64);
89+
90+
EXPECT_EQ(test_return_i64(context_, 1.0), -111111111111111111L) << context_->getLog();
91+
}
92+
93+
TEST_P(EndiannessTest, WasmCallReturnsFloatValue) {
94+
WasmCall_fff test_return_f32;
95+
wasm_->wasm_vm()->getFunction("test_return_f32", &test_return_f32);
96+
97+
EXPECT_THAT(test_return_f32(context_, 1.0, 1.0),
98+
testing::AllOf(testing::Lt(1112.0), testing::Gt(1110.0)))
99+
<< context_->getLog();
100+
}
101+
102+
TEST_P(EndiannessTest, WasmCallReturnsDoubleValue) {
103+
WasmCall_dfff test_return_f64;
104+
wasm_->wasm_vm()->getFunction("test_return_f64", &test_return_f64);
105+
106+
EXPECT_THAT(test_return_f64(context_, 1.0, 1.0, 1.0),
107+
testing::AllOf(testing::Lt(1111111112.0), testing::Gt(1111111110.0)))
108+
<< context_->getLog();
109+
}
110+
111+
TEST_P(EndiannessTest, HostCallReturnsWordValue) {
112+
WasmCallWord<0> test_host_return;
113+
wasm_->wasm_vm()->getFunction("test_host_return", &test_host_return);
114+
115+
EXPECT_TRUE(test_host_return(context_)) << context_->getLog();
116+
}
117+
118+
TEST_P(EndiannessTest, HostPassesPrimitiveValues) {
119+
WasmCall_WWlfd test_primitives;
120+
wasm_->wasm_vm()->getFunction("test_primitives", &test_primitives);
121+
122+
ASSERT_TRUE(test_primitives(context_, 3333333333U, 11111111111111111111UL, 1111, 1111111111))
123+
<< context_->getLog();
124+
}
125+
126+
TEST_P(EndiannessTest, HostPassesNegativePrimitiveValues) {
127+
WasmCall_WWlfd test_negative_primitives;
128+
wasm_->wasm_vm()->getFunction("test_negative_primitives", &test_negative_primitives);
129+
130+
ASSERT_TRUE(
131+
test_negative_primitives(context_, -1111111111, -1111111111111111111, -1111, -1111111111))
132+
<< context_->getLog();
133+
}
134+
135+
TEST_P(EndiannessTest, HostReadsPointersToWasmMemory) {
136+
WasmCallWord<0> test_buffer_from_wasm;
137+
wasm_->wasm_vm()->getFunction("test_buffer_from_wasm", &test_buffer_from_wasm);
138+
139+
ASSERT_TRUE(test_buffer_from_wasm(context_)) << context_->getLog();
140+
141+
context_->isLogged("hello from wasm land!");
142+
}
143+
144+
TEST_P(EndiannessTest, WasmCallReadsBufferPassedByHost) {
145+
context_->setBuffer(0, "hello from host land!");
146+
WasmCallWord<0> test_buffer_from_host;
147+
wasm_->wasm_vm()->getFunction("test_buffer_from_host", &test_buffer_from_host);
148+
149+
ASSERT_TRUE(test_buffer_from_host(context_)) << context_->getLog();
150+
}
151+
152+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndiannessTest);
153+
154+
} // namespace
155+
} // namespace proxy_wasm

test/test_data/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ wasm_rust_binary(
5757
srcs = ["trap.rs"],
5858
)
5959

60+
wasm_rust_binary(
61+
name = "endianness.wasm",
62+
srcs = ["endianness.rs"],
63+
)
64+
6065
wasm_rust_binary(
6166
name = "resource_limits.wasm",
6267
srcs = ["resource_limits.rs"],

0 commit comments

Comments
 (0)