DevSecOps

Temp File Race Conditions in Build Systems: The TOCTOU Problem

Build systems create and process temporary files constantly. Race conditions in temp file handling can be exploited to inject malicious content into builds.

James
Security Analyst
5 min read

Build systems are prolific creators of temporary files. Compiler intermediates, downloaded dependencies, extracted archives, generated code, test fixtures -- all written to temporary directories during the build process. When multiple builds run on the same machine, or when an attacker can predict or influence temp file paths, race conditions in temp file handling become exploitable.

The fundamental vulnerability is Time-of-Check-to-Time-of-Use (TOCTOU): the state of a file changes between when the build system checks it and when it uses it.

The Race Condition Pattern

A typical vulnerable pattern in a build script:

  1. Generate a temporary file path: /tmp/build-artifact-12345
  2. Check if the file exists (it does not)
  3. Write build output to the file
  4. Read the file back for the next build step

Between steps 2 and 3, or between steps 3 and 4, an attacker with access to the same filesystem can:

  • Create a symlink at the expected path pointing to a file they want to overwrite
  • Replace the file contents with malicious code before the build reads it back
  • Read the file contents if they contain sensitive information (credentials, signing keys)

Build System Exposure

Shared CI runners. When multiple CI jobs run on the same machine (common with self-hosted runners), they share the /tmp directory. One job can observe and interact with another job's temp files.

Predictable temp paths. Build tools that use predictable temp file names (based on PID, timestamp, or sequential numbers) are easier to attack. The attacker can predict the path and create a symlink before the build tool creates the file.

World-writable directories. /tmp on most Unix systems is world-writable with the sticky bit. Any user can create files, but the sticky bit only prevents deletion of other users' files -- not reading or symlinking.

Docker shared volumes. When Docker containers share a temp directory volume, container isolation does not protect temp files in the shared volume.

Real-World Examples

CVE-2019-12900 (bzip2). While primarily a decompression bug, the vulnerability was often triggered during build-time decompression of source archives in temp directories.

CVE-2020-8945 (gpgme Go library). A race condition in how the library handled temp files during PGP operations. In a build system context, this could be exploited to intercept or modify signed data during build verification.

Numerous build tool advisories for tools that use predictable temp paths during compilation, linking, or artifact generation. The pattern is consistent across languages and build systems.

Exploitation Scenarios

Build artifact injection. An attacker replaces a compiler output file in /tmp with a backdoored version before the linker reads it. The final binary contains the attacker's code.

Dependency substitution. During a build, a downloaded dependency is written to a temp file. The attacker replaces the temp file with a modified version before the build system verifies or installs it.

Credential interception. Build systems that write credentials (signing keys, deployment tokens) to temp files for use by build steps expose those credentials to other processes.

Test fixture manipulation. If test data is written to predictable temp paths, an attacker can modify it to make security tests pass when they should fail.

Defensive Practices

Use secure temp file creation. Every language has a secure temp file API that creates files with unique, unpredictable names and opens them atomically:

  • Python: tempfile.mkstemp() or tempfile.TemporaryFile()
  • Go: os.CreateTemp()
  • Java: Files.createTempFile()
  • Node.js: tmp package or fs.mkdtemp()
  • C: mkstemp() (never mktemp() or tmpnam())

These functions create the file and return a file descriptor in a single atomic operation, eliminating the TOCTOU window.

Use private temp directories. Create a temp directory with restricted permissions (0700) for each build and use it exclusively. This prevents other users from accessing the temp files.

Isolate CI builds. Run each CI job in its own container or VM with its own filesystem. Shared runners should not share temp directories between jobs.

Avoid writing secrets to temp files. Pass credentials through environment variables, stdin pipes, or file descriptors rather than temp files. If you must use temp files for secrets, create them in a dedicated directory with restrictive permissions and delete them immediately after use.

Use tmpfs where possible. On Linux, mounting /tmp as tmpfs (RAM-backed) is faster and prevents temp files from persisting on disk, though it does not prevent TOCTOU races.

Verify before use. After writing a temp file and before using it in a subsequent step, verify the file's contents (checksum), permissions, and ownership. This adds a layer of detection for manipulation.

How Safeguard.sh Helps

Safeguard.sh monitors build tool dependencies for known race condition and temp file vulnerabilities. When a CVE related to insecure temp file handling is published for a build tool or library in your dependency tree, Safeguard.sh alerts you with the affected projects and remediation guidance. For organizations running shared CI infrastructure, this monitoring helps protect the build pipeline against an attack class that exploits the trust placed in temp file operations.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.