Skip to content

fix: serialise BuildInplaceOnly tarball extraction to prevent TOCTOU race#269

Open
angerman wants to merge 1 commit intowip/andrea/add-executable-extensionfrom
fix/unpack-toctou-race
Open

fix: serialise BuildInplaceOnly tarball extraction to prevent TOCTOU race#269
angerman wants to merge 1 commit intowip/andrea/add-executable-extensionfrom
fix/unpack-toctou-race

Conversation

@angerman
Copy link
Copy Markdown

Summary

  • Adds an unpackLock to serialise the doesDirectoryExist check and unpackPackageTarball call in the BuildInplaceOnly path of withTarballLocalDirectory
  • Prevents a TOCTOU race when two stages of the same package (e.g. build: and host: in cross-compilation) are scheduled concurrently
  • Uses the existing Lock/criticalSection pattern from JobControl, matching registerLock and cacheLock

Root Cause

In cross-compilation builds, cabal-install creates two independent graph nodes for each package: WithStage Build <unitid> and WithStage Host <unitid>. Both are scheduled concurrently by InstallPlan.execute. When both stages have BuildInplaceOnly build style, they call withTarballLocalDirectory which:

  1. Checks if distUnpackedSrcDirectory pkgid exists (keyed only by PackageId, not stage)
  2. Both threads see it as non-existent
  3. Both race to extract the tarball to the same directory
  4. The second extraction overwrites the first mid-flight, corrupting the result

This manifests as intermittent "No cabal file found" errors (observed on FreeBSD CI for os-string-2.0.8).

Fix

Wrap the check-and-extract block in criticalSection unpackLock so only one thread performs the extraction while the other waits and finds the directory already present.

Test plan

  • Verify the fix compiles (single file change, no new dependencies)
  • Run cross-compilation build that previously triggered the race
  • Verify normal (non-cross) builds are unaffected

…o prevent TOCTOU race

Use a lock to prevent a race when two stages of the same package
(e.g. build: and host: in cross-compilation) are scheduled concurrently:
both may see the directory as non-existent and race to unpack, corrupting
the extraction and causing "No cabal file found" errors.
@andreabedini andreabedini force-pushed the fix/unpack-toctou-race branch from 254388a to 9f4127a Compare May 4, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants