Skip to content

fix(agent): prevent race condition in RunStore fingerprint key creation (#2164) - #2169

Open
yunusarda-sudo wants to merge 2 commits into
OpenAdaptAI:mainfrom
yunusarda-sudo:fix/runstore-fingerprint-key-concurrency-race
Open

yunusarda-sudo wants to merge 2 commits into
OpenAdaptAI:mainfrom
yunusarda-sudo:fix/runstore-fingerprint-key-concurrency-race

Conversation

@yunusarda-sudo

Copy link
Copy Markdown
Contributor

Summary

In RunStore._fingerprint_key (openadapt_flow/agent/runs.py), concurrent threads creating fingerprint.key could race: one thread created the file via os.O_CREAT | os.O_EXCL but had not yet written its contents, while another thread receiving FileExistsError immediately read path.read_bytes(), resulting in an empty or partial key read. Subsequent HMAC fingerprints computed by different RunStore instances would then disagree.

Changes

  1. Atomic creation & durability:
    • Write new 32-byte secret key to a temporary file (.tmp-key-) within self.root and flush/os.fsync it before atomically linking (os.link) or replacing (os.replace) into fingerprint.key.
    • Ensure temporary files are unlinked in a finally block.
  2. Fail closed on corrupted or short keys:
    • Explicitly validate key length on read (len(existing) < 32), raising ValueError if a short or empty key is encountered.
    • Refuse symlinks for fingerprint.key with ValueError.
  3. Tests:
    • Added test_fingerprint_key_creation_races_under_concurrency_produce_same_key asserting 20 concurrent threads across 8 workers all yield the exact same 32-byte key.
    • Added tests verifying ValueError is raised on short keys, empty keys, and symlinks.
    • Included test cases in packages/flow/tests/test_agent_runs.py and packages/agent/tests/test_runs.py.

Closes #2164.

Signed-off-by: Yunus Ardaguney yunusarda-sudo@users.noreply.github.com

…on (OpenAdaptAI#2164)

In RunStore._fingerprint_key, concurrent threads could race when creating fingerprint.key: one thread created the file via O_EXCL but hadn't finished writing it, causing a second thread encountering FileExistsError to read an empty or truncated key. Subsequent HMAC fingerprints would then disagree.

Fix this by:
1. Writing the new key to a temporary file in root and fsyncing it before atomically linking (os.link) or replacing (os.replace) into place.
2. Rejecting any key shorter than 32 bytes on read with ValueError to fail closed against corrupt/empty keys.
3. Rejecting symlinks for fingerprint.key.
4. Adding tests asserting concurrent initialization safety and validation rejection in packages/flow/tests/test_agent_runs.py and packages/agent/tests/test_runs.py.

Closes OpenAdaptAI#2164.

Signed-off-by: Yunus Ardaguney <yunusarda-sudo@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

RunStore fingerprint key creation races under concurrency (empty HMAC key possible)

1 participant