Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

bug(orchestrator): Cleanup.Add has TOCTOU race — cleanup functions silently dropped after Run()

Abierto
#3,557 0 comentarios 0 reacciones 0 asignados Ver en GitHub

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

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

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de e2b-dev/runtime

Todos los issues de e2b-dev/runtime

Issues similares

Más issues de Go

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.