Skip to content

Do not unlink a reused atomic JSON temporary name #485

Closed

Description

@Anionix

Classification

  • Category: correctness / concurrency
  • Micro-category: temporary-name reuse during inode validation
  • Severity: P1

Source

Expected

Cleanup may unlink a temporary basename only while the directory entry still
identifies the inode opened by this writer.

Observed

If the temporary entry is removed and an unrelated file reuses its basename
before inode validation, validation raises but the caller retains the basename.
The finally block then unlinks the unrelated replacement.

Acceptance

  • Record the opened temporary inode identity.
  • Treat missing or mismatched validation entries as lost ownership.
  • Before failure cleanup, verify the current entry still matches the opened
    identity.
  • Preserve an unrelated same-name replacement in a deterministic regression.

Primary sources

Activity

  1. Anionix commented on Jul 24, 2026

    @Anionix
    OwnerAuthor

    Resolved at 84ddd0d11fd5a39d56d84b50ed24c56b9b9961ad by removing the
    non-portable guarantee instead of adding another check:

    • POSIX/Python provides separate stat and unlink operations, so an inode
      comparison cannot make name-based deletion atomic.
    • Failure cleanup no longer calls unlink at all.
    • The temp is narrowed to 0600 before payload bytes are written.
    • Its intended umask-derived or existing-destination mode is restored only
      after the complete payload is flushed, immediately before replacement.
    • Successful replacement consumes the name; failed publication conservatively
      retains the temp.
    • A regression patches os.unlink to fail if any name-based failure cleanup is
      attempted.

    This closes both synchronized-thread and injected-wrapper reproductions without
    claiming a kernel primitive that does not exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions