Skip to content

[Audit][High] Mixer loop cursor reset skips bounds re-check, risking OOB read and audio artifactsΒ #718

@MichaelFisher1997

Description

@MichaelFisher1997

πŸ” Module Scanned

(automated audit scan)

πŸ“ Summary

In the function of , when a looping voice reaches end-of-buffer, the cursor is reset to 0.0 and looping continues, but the bounds check that follows does not re-fetch the updated cursor value. Since was just modified to 0.0 before the comparison at line 265, the subsequent condition still uses the stale from before the reset. This means after looping, the first sample read can be at an incorrect offset, causing audio glitches or OOB memory access if the buffer was freed.

πŸ“ Location

  • File:
  • Function/Scope: β€” loop cursor reset path

πŸ”΄ Severity: High

  • Critical: Potential out-of-bounds memory read, possible crash or audio corruption
  • High: Audio artifacts (clicking/popping) when looped sounds wrap
  • Medium: The bug is masked by the subsequent re-calculation at line 275, but that second check can also trigger false positives when it shouldn't

πŸ’₯ Impact

When a looping voice reaches its end-of-buffer:

  1. is set to 0.0 at line 267
  2. The loop continues without breaking
  3. Line 265's bounds check uses stale (from before cursor reset)
  4. An incorrect sample offset may be used for the next iteration before re-calculation
  5. In pathological cases (e.g., very small buffers, or pitch > 1.0 causing rapid wrap), this could read past buffer end before the second check at line 276 catches it

πŸ”Ž Evidence

The problem is that after , control flows directly to line 275 without a or break. The code continues to line 283 where it reads using the updated cursor (now 0), but the second bounds check at line 276 uses the newly calculated (which would be 0 after loop). If buffer length is less than 2 bytes (unlikely but possible for malformed data), the check at line 276 would catch it, but the deeper issue is the flow: after in the loop, the code continues to use (line 283) which was calculated before the reset.

πŸ› οΈ Proposed Fix

After setting inside the loop, add a to skip the rest of the iteration and allow the next iteration to properly calculate from the reset cursor:

Additionally, consolidate the redundant bounds checks. The check at line 276-279 duplicates logic that could be handled more cleanly after the cursor reset.

βœ… Acceptance Criteria

  • All unit tests in pass
  • Looping sounds correctly restart from sample 0 without artifacts
  • No out-of-bounds memory access when mixing looped audio with pitch != 1.0
  • The fix has been verified with Zig 0.16.0 + SDL3 Dev Environment
    Compiler: 0.16.0
    assets/shaders/vulkan/shadow.vert
    assets/shaders/vulkan/lpv_propagate.comp
    assets/shaders/vulkan/ui.frag
    assets/shaders/vulkan/ui_tex.frag
    assets/shaders/vulkan/g_pass.frag
    assets/shaders/vulkan/debug_shadow.vert
    assets/shaders/vulkan/lpv_inject.comp
    assets/shaders/vulkan/ssao.vert
    assets/shaders/vulkan/sky.frag
    assets/shaders/vulkan/water.vert
    assets/shaders/vulkan/bloom_downsample.vert
    assets/shaders/vulkan/bloom_upsample.vert
    assets/shaders/vulkan/debug_shadow.vert
    assets/shaders/vulkan/fxaa.vert
    assets/shaders/vulkan/post_process.vert
    assets/shaders/vulkan/shadow.vert
    assets/shaders/vulkan/sky.vert
    assets/shaders/vulkan/ssao.vert
    assets/shaders/vulkan/taa.vert
    assets/shaders/vulkan/terrain.vert
    assets/shaders/vulkan/ui.vert
    assets/shaders/vulkan/ui_tex.vert
    assets/shaders/vulkan/water.vert
    assets/shaders/vulkan/bloom_downsample.frag
    assets/shaders/vulkan/bloom_upsample.frag
    assets/shaders/vulkan/debug_shadow.frag
    assets/shaders/vulkan/fxaa.frag
    assets/shaders/vulkan/g_pass.frag
    assets/shaders/vulkan/post_process.frag
    assets/shaders/vulkan/shadow.frag
    assets/shaders/vulkan/sky.frag
    assets/shaders/vulkan/ssao.frag
    assets/shaders/vulkan/ssao_blur.frag
    assets/shaders/vulkan/taa.frag
    assets/shaders/vulkan/terrain.frag
    assets/shaders/vulkan/terrain_debug.frag
    assets/shaders/vulkan/ui.frag
    assets/shaders/vulkan/ui_tex.frag
    assets/shaders/vulkan/water.frag
    assets/shaders/vulkan/culling.comp
    assets/shaders/vulkan/depth_pyramid.comp
    assets/shaders/vulkan/lpv_inject.comp
    assets/shaders/vulkan/lpv_propagate.comp
    assets/shaders/vulkan/mesh.comp
    assets/shaders/vulkan/debug_shadow.frag
    assets/shaders/vulkan/taa.frag
    assets/shaders/vulkan/mesh.comp
    assets/shaders/vulkan/water.frag
    assets/shaders/vulkan/taa.vert
    assets/shaders/vulkan/depth_pyramid.comp
    assets/shaders/vulkan/culling.comp
    assets/shaders/vulkan/ssao.frag
    assets/shaders/vulkan/ui.vert
    assets/shaders/vulkan/shadow.frag
    assets/shaders/vulkan/ssao_blur.frag
    assets/shaders/vulkan/terrain.vert
    assets/shaders/vulkan/ui_tex.vert
    assets/shaders/vulkan/terrain.frag
    assets/shaders/vulkan/sky.vert

πŸ“š References

Metadata

Metadata

Assignees

No one assigned

    Labels

    automated-auditIssues found by automated opencode audit scansbugSomething isn't workingdocumentationImprovements or additions to documentationhotfix

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions