Skip to content

[Audit][High] JobQueue.doReprioritize leaks jobs on re-add failure #488

@MichaelFisher1997

Description

@MichaelFisher1997

🔍 Module Scanned

src/engine/core/ (automated audit scan)

📝 Summary

In JobQueue.doReprioritize, when re-adding jobs to the priority queue fails (due to an allocation error), the job is silently dropped without calling its cleanup_fn. This causes a memory leak for generic jobs that own heap-allocated context.

📍 Location

  • File: src/engine/core/job_system.zig:226-229
  • Function/Scope: JobQueue.doReprioritize

🔴 Severity: High

  • High: Memory leaks, race conditions, incorrect rendering, broken features

💥 Impact

When the priority queue's underlying array grows during add() and the allocation fails, any generic job that was successfully extracted from the original queue and appended to the temporary list is permanently lost. The cleanup_fn is never called, leaking any heap-allocated context the job owns. This accumulates over time as the player moves and the queue is repeatedly reprioritized.

🔎 Evidence

// Re-add with updated priorities
for (temp.items) |job| {
    self.jobs.add(job) catch {
        log.log.warn("Job queue: failed to re-add job after priority update", .{});
        continue;  // <-- BUG: job dropped, cleanup never called
    };
}

Compare to the correct pattern in doReprioritize at line 217-221:

temp.append(self.allocator, updated_job) catch {
    log.log.warn("Job queue: dropped job during priority update (allocation failed)", .{});
    updated_job.cleanup();  // <-- CORRECT: cleanup is called
    continue;
};

The first catch block (line 219) correctly calls updated_job.cleanup() before dropping the job. The second catch block (line 226-229) does not, causing a leak.

🛠️ Proposed Fix

Add job.cleanup() before continue in the second catch block at line 226:

self.jobs.add(job) catch {
    log.log.warn("Job queue: failed to re-add job after priority update", .{});
    job.cleanup();  // Release generic job context before dropping
    continue;
};

✅ Acceptance Criteria

  • All unit tests in src/engine/core/job_system.zig pass
  • Generic jobs with cleanup_fn that fail to re-prioritize have their cleanup called
  • No memory leaks when std.mem.Allocator fails during jobs.add() in doReprioritize

📚 References

  • Related: Job.cleanup() at line 92-101 which handles cleanup for generic jobs
  • Related: doReprioritize at line 217-221 which correctly calls cleanup before dropping jobs

Metadata

Metadata

Assignees

No one assigned

    Labels

    automated-auditIssues found by automated opencode audit scansbugSomething isn't workingenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions