ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.62k stars 2.46k forks source link

memory corruption issue can only reproduce on Wasm -Doptimize=ReleaseSafe #17529

Open silbinarywolf opened 10 months ago

silbinarywolf commented 10 months ago

Zig Version

0.12.0-dev.668+c07d6e4c1

Targets tested on

Opening

3d Raylib game locks up after 5-15 seconds when running WebAssembly build. Sometimes I notice printed text on the screen becomes garbled. (Top line of text becomes ????????????????????????????????????)

I assumed the issue was something to do with extern struct types mixing with Zig struct types because replacing player_start_position: rl.Vector3 = rl.Vector3.init(0, 0, 0) with my own vector type seemed to fix the issue, but when I tried putting together a minimal code example, I couldn't reproduce the issue.

This issue does not occur when doing a Debug WASM build.

Link to code

Because I couldn't create recreate the issue, I've opted for the next thing which is to just upload the whole project as-is here: https://github.com/silbinarywolf/3d-raylib-toy-project

Steps to Reproduce and Observed Behavior

To reproduce, follow the steps here to get the Emscripten SDK if you don't have it already: https://github.com/Not-Nik/raylib-zig#exporting-for-web

To build this, do the following:

Chrome / Firefox Logs

Game ends up spamming console logs with "index.wasm:0x2ece5 Uncaught RuntimeError: memory access out of bounds" Pointing at the line i32.store offset=28 below"

if
  global.get $global0
  i32.const 32
  i32.sub
  local.tee $var10
  global.set $global0
  local.get $var10
  i32.const 203572
  i32.load
  i32.store offset=28  // <- Error points at this line of wasm
  local.get $var0
  i32.eqz
  local.set $var4
end

Expected Behavior

I expect the WASM build to not lock up or randomly corrupt a text drawn on string. If I am doing something wrong here, I feel like at most the game should just crash/lock up, not stomp over something else.

Srekel commented 9 months ago

I'm also seeing a memory corruption issue in ReleaseSafe, that isn't happening in Debug, unsure if it's the same issue.

Here's my code:

    var splatmap_namebuf: [256]u8 = undefined;
    const splatmap_path = std.fmt.bufPrintZ(
        splatmap_namebuf[0..splatmap_namebuf.len],
        "content/patch/splatmap/lod{}/splatmap_x{}_y{}.png",
        .{
            patch.lookup.lod,
            patch.lookup.patch_x,
            patch.lookup.patch_z,
        },
    ) catch unreachable;

    const splatmap_asset_id = IdLocal.init(splatmap_path);
    const splatmap_data = ctx.asset_manager.loadAssetBlocking(splatmap_asset_id, .instant_blocking);

The called function:

    pub fn loadAssetBlocking(self: *AssetManager, id: IdLocal, urgency: Urgency) []u8 {
        std.log.info("01{s}\n", .{id.toString()});

The log shows a broken string. If I log the exact same thing before the call (splatmap_asset_id ) it looks fine.

IdLocal looks like this:


pub const IdLocal = struct {
    str: [191]u8 = .{0} ** 191,
    strlen: u8 = 0,
    hash: u64 = 0,

https://github.com/Srekel/tides-of-revival/blob/main/src/variant.zig#L6

silbinarywolf commented 6 months ago

I'm wondering if this issue I raised might be related to the first line item from this issue here: https://github.com/ziglang/zig/issues/10836#issuecomment-1951538464

ie. I had to put this in my main.zig for a project that uses SDL2 to avoid memory corruption issues, My guess is because you're not allocating with Emscriptens malloc, you end up stomping over things allocated with C libraries.

// Force allocator to use c allocator for emscripten
// NOTE: This logic assumes you're using the workaround used here: https://github.com/Not-Nik/raylib-zig/blob/183347adaf8fb6dddf25ece3c8a157c8bab212c5/build.zig#L355
pub const os = if ( builtin.os.tag != .wasi) std.os else struct {
    pub const heap = struct {
        pub const page_allocator = std.heap.c_allocator;
    };
};