bug(orchestrator): Cleanup.Add has TOCTOU race — cleanup functions silently dropped after Run()
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 72/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Tranquilo
- Stack tecnológico
- go
- Área
- infrastructure
Línea de trabajo
Revisa Cleanup.Add, Cleanup.AddPriority y Cleanup.run en packages/orchestrator/pkg/sandbox/cleanup.go, centrándote en el orden de lock y hasRun. Confirma que el cambio cierra la ventana TOCTOU y que las funciones de limpieza registradas concurrentemente con Run() ya no se descartan silenciosamente; después, ejecuta las pruebas existentes del paquete con el detector de carreras.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Cleanup.Add and Cleanup.AddPriority have a TOCTOU (time-of-check/time-of-use) race against Cleanup.run that silently discards cleanup functions without executing them, causing permanent resource leaks.
Root Cause
In packages/orchestrator/pkg/sandbox/cleanup.go:
run() sets hasRun before acquiring the lock (line 80 vs 82):
func (c *Cleanup) run(ctx context.Context) {
c.hasRun.Store(true) // ← written outside the lock
c.mu.Lock()
defer c.mu.Unlock()
// ... drains c.cleanup and c.priorityCleanup
}
Add() reads hasRun before acquiring the lock (line 40 vs 49):
func (c *Cleanup) Add(ctx context.Context, f func(ctx context.Context) error) {
if c.hasRun.Load() == true { // ← read outside the lock
// run f immediately
return
}
c.mu.Lock()
defer c.mu.Unlock()
c.cleanup = append(c.cleanup, f) // ← appended after run() may have drained
}
Race Window
Goroutine A (Add): hasRun.Load() == false → [not yet locked]
Goroutine B (run): hasRun.Store(true) → Lock() → drain all cleanup → Unlock()
Goroutine A (Add): Lock() → append(f) ← f is now in a drained slice, never executed
sync.Once in Run() prevents run() from being called a second time, so f is permanently lost — no log, no error, no signal.
Impact
The Cleanup type is the central resource-release mechanism for Firecracker sandbox lifecycle. The package has 28 call sites across sandbox startup (sandbox.go, resume.go, reboot.go, etc.) registering operations such as:
- Closing overlay filesystems
- Releasing network slots
- Removing Firecracker and UFFD socket files
- Stopping the Firecracker process
Any one of these silently dropped during a concurrent error-path teardown leaves a leaked resource on the host until the orchestrator restarts.
Fix
Move hasRun.Store(true) inside the lock in run(), and add a double-check in Add() / AddPriority() after acquiring mu:
func (c *Cleanup) Add(ctx context.Context, f func(ctx context.Context) error) {
// Optimistic fast path.
if c.hasRun.Load() {
err := f(context.WithoutCancel(ctx))
if err != nil {
logger.L().Error(ctx, "failed to run function after cleanup has run", zap.Error(err))
}
return
}
c.mu.Lock()
// Double-check: run() may have completed between the Load above and here.
if c.hasRun.Load() {
c.mu.Unlock()
err := f(context.WithoutCancel(ctx))
if err != nil {
logger.L().Error(ctx, "failed to run function after cleanup has run", zap.Error(err))
}
return
}
c.cleanup = append(c.cleanup, f)
c.mu.Unlock()
}
func (c *Cleanup) run(ctx context.Context) {
c.mu.Lock()
defer c.mu.Unlock()
c.hasRun.Store(true) // now set under the lock — closes the race window
// ... rest unchanged
}
The same double-check applies to AddPriority.
- Lenguaje dominante
- Go
- Estrellas
- 1.6k
- Forks
- 438
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de e2b-dev/runtime
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
-
sandbox cache: StartRemoving state transition not broadcast, all allocations see stale Running stateAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
Todos los issues de e2b-dev/runtime
Issues similares
-
area/proxy kind/bug priority/backlog triage/accepted
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
lexfrei/cloudflare-tunnel-gateway-controller#840 ·
Los mantenedores suelen responder en 1 día
-
area:chat bug sev:papercut
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Agent-Field/CodeAF#1592 ·
Los mantenedores suelen responder en 1 día
-
kind/bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 7 días
-
bug needs triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
bug P2 reliability
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
afreidah/s3-orchestrator#1564 ·
Los mantenedores suelen responder en 1 día