Skip to content

NVM locking: improve locking across the crypto layer#438

Open
rizlik wants to merge 4 commits into
wolfSSL:mainfrom
rizlik:fix-nvm-races
Open

NVM locking: improve locking across the crypto layer#438
rizlik wants to merge 4 commits into
wolfSSL:mainfrom
rizlik:fix-nvm-races

Conversation

@rizlik

@rizlik rizlik commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

This address two classes of gap with current state of N locking:

  1. crypto layer is now proper locking operation that may involve the global cache or the NVM
  2. To avoid TOCTOU some operations must be consolidate under a single lock, for example: GetUniqueIq + KeyImporting, or UsageFlagEnforcing + KeyExporting

Part of this new enforcement is needed only for global shared cache, but I'm not sure if it's worth to have separate locking codepaths for local vs global keys.

I'm not sure this is the best way to test this kind of problem neither.

rizlik added 2 commits July 9, 2026 16:51
The crypto layer uses the keystore cache/nvm without locking.

While the keystore cache is shared only for global keys, the NVM is
shared across all clients for any keyId, so operations that might touch
the NVM layer must be locked.

Also, check + usage type of operations (check enforcement flags, then use
the key) must held the lock over the all operation, otherwise a key can
change flags between check and usage. This can happen only with global
keys.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens thread-safety and TOCTOU resistance around global/shared NVM-backed key caching by making crypto-layer operations (key usage enforcement, key export/use, and key-id allocation + import) occur under a single NVM lock scope where necessary.

Changes:

  • Adds an atomic “read key + enforce usage policy” primitive and updates usage enforcement to avoid policy/key-generation races.
  • Introduces *Export*Enforce wrappers for multiple key types and applies them throughout server crypto handlers; also consolidates key-id allocation + cache import under one lock hold.
  • Adds POSIX-only concurrent stress tests (including TSAN support) to detect key-cache read mixups and unique-id collisions under contention.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
wolfhsm/wh_settings.h Adds WOLFHSM_CFG_SERVER_KDF_MAX_KEY_SIZE to bound cached KDF input copies.
wolfhsm/wh_server_keystore.h Declares wh_Server_KeystoreReadKeyEnforce and documents TOCTOU caveats for standalone policy checks.
wolfhsm/wh_server_crypto.h Adds *CacheExport*Enforce APIs to enforce usage policy against a locked snapshot during export.
src/wh_server_keystore.c Implements wh_Server_KeystoreReadKeyEnforce; updates usage-enforcement helper to lock around freshen+check.
src/wh_server_crypto.c Applies atomic export/enforce patterns across handlers; adds locked eviction helper; adds KDF input copying + bounds + zeroization; locks keygen id-allocation+import.
src/wh_server_cert.c Documents non-recursive lock behavior in cert path; avoids re-locking and fixes an error-path cleanup.
test/wh_test_crypto.c Updates ML-DSA DMA verify test to import key with required verify usage flag.
test-refactor/posix/wh_test_posix_main.c Registers the new concurrent stress tests in the POSIX test runner.
test-refactor/posix/wh_test_keyread_race.h Declares a self-contained concurrent cached-key read race test.
test-refactor/posix/wh_test_keyread_race.c Adds concurrent AES-ECB read/compare stress test to detect wrong-key cache reads under contention.
test-refactor/posix/wh_test_keygen_unique_id.h Declares a self-contained concurrent unique-id allocation test.
test-refactor/posix/wh_test_keygen_unique_id.c Adds concurrent cache-keygen test to detect duplicate id allocation under contention.
test-refactor/posix/Makefile Adds TSAN build/run wiring and enables TSAN-specific transport shims via defines.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c Outdated
Comment thread src/wh_server_crypto.c
Comment thread wolfhsm/wh_settings.h Outdated

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #438

Scan targets checked: wolfhsm-core-bugs, wolfhsm-crypto-bugs, wolfhsm-src

No new issues found in the changed files. ✅

@rizlik rizlik assigned bigbrett and AlexLanzano and unassigned rizlik Jul 14, 2026

@bigbrett bigbrett left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass. Overall looks good and necessary to clean up some thread safety violations.

Comment thread src/wh_server_cert.c
Comment on lines +484 to +490
/* The whole id allocation + cache import chain below is already
* atomic: every caller of this function reaches it through the
* cert request dispatch, which holds WH_SERVER_NVM_LOCK across
* the entire verify call. The NVM lock is non-recursive, so we
* must NOT re-acquire it here -- use the unlocked GetUniqueId +
* GetCacheSlotChecked building blocks under the caller's lock.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recommend removing overly verbose AI comment, doesn't really add anything to reader

Comment thread src/wh_server_keystore.c
}
else if (out != NULL) {
/* Don't hand back key material that failed the policy check */
memset(out, 0, *outSz);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should start using the new wh_Utils_ForceZero here

Comment thread src/wh_server_keystore.c
/* Don't hand back key material that failed the policy check */
memset(out, 0, *outSz);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
} /* WH_SERVER_NVM_LOCK() */

Comment thread src/wh_server_keystore.c
Comment on lines +3256 to +3259
/* May be deprecated soon: see wh_server_keystore.h. Enforcing here and using
* the key in a later lock scope leaves a TOCTOU window; prefer
* wh_Server_KeystoreReadKeyEnforce or the typed wh_Server_*CacheExport*Enforce
* wrappers. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just rip it out?

Comment thread src/wh_server_keystore.c
if (ret == WH_ERROR_OK) {
/* Enforce the usage policy with the obtained metadata */
ret = wh_Server_KeystoreEnforceKeyUsage(meta, requiredUsage);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
} /* WH_SERVER_NVM_LOCK() */

Comment thread src/wh_server_crypto.c
if (ret == WH_ERROR_OK) {
(void)wh_Server_KeystoreEvictKey(ctx, keyId);
(void)WH_SERVER_NVM_UNLOCK(ctx);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
} /* WH_SERVER_NVM_LOCK() */

Comment thread src/wh_server_crypto.c
ret = wh_Crypto_RsaDeserializeKeyDer(cacheMeta->len, cacheBuf, key);
}
(void)WH_SERVER_NVM_UNLOCK(ctx);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
} /* WH_SERVER_NVM_LOCK() */

I'll stop commenting these but plz do a pass through the file and make sure all the end if braces around the critical sections are commented as such

Comment thread src/wh_server_crypto.c
Comment on lines 453 to 462
}

/* Extract parameters from translated request */
int op_type = (int)(req.opType);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plz run git-clang-format main

Comment thread src/wh_server_crypto.c
return ret;
}

int wh_Server_CacheExportRsaKeyEnforce(whServerContext* ctx, whKeyId keyId,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this (and the other algo export helpers) need to be public? Should only happen in response to client request so should keep static if possible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason we wouldn't want to just add this to the existing multithreaded stress test as a test case?

@bigbrett bigbrett assigned rizlik and unassigned bigbrett Jul 14, 2026
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.

5 participants