Prevent unaligned memory accesses in libretro builds

These are undefined behaviour. All memory accesses need to be aligned.
For unaligned memory accesses, we have to use memcpy.
This commit is contained in:
刘皓 2025-07-02 20:46:54 -04:00
parent e3146c5e1d
commit 81cb43ef37
No known key found for this signature in database
GPG key ID: 7901753DB465B711
3 changed files with 90 additions and 53 deletions

View file

@ -24,6 +24,8 @@
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <tuple>
@ -153,8 +155,18 @@ namespace mkxp_sandbox {
// Gets a reference to the value stored at a given address in sandbox memory.
template <typename T> T &sandbox_ref(struct w2c_ruby &instance, wasm_ptr_t address) noexcept {
static_assert(std::is_arithmetic<T>::value, "can only get references to numeric values in the sandbox");
if (address % sizeof(T) != 0) {
#ifdef MKXPZ_RETRO_MEMORY64
std::fprintf(stderr, "unaligned memory access of size %u at address 0x%016llx in `mkxp_sandbox::sandbox_ref()`\n", (unsigned int)sizeof(T), (unsigned long long)address);
#else
std::fprintf(stderr, "unaligned memory access of size %u at address 0x%08llx in `mkxp_sandbox::sandbox_ref()`\n", (unsigned int)sizeof(T), (unsigned long long)address);
#endif // MKXPZ_RETRO_MEMORY64
std::fflush(stderr);
std::abort();
}
assert(address % sizeof(T) == 0);
#ifdef MKXPZ_BIG_ENDIAN
return *(T *)((uint8_t *)sandbox_ptr(instance, address) - sizeof(T));
return *(T *)sandbox_ptr(instance, address + sizeof(T));
#else
return *(T *)sandbox_ptr(instance, address);
#endif // MKXPZ_BIG_ENDIAN

View file

@ -178,64 +178,63 @@ bool sandbox_swizzle_info::get_exists() const {
template <> bool mkxp_sandbox::sandbox_serialize(bool value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
*(uint8_t *)data = value;
std::memcpy(data, &value, sizeof(uint8_t));
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(bool &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
value = *(uint8_t *)data;
std::memcpy(&value, data, sizeof(uint8_t));
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int8_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int8_t));
*(int8_t *)data = value;
std::memcpy(data, &value, sizeof(int8_t));
ADVANCE(sizeof(int8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int8_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int8_t));
value = *(int8_t *)data;
std::memcpy(&value, data, sizeof(int8_t));
ADVANCE(sizeof(int8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(uint8_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
*(uint8_t *)data = value;
std::memcpy(data, &value, sizeof(uint8_t));
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint8_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
value = *(uint8_t *)data;
std::memcpy(&value, data, sizeof(uint8_t));
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int16_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int16_t));
*(int16_t *)data = value;
std::memcpy(data, &value, sizeof(int16_t));
ADVANCE(sizeof(int16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int16_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int16_t));
std::memcpy(&value, data, sizeof(int16_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned short) == sizeof(int16_t), "unsigned short should be 16 bits");
value = (int16_t)_byteswap_ushort(*(unsigned short *)data);
value = (int16_t)_byteswap_ushort((unsigned short)value);
#else
value = (int16_t)__builtin_bswap16(*(uint16_t *)data);
value = (int16_t)__builtin_bswap16((uint16_t)value);
#endif // _MSC_VER
} else {
value = *(int16_t *)data;
}
ADVANCE(sizeof(int16_t));
return true;
@ -243,22 +242,21 @@ template <> bool mkxp_sandbox::sandbox_deserialize(int16_t &value, const void *&
template <> bool mkxp_sandbox::sandbox_serialize(uint16_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint16_t));
*(uint16_t *)data = value;
std::memcpy(data, &value, sizeof(uint16_t));
ADVANCE(sizeof(uint16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint16_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint16_t));
std::memcpy(&value, data, sizeof(uint16_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned short) == sizeof(uint16_t), "unsigned short should be 16 bits");
value = (uint16_t)_byteswap_ushort(*(unsigned short *)data);
value = (uint16_t)_byteswap_ushort((unsigned short)value);
#else
value = __builtin_bswap16(*(uint16_t *)data);
value = __builtin_bswap16(value);
#endif // _MSC_VER
} else {
value = *(uint16_t *)data;
}
ADVANCE(sizeof(uint16_t));
return true;
@ -266,22 +264,21 @@ template <> bool mkxp_sandbox::sandbox_deserialize(uint16_t &value, const void *
template <> bool mkxp_sandbox::sandbox_serialize(int32_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int32_t));
*(int32_t *)data = value;
std::memcpy(data, &value, sizeof(int32_t));
ADVANCE(sizeof(int32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int32_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int32_t));
std::memcpy(&value, data, sizeof(int32_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(int32_t), "unsigned long should be 32 bits");
value = (int32_t)_byteswap_ulong(*(unsigned long *)data);
value = (int32_t)_byteswap_ulong((unsigned long)value);
#else
value = (int32_t)__builtin_bswap32(*(uint32_t *)data);
value = (int32_t)__builtin_bswap32((uint32_t)value);
#endif // _MSC_VER
} else {
value = *(int32_t *)data;
}
ADVANCE(sizeof(int32_t));
return true;
@ -289,22 +286,21 @@ template <> bool mkxp_sandbox::sandbox_deserialize(int32_t &value, const void *&
template <> bool mkxp_sandbox::sandbox_serialize(uint32_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint32_t));
*(uint32_t *)data = value;
std::memcpy(data, &value, sizeof(uint32_t));
ADVANCE(sizeof(uint32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint32_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint32_t));
std::memcpy(&value, data, sizeof(uint32_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(uint32_t), "unsigned long should be 32 bits");
value = (uint32_t)_byteswap_ulong(*(unsigned long *)data);
value = (uint32_t)_byteswap_ulong((unsigned long)value);
#else
value = __builtin_bswap32(*(uint32_t *)data);
value = __builtin_bswap32(value);
#endif // _MSC_VER
} else {
value = *(uint32_t *)data;
}
ADVANCE(sizeof(uint32_t));
return true;
@ -312,21 +308,20 @@ template <> bool mkxp_sandbox::sandbox_deserialize(uint32_t &value, const void *
template <> bool mkxp_sandbox::sandbox_serialize(int64_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int64_t));
*(int64_t *)data = value;
std::memcpy(data, &value, sizeof(int64_t));
ADVANCE(sizeof(int64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int64_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int64_t));
std::memcpy(&value, data, sizeof(int64_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
value = (int64_t)_byteswap_uint64(*(unsigned __int64 *)data);
value = (int64_t)_byteswap_uint64((unsigned __int64)value);
#else
value = (int64_t)__builtin_bswap64(*(uint64_t *)data);
value = (int64_t)__builtin_bswap64((uint64_t)value);
#endif // _MSC_VER
} else {
value = *(int64_t *)data;
}
ADVANCE(sizeof(int64_t));
return true;
@ -334,21 +329,20 @@ template <> bool mkxp_sandbox::sandbox_deserialize(int64_t &value, const void *&
template <> bool mkxp_sandbox::sandbox_serialize(uint64_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint64_t));
*(uint64_t *)data = value;
std::memcpy(data, &value, sizeof(uint64_t));
ADVANCE(sizeof(uint64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint64_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint64_t));
std::memcpy(&value, data, sizeof(uint64_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
value = (uint64_t)_byteswap_uint64(*(unsigned __int64 *)data);
value = (uint64_t)_byteswap_uint64((unsigned __int64)value);
#else
value = __builtin_bswap64(*(uint64_t *)data);
value = __builtin_bswap64(value);
#endif // _MSC_VER
} else {
value = *(uint64_t *)data;
}
ADVANCE(sizeof(uint64_t));
return true;
@ -356,7 +350,7 @@ template <> bool mkxp_sandbox::sandbox_deserialize(uint64_t &value, const void *
template <> bool mkxp_sandbox::sandbox_serialize(float value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(float));
*(float *)data = value;
std::memcpy(data, &value, sizeof(float));
ADVANCE(sizeof(float));
return true;
}
@ -364,15 +358,17 @@ template <> bool mkxp_sandbox::sandbox_serialize(float value, void *&data, wasm_
template <> bool mkxp_sandbox::sandbox_deserialize(float &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(float));
if (deser_swap_bytes) {
uint32_t tmp;
std::memcpy(&tmp, data, sizeof(float));
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(float), "unsigned long should be 32 bits");
uint32_t tmp = (uint32_t)_byteswap_ulong(*(unsigned long *)data);
tmp = (uint32_t)_byteswap_ulong((unsigned long)tmp);
#else
uint32_t tmp = __builtin_bswap32(*(uint32_t *)data);
tmp = __builtin_bswap32(tmp);
#endif // _MSC_VER
std::memcpy(&value, &tmp, 4);
std::memcpy(&value, &tmp, sizeof(float));
} else {
value = *(float *)data;
std::memcpy(&value, data, sizeof(float));
}
ADVANCE(sizeof(float));
return true;
@ -380,7 +376,7 @@ template <> bool mkxp_sandbox::sandbox_deserialize(float &value, const void *&da
template <> bool mkxp_sandbox::sandbox_serialize(double value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(double));
*(double *)data = value;
std::memcpy(data, &value, sizeof(double));
ADVANCE(sizeof(double));
return true;
}
@ -388,14 +384,16 @@ template <> bool mkxp_sandbox::sandbox_serialize(double value, void *&data, wasm
template <> bool mkxp_sandbox::sandbox_deserialize(double &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(double));
if (deser_swap_bytes) {
uint64_t tmp;
std::memcpy(&tmp, data, sizeof(double));
#ifdef _MSC_VER
uint64_t tmp = (uint64_t)_byteswap_uint64(*(unsigned __int64 *)data);
tmp = (uint64_t)_byteswap_uint64((unsigned __int64)tmp);
#else
uint64_t tmp = __builtin_bswap64(*(uint64_t *)data);
tmp = __builtin_bswap64(tmp);
#endif // _MSC_VER
std::memcpy(&value, &tmp, 8);
std::memcpy(&value, &tmp, sizeof(double));
} else {
value = *(double *)data;
std::memcpy(&value, data, sizeof(double));
}
ADVANCE(sizeof(double));
return true;

View file

@ -479,7 +479,7 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_filestat_get(wasi_t *wasi, u
return WASI_EIO;
}
wasi->ref<uint64_t>(result) = fd; // dev
wasi->ref<uint64_t>(result + 8) = 0; // ino // TODO: generate a pseudorandom inode number
wasi->ref<uint64_t>(result + 8) = 0; // ino
wasi->ref<uint8_t>(result + 16) = WASI_IFDIR; // filetype
wasi->ref<uint32_t>(result + 24) = 1; // nlink
wasi->ref<uint64_t>(result + 32) = stat.filesize; // size
@ -499,7 +499,7 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_filestat_get(wasi_t *wasi, u
return WASI_EIO;
}
wasi->ref<uint64_t>(result) = fd; // dev
wasi->ref<uint64_t>(result + 8) = 0; // ino // TODO: generate a pseudorandom inode number
wasi->ref<uint64_t>(result + 8) = 0; // ino
wasi->ref<uint8_t>(result + 16) = WASI_IFREG; // filetype
wasi->ref<uint32_t>(result + 24) = 1; // nlink
wasi->ref<uint64_t>(result + 32) = stat.filesize; // size
@ -694,19 +694,46 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_fd_readdir(wasi_t *wasi, uint32
if (edata->buf - edata->original_buf + 8 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
wasi->ref<uint64_t>(edata->buf) = edata->cookie;
std::memcpy(
#ifdef MKXPZ_BIG_ENDIAN
wasi->ptr(edata->buf + 8),
#else
wasi->ptr(edata->buf),
#endif // MKXPZ_BIG_ENDIAN
&edata->cookie,
8
);
edata->buf += 8;
if (edata->buf - edata->original_buf + 8 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
wasi->ref<uint64_t>(edata->buf) = 0; // TODO: generate a pseudorandom inode number
std::memset(
#ifdef MKXPZ_BIG_ENDIAN
wasi->ptr(edata->buf + 8),
#else
wasi->ptr(edata->buf),
#endif // MKXPZ_BIG_ENDIAN
0,
8
);
edata->buf += 8;
if (edata->buf - edata->original_buf + 4 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
wasi->ref<uint32_t>(edata->buf) = std::strlen(filename);
{
const uint32_t value = std::strlen(filename);
std::memcpy(
#ifdef MKXPZ_BIG_ENDIAN
wasi->ptr(edata->buf + 8),
#else
wasi->ptr(edata->buf),
#endif // MKXPZ_BIG_ENDIAN
&value,
4
);
}
edata->buf += 4;
if (edata->buf - edata->original_buf + 4 > edata->buf_len) {
@ -948,7 +975,7 @@ extern "C" uint32_t w2c_wasi__snapshot__preview1_path_filestat_get(wasi_t *wasi,
}
wasi->ref<uint64_t>(result) = fd; // dev
wasi->ref<uint64_t>(result + 8) = 0; // ino // TODO: generate a pseudorandom inode number
wasi->ref<uint64_t>(result + 8) = 0; // ino
wasi->ref<uint8_t>(result + 16) = stat.filetype == PHYSFS_FILETYPE_DIRECTORY ? WASI_IFDIR : WASI_IFREG; // filetype
wasi->ref<uint32_t>(result + 24) = 1; // nlink
wasi->ref<uint64_t>(result + 32) = stat.filetype; // size