From c626b6403a580286593a6bcd1f0c0386a529bfaa Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:40:42 -0500 Subject: [PATCH] Fix unaligned access (#6208) --- src/test/app/HostFuncImpl_test.cpp | 18 ++++++++++++ src/test/app/Wasm_test.cpp | 28 +++++++++++++++++++ src/test/app/wasm_fixtures/bad_align.c | 23 +++++++++++++++ src/test/app/wasm_fixtures/fixtures.cpp | 17 +++++++++++ src/test/app/wasm_fixtures/fixtures.h | 1 + src/xrpld/app/wasm/detail/HostFuncImpl.cpp | 12 ++++++-- src/xrpld/app/wasm/detail/HostFuncWrapper.cpp | 9 +++++- 7 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/test/app/wasm_fixtures/bad_align.c diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index 9a5326e18d..b53126671b 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -594,6 +594,24 @@ struct HostFuncImpl_test : public beast::unit_test::suite } } + { + // unaligned locator + std::vector locatorVec(sizeof(int32_t) + 1); + memcpy( + locatorVec.data() + 1, &sfAccount.fieldCode, sizeof(int32_t)); + Slice locator( + reinterpret_cast(locatorVec.data() + 1), + sizeof(int32_t)); + + auto const account = hfs.getTxNestedField(locator); + if (BEAST_EXPECTS( + account.has_value(), + std::to_string(static_cast(account.error())))) + { + BEAST_EXPECT(std::ranges::equal(*account, env.master.id())); + } + } + auto expectError = [&](std::vector const& locatorVec, HostFunctionError expectedError) { Slice locator( diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index c61482a057..0eca654335 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -965,6 +965,33 @@ struct Wasm_test : public beast::unit_test::suite env.close(); } + void + testBadAlign() + { + testcase("Wasm Bad Align"); + + // bad_align.c + auto wasmStr = boost::algorithm::unhex(badAlignHex); + Bytes wasm(wasmStr.begin(), wasmStr.end()); + + using namespace test::jtx; + + Env env{*this}; + TestHostFunctions hf(env); + ImportVec imports = createWasmImport(hf); + + { // Calls float_from_uint with bad aligment. + // Can be checked through codecov + auto& engine = WasmEngine::instance(); + + auto re = engine.run( + wasm, "test", {}, imports, &hf, 1'000'000, env.journal); + BEAST_EXPECT(re && re->result == 0xbab88d46); + } + + env.close(); + } + void run() override { @@ -995,6 +1022,7 @@ struct Wasm_test : public beast::unit_test::suite testStartFunctionLoop(); testBadAlloc(); + testBadAlign(); // perfTest(); } diff --git a/src/test/app/wasm_fixtures/bad_align.c b/src/test/app/wasm_fixtures/bad_align.c new file mode 100644 index 0000000000..ff29456c2a --- /dev/null +++ b/src/test/app/wasm_fixtures/bad_align.c @@ -0,0 +1,23 @@ +#include + +int32_t +float_from_uint(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +int32_t +get_tx_nested_field(uint8_t const*, int32_t, uint8_t*, int32_t); + +uint8_t e_data[32 * 1024]; + +int32_t +test() +{ + e_data[1] = 0xFF; + e_data[2] = 0xFF; + e_data[3] = 0xFF; + e_data[4] = 0xFF; + e_data[5] = 0xFF; + e_data[6] = 0xFF; + e_data[7] = 0xFF; + e_data[8] = 0xFF; + float_from_uint(&e_data[1], 8, &e_data[35], 12, 0); + return *((int32_t*)(&e_data[36])); +} diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index 3f339e7a0d..44d4403382 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -1378,3 +1378,20 @@ extern std::string const badAllocHex = "3732302900490f7461726765745f6665617475726573042b0f6d757461626c652d676c6f62" "616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c7469" "76616c7565"; + +extern std::string const badAlignHex = + "0061736d0100000001110360057f7f7f7f7f017f6000006000017f02170103656e760f666c" + "6f61745f66726f6d5f75696e7400000303020102050301000206400a7f004180080b7f0041" + "80080b7f00418088020b7f00418088020b7f00418088060b7f004180080b7f00418088060b" + "7f00418080080b7f0041000b7f0041010b07b1010d066d656d6f72790200115f5f7761736d" + "5f63616c6c5f63746f727300010474657374000206655f6461746103000c5f5f64736f5f68" + "616e646c6503010a5f5f646174615f656e6403020b5f5f737461636b5f6c6f7703030c5f5f" + "737461636b5f6869676803040d5f5f676c6f62616c5f6261736503050b5f5f686561705f62" + "61736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f6261736503080c5f5f" + "7461626c655f6261736503090a240202000b1f00418108427f370000418108410841a30841" + "0c410010001a41a4082802000b007f0970726f647563657273010c70726f6365737365642d" + "62790105636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769" + "746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a6563742061623462356132646235" + "3832393538616631656533303861373930636664623432626432343732302900490f746172" + "6765745f6665617475726573042b0f6d757461626c652d676c6f62616c732b087369676e2d" + "6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c7565"; diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h index bb7a80f791..ff72711000 100644 --- a/src/test/app/wasm_fixtures/fixtures.h +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -84,3 +84,4 @@ extern std::string const infiniteLoopWasmHex; extern std::string const startLoopHex; extern std::string const badAllocHex; +extern std::string const badAlignHex; diff --git a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp b/src/xrpld/app/wasm/detail/HostFuncImpl.cpp index 3fda13424a..b57dae7f7c 100644 --- a/src/xrpld/app/wasm/detail/HostFuncImpl.cpp +++ b/src/xrpld/app/wasm/detail/HostFuncImpl.cpp @@ -196,8 +196,16 @@ locateField(STObject const& obj, Slice const& locator) if (locator.empty() || (locator.size() & 3)) // must be multiple of 4 return Unexpected(HostFunctionError::LOCATOR_MALFORMED); - int32_t const* locPtr = reinterpret_cast(locator.data()); - int32_t const locSize = locator.size() / 4; + int32_t locBuf[maxWasmParamLength / sizeof(int32_t)]; + int32_t const* locPtr = &locBuf[0]; + int32_t const locSize = locator.size() / sizeof(int32_t); + + uintptr_t p = reinterpret_cast(locator.data()); + if (p & (alignof(int32_t) - 1)) // unaligned + memcpy(&locBuf[0], locator.data(), locator.size()); + else + locPtr = reinterpret_cast(locator.data()); + STBase const* field = nullptr; auto const& knownSFields = SField::getKnownCodeToField(); diff --git a/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp b/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp index b5da69f71d..ee284fa6e9 100644 --- a/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp +++ b/src/xrpld/app/wasm/detail/HostFuncWrapper.cpp @@ -71,7 +71,14 @@ getDataUInt64(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) if (r->size() != sizeof(uint64_t)) return Unexpected(HostFunctionError::INVALID_PARAMS); - return *reinterpret_cast(r->data()); + uint64_t x; + uintptr_t p = reinterpret_cast(r->data()); + if (p & (alignof(uint64_t) - 1)) // unaligned + memcpy(&x, r->data(), sizeof(uint64_t)); + else + x = *reinterpret_cast(r->data()); + + return x; } template