{"record":{"id":"0bd8de27a84ccc9f","repo":"wasmerio/wasmer","slug":"failed-to-convert-size-to-offset","errorCode":null,"errorMessage":"Failed to convert size to offset","messagePattern":"Failed to convert size to offset","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/wasix/src/syscalls/wasix/dlopen.rs","lineNumber":98,"sourceCode":"    let err_buf_len = err_buf_len as usize;\n\n    // The message is always written with a trailing NUL, so a zero-length\n    // buffer leaves no room for anything.\n    if err_buf_len == 0 {\n        return Ok(());\n    }\n\n    // Reserve one byte for the trailing NUL.\n    let max_err_len = err_buf_len - 1;\n    let mut err_len = err.len();\n\n    if err_len > max_err_len {\n        err_len = max_err_len;\n        err = &err[..err_len];\n    }\n\n    let Ok(err_len_offset) = M::Offset::try_from(err_len + 1) else {\n        panic!(\"Failed to convert size to offset\")\n    };\n    let mut err_buf = err_buf.slice(memory, err_len_offset)?.access()?;\n    let dst = err_buf.as_mut();\n    dst[..err_len].copy_from_slice(err.as_bytes());\n    dst[err_len] = 0;\n\n    Ok(())\n}\n\n#[cfg(all(test, not(target_arch = \"wasm32\")))]\nmod tests {\n    use super::write_dl_error;\n    use wasmer::{Memory, Memory32, MemoryType, Store, WasmPtr};\n\n    #[test]\n    fn write_dl_error_zero_len_buffer_writes_nothing() {\n        let mut store = Store::default();\n        let memory = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap();","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/wasmerio/wasmer/blob/8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5/lib/wasix/src/syscalls/wasix/dlopen.rs#L80-L116","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before (guest C)\ndlopen(path, flags, err_buf, (size_t)-1, ...);\n// after\nchar err_buf[256]; dlopen(path, flags, err_buf, sizeof(err_buf), ...);","handlingStrategy":"validation","validationCode":"// Guest side, before calling dlopen\nif (err_buf_len == 0 || err_buf_len > (1ULL << 31)) {\n    err_buf_len = sizeof(err_buf); // clamp to the real buffer size\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["wasix","panic","dlopen","memory-offset","guest-memory"],"backgroundTag":"memory-offset-conversion-failed","analyzedSha":"8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5","analyzedAt":"2026-09-01T23:06:31.009Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}