wasmerio/wasmer · error
Failed to convert size to offset
Error message
Failed to convert size to offset
What it means
write_dl_error copies a dlopen error message plus a NUL terminator into guest memory. The computed length (err_len + 1, always positive and bounded by err_buf_len which is a u64 widened to usize) must be convertible to the memory's Offset type (u32 for memory32); if not, the code panics with "Failed to convert size to offset". With a 4GiB-1 guest buffer in memory32 this can occur, though offset conversion normally bounds it.
Source
Thrown at lib/wasix/src/syscalls/wasix/dlopen.rs:98
let err_buf_len = err_buf_len as usize;
// The message is always written with a trailing NUL, so a zero-length
// buffer leaves no room for anything.
if err_buf_len == 0 {
return Ok(());
}
// Reserve one byte for the trailing NUL.
let max_err_len = err_buf_len - 1;
let mut err_len = err.len();
if err_len > max_err_len {
err_len = max_err_len;
err = &err[..err_len];
}
let Ok(err_len_offset) = M::Offset::try_from(err_len + 1) else {
panic!("Failed to convert size to offset")
};
let mut err_buf = err_buf.slice(memory, err_len_offset)?.access()?;
let dst = err_buf.as_mut();
dst[..err_len].copy_from_slice(err.as_bytes());
dst[err_len] = 0;
Ok(())
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::write_dl_error;
use wasmer::{Memory, Memory32, MemoryType, Store, WasmPtr};
#[test]
fn write_dl_error_zero_len_buffer_writes_nothing() {
let mut store = Store::default();
let memory = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap();View on GitHub (pinned to 8c4b9ee9d3)
Solutions
- Pass a sane err_buf_len (the real byte size of your error buffer) to dlopen/dlsym.
- Validate err_buf_len in the guest before the call; keep it within the memory's addressable size.
- If you maintain the runtime, return Errno::Inval/Overflow instead of panicking on the try_from failure.
- Check whether a signed-to-unsigned conversion bug in the guest is producing the huge length.
Example fix
// before (guest C) dlopen(path, flags, err_buf, (size_t)-1, ...); // after char err_buf[256]; dlopen(path, flags, err_buf, sizeof(err_buf), ...);
Defensive patterns
Strategy: validation
Validate before calling
// Guest side, before calling dlopen
if (err_buf_len == 0 || err_buf_len > (1ULL << 31)) {
err_buf_len = sizeof(err_buf); // clamp to the real buffer size
} Prevention
- Always pass the actual size of your error buffer, never sentinel values like -1 or usize::MAX.
- Keep err_buf_len within the addressable range of the instance memory (4GiB-1 for memory32).
- Audit guest bindings for signed/unsigned length conversion bugs.
When it happens
Trigger: A dlopen/dlsym error occurs and the guest supplied an err_buf_len so large that err_len + 1 cannot be represented as M::Offset (only feasible with 64-bit lengths on a 32-bit memory, i.e. err_buf_len near u64::MAX).
Common situations: Guest passing a bogus/huge err_buf_len (e.g. usize::MAX or -1 as u32 wrap) when calling dlopen on a failing module load; memory32 environments where the slice cannot be created.
Related errors
- Internal error: failed to resolve exported function {}: {e:?
- Internal error: Tried to import TLS symbol from module {} th
- Internal error: missing import resolution '{0}'.{1}
- Internal error: bad in-progress symbol resolution: {e:?}
- Internal error: bad in-progress symbol resolution
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/0bd8de27a84ccc9f.
Report an issue: GitHub.