java-topology/defects/lean4/patch/lean4-0005-lake-jobreg-mutex.patch
russell@unturf.com ae6e04c5bd feat: add lean4-0004 through lean4-0007 patches
lean4-0004: collapse 3-phase lock in ir_interpreter lookup_symbol to single unique_lock
lean4-0005: replace IO.Ref JobQueue with Std.Mutex in Lake job registry
lean4-0006: register thread-local reset for g_opts in kernel/trace.cpp
lean4-0007: build unordered_set of override keys outside env-var loop (Windows)
2026-04-13 10:24:23 -04:00

53 lines
2.3 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000001263
# CWE-362: Race Condition -- registerJob IO.Ref.modify without mutex
#
# Defect: registeredJobs : IO.Ref (Array OpaqueJob). Multiple concurrent async
# tasks call .modify (·.push job) simultaneously. IO.Ref.modify is not atomic
# across tasks -- concurrent pushes lose registrations.
#
# Fix: IO.Mutex (Array OpaqueJob) with .atomically for all register + poll ops.
#
# Complexity gate: correctness -- no lost registrations under -j 8 parallel build
--- a/src/lake/Lake/Build/Context.lean
+++ b/src/lake/Lake/Build/Context.lean
@@ -6,6 +6,8 @@ module
prelude
+public import Std.Sync.Mutex
public import Lake.Config.Cache
public import Lake.Config.Context
public import Lake.Build.Job.Basic
@@ -40,10 +42,10 @@ public def BuildConfig.showProgress (cfg : BuildConfig) : Bool :=
(cfg.noBuild ∧ cfg.verbosity == .verbose) cfg.verbosity != .quiet
-/-- Mutable reference of registered build jobs. -/
+/-- Mutex-guarded queue of registered build jobs. -/
@[expose] -- for codegen
-public def JobQueue := IO.Ref (Array OpaqueJob)
+public def JobQueue := Std.Mutex (Array OpaqueJob)
-/-- Returns a new empty job queue. -/
+/-- Returns a new empty job queue (mutex-guarded). -/
@[inline] public def mkJobQueue : BaseIO JobQueue :=
- IO.mkRef #[]
+ Std.Mutex.new #[]
--- a/src/lake/Lake/Build/Job/Register.lean
+++ b/src/lake/Lake/Build/Job/Register.lean
@@ -38,7 +38,9 @@ public def Job.renew (self : Job α) : Job α :=
@[inline] public def registerJob
[Monad m] [MonadLiftT (ST IO.RealWorld) m] [MonadBuild m]
+ [MonadLiftT BaseIO m] [MonadFinally m]
(caption : String) (job : Job α) (optional := false)
: m (Job α) := do
let job : Job α := {job with caption, optional}
- (← getBuildContext).registeredJobs.modify (·.push job)
+ (← getBuildContext).registeredJobs.atomically (·.modify (·.push job))
return job.renew
--- a/src/lake/Lake/Build/Run.lean
+++ b/src/lake/Lake/Build/Run.lean
@@ -156,7 +156,7 @@ where
def poll (unfinished : Array OpaqueJob) : MonitorM (Array OpaqueJob × Array OpaqueJob) := do
- let newJobs ← (← read).jobs.modifyGet ((·, #[]))
+ let newJobs ← (← read).jobs.atomically (·.modifyGet ((·, #[])))
modify fun s => {s with totalJobs := s.totalJobs + newJobs.size}
let pollJobs := fun (running, unfinished) job => do