Add option to allocate large MLDSA objects from a global buffer #786

Merged
bensze01 merged 61 commits from mldsa-buffer-alloc into development 2026-07-08 13:42:11 -05:00
bensze01 commented 2026-04-30 02:50:19 -05:00 (Migrated from github.com)

Resolves https://github.com/Mbed-TLS/TF-PSA-Crypto/issues/645

PR checklist

  • changelog not required because: MLDSA is not user-visible yet
  • framework PR not required: crypto-only feature
  • TF-PSA-Crypto development PR provided #786
  • TF-PSA-Crypto 1.1 PR not required because: new feature
  • mbedtls development PR not required because: crypto-only feature
  • mbedtls 4.1 PR not required because: crypto-only feature
  • mbedtls 3.6 PR not required because: crypto-only feature
  • tests provided
Resolves https://github.com/Mbed-TLS/TF-PSA-Crypto/issues/645 ## PR checklist - [ ] **changelog** not required because: MLDSA is not user-visible yet - [ ] **framework PR** not required: crypto-only feature - [ ] **TF-PSA-Crypto development PR** provided #786 - [ ] **TF-PSA-Crypto 1.1 PR** not required because: new feature - [ ] **mbedtls development PR** not required because: crypto-only feature - [ ] **mbedtls 4.1 PR** not required because: crypto-only feature - [ ] **mbedtls 3.6 PR** not required because: crypto-only feature - **tests** provided
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-05-18 10:21:48 -05:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

Adds an experimental option TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC that makes mldsa-native allocate its large temporary objects from a single, global, statically-allocated bump buffer instead of the stack. When MBEDTLS_THREADING_C is enabled, access to the buffer is serialized by a new dedicated mutex.

Changes:

  • New pqcp_buffer_alloc.{h,c} defines the global buffer and MLD_CUSTOM_ALLOC/MLD_CUSTOM_FREE macros plumbed into mldsa-native via pqcp-config.h and a per-call empty context stuffed by macros in wrap_mldsa_native.h.
  • A new global mutex mbedtls_threading_pqcp_buffer_alloc_mutex is declared/initialised in platform/threading.{c,h} to guard the buffer.
  • New configuration option (config metadata + scripts + two new CI components exercising the option with and without pthreads).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
include/psa/crypto_config.h Adds docblock + define for TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC (currently enabled by default; module path also wrong).
scripts/data_files/config-options-current.txt Registers the new option name.
scripts/config.py Excludes the new option from blanket "full" configs.
drivers/pqcp/src/pqcp-config.h Wires MLD_CONFIG_CONTEXT_PARAMETER, MLD_CONFIG_CUSTOM_ALLOC_FREE, and the alloc/free macros into mldsa-native.
drivers/pqcp/src/pqcp_buffer_alloc.h Defines the global buffer, context struct and CUSTOM_ALLOC/CUSTOM_FREE bump-allocator macros.
drivers/pqcp/src/pqcp_buffer_alloc.c Provides the storage for the global buffer.
drivers/pqcp/src/wrap_mldsa_native.h Adds per-function macro wrappers that inject a zero-initialised context argument when context-stuffing is enabled.
drivers/pqcp/src/wrap_mldsa_native.c Disables the new wrapper macros locally so the real function definitions are not affected, and tightens the guard with MBEDTLS_PSA_CRYPTO_C.
platform/threading.c, platform/threading_internal.h Declares/initialises/destroys the new mutex when the option is enabled.
tests/scripts/components-configuration-crypto.sh Adds two CI components covering the new option with and without pthread.

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

## Pull request overview Adds an experimental option `TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC` that makes mldsa-native allocate its large temporary objects from a single, global, statically-allocated bump buffer instead of the stack. When `MBEDTLS_THREADING_C` is enabled, access to the buffer is serialized by a new dedicated mutex. **Changes:** - New `pqcp_buffer_alloc.{h,c}` defines the global buffer and `MLD_CUSTOM_ALLOC`/`MLD_CUSTOM_FREE` macros plumbed into mldsa-native via `pqcp-config.h` and a per-call empty context stuffed by macros in `wrap_mldsa_native.h`. - A new global mutex `mbedtls_threading_pqcp_buffer_alloc_mutex` is declared/initialised in `platform/threading.{c,h}` to guard the buffer. - New configuration option (config metadata + scripts + two new CI components exercising the option with and without pthreads). ### Reviewed changes Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments. <details> <summary>Show a summary per file</summary> | File | Description | | ---- | ----------- | | include/psa/crypto_config.h | Adds docblock + define for `TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC` (currently enabled by default; module path also wrong). | | scripts/data_files/config-options-current.txt | Registers the new option name. | | scripts/config.py | Excludes the new option from blanket "full" configs. | | drivers/pqcp/src/pqcp-config.h | Wires `MLD_CONFIG_CONTEXT_PARAMETER`, `MLD_CONFIG_CUSTOM_ALLOC_FREE`, and the alloc/free macros into mldsa-native. | | drivers/pqcp/src/pqcp_buffer_alloc.h | Defines the global buffer, context struct and `CUSTOM_ALLOC`/`CUSTOM_FREE` bump-allocator macros. | | drivers/pqcp/src/pqcp_buffer_alloc.c | Provides the storage for the global buffer. | | drivers/pqcp/src/wrap_mldsa_native.h | Adds per-function macro wrappers that inject a zero-initialised context argument when context-stuffing is enabled. | | drivers/pqcp/src/wrap_mldsa_native.c | Disables the new wrapper macros locally so the real function definitions are not affected, and tightens the guard with `MBEDTLS_PSA_CRYPTO_C`. | | platform/threading.c, platform/threading_internal.h | Declares/initialises/destroys the new mutex when the option is enabled. | | tests/scripts/components-configuration-crypto.sh | Adds two CI components covering the new option with and without pthread. | </details> --- 💡 <a href="/Mbed-TLS/TF-PSA-Crypto/new/development?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-18 10:21:48 -05:00

TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE is hard-coded to 123200. This appears to be specifically the maximum stack-replaced size for MLDSA-87 signing, but if TF_PSA_CRYPTO_PQCP_MLDSA_87_ENABLED is not set (only smaller parameter sets are enabled in the future), this size is larger than necessary; conversely it may need to grow if a larger parameter set is added. Consider deriving this from the enabled parameter set(s) (or at least introducing a named constant per parameter set summed/maxed appropriately), and/or letting the user override the size via configuration. At minimum, the inline comment should explain how the value was determined so a future maintainer can revalidate it.

`TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE` is hard-coded to `123200`. This appears to be specifically the maximum stack-replaced size for MLDSA-87 signing, but if `TF_PSA_CRYPTO_PQCP_MLDSA_87_ENABLED` is not set (only smaller parameter sets are enabled in the future), this size is larger than necessary; conversely it may need to grow if a larger parameter set is added. Consider deriving this from the enabled parameter set(s) (or at least introducing a named constant per parameter set summed/maxed appropriately), and/or letting the user override the size via configuration. At minimum, the inline comment should explain how the value was determined so a future maintainer can revalidate it.
@ -0,0 +48,4 @@
if ((N) > 0 && (N) <= TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE / sizeof(T)) { \
(v) = tf_psa_crypto_pqcp_alloc_push(MLD_ALIGN_UP(sizeof(T) * (N))); \
} \
} while (0)
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-18 10:21:48 -05:00

There is a potential deadlock when threading is enabled. If the very first CUSTOM_ALLOC call in a function successfully acquires the mutex (so alloc_offset is set to 0 by the lock), but the requested allocation does not fit because MLD_ALIGN_UP(sizeof(T) * (N)) exceeds TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE (the prior (N) <= BUFFER_SIZE/sizeof(T) check only bounds sizeof(T)*N, not its aligned-up size), the code takes the else branch and sets alloc_offset = PSA_ERROR_INSUFFICIENT_MEMORY. In that case no allocation was ever made, so no FREE call will fire to release the mutex (the FREE only unlocks when v == buffer start, and v is NULL here). All subsequent ALLOCs see alloc_offset != 0 and skip re-locking, so the mutex is held forever.

The accompanying comment "If we ended up here, that implies (alloc_offset != 0) thus we don't need to call mutex_unlock()" is incorrect in this edge case — alloc_offset could have been just set to 0 by a successful lock in this same invocation, with no prior successful allocation. The size check or the unlock logic needs to handle this case (e.g., unlock immediately when the first-and-only allocation fails, or tighten the upper bound to account for MLD_ALIGN_UP).

There is a potential deadlock when threading is enabled. If the very first CUSTOM_ALLOC call in a function successfully acquires the mutex (so `alloc_offset` is set to 0 by the lock), but the requested allocation does not fit because `MLD_ALIGN_UP(sizeof(T) * (N))` exceeds `TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE` (the prior `(N) <= BUFFER_SIZE/sizeof(T)` check only bounds `sizeof(T)*N`, not its aligned-up size), the code takes the `else` branch and sets `alloc_offset = PSA_ERROR_INSUFFICIENT_MEMORY`. In that case no allocation was ever made, so no FREE call will fire to release the mutex (the FREE only unlocks when `v == buffer start`, and `v` is NULL here). All subsequent ALLOCs see `alloc_offset != 0` and skip re-locking, so the mutex is held forever. The accompanying comment "If we ended up here, that implies (alloc_offset != 0) thus we don't need to call mutex_unlock()" is incorrect in this edge case — `alloc_offset` could have been just set to 0 by a successful lock in this same invocation, with no prior successful allocation. The size check or the unlock logic needs to handle this case (e.g., unlock immediately when the first-and-only allocation fails, or tighten the upper bound to account for `MLD_ALIGN_UP`).
@ -2065,6 +2065,50 @@
*/
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-18 10:21:46 -05:00

This option is uncommented by default, which means it is enabled in the default configuration. All other experimental TF_PSA_CRYPTO_PQCP_* options in this file (e.g., TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED line 2026, TF_PSA_CRYPTO_PQCP_OWN_SHAKE line 2066) follow the //#define ... pattern so users explicitly opt in. This line should be commented out (//#define TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC) consistent with the surrounding options and the "Uncomment to ..." instruction in the docblock.

This option is uncommented by default, which means it is enabled in the default configuration. All other experimental `TF_PSA_CRYPTO_PQCP_*` options in this file (e.g., `TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED` line 2026, `TF_PSA_CRYPTO_PQCP_OWN_SHAKE` line 2066) follow the `//#define ...` pattern so users explicitly opt in. This line should be commented out (`//#define TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC`) consistent with the surrounding options and the "Uncomment to ..." instruction in the docblock.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-18 10:21:47 -05:00

The Module path in this docblock refers to drivers/pqcp/src/mldsa_native_buffer_alloc.c, but the actual source file introduced in this PR is drivers/pqcp/src/pqcp_buffer_alloc.c. The path should be updated to match.

The Module path in this docblock refers to `drivers/pqcp/src/mldsa_native_buffer_alloc.c`, but the actual source file introduced in this PR is `drivers/pqcp/src/pqcp_buffer_alloc.c`. The path should be updated to match.
bensze01 (Migrated from github.com) reviewed 2026-05-18 10:31:57 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-18 10:31:57 -05:00

Adjusting the size of the buffer will be handled in a follow-up PR.

Adjusting the size of the buffer will be handled in a follow-up PR.
bensze01 (Migrated from github.com) reviewed 2026-05-18 10:35:06 -05:00
@ -0,0 +48,4 @@
if ((N) > 0 && (N) <= TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE / sizeof(T)) { \
(v) = tf_psa_crypto_pqcp_alloc_push(MLD_ALIGN_UP(sizeof(T) * (N))); \
} \
} while (0)
bensze01 (Migrated from github.com) commented 2026-05-18 10:35:05 -05:00

TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE is divisible by 32, the same granularity that MLD_ALIGN_UP() uses. This guarantees that the edge case above cannot be hit (if an allocation size s would fit in the buffer, an allocation of size ṀLD_ALIGN_UP(s) is also guaranteed to fit.

`TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE` is divisible by 32, the same granularity that `MLD_ALIGN_UP()` uses. This guarantees that the edge case above cannot be hit (if an allocation size `s` would fit in the buffer, an allocation of size `ṀLD_ALIGN_UP(s)` is also guaranteed to fit.
gilles-peskine-arm (Migrated from github.com) requested changes 2026-05-19 05:31:34 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

I've done a design review, not yet a full code review.

The design feels more complex and fragile than it needs to be. So please document the rationale. I especially want to understand why a context parameter is needed at all: can't we just make the offset a global variable, given that we end up having only one mldsa-native function active at time anyway?

I've done a design review, not yet a full code review. The design feels more complex and fragile than it needs to be. So please document the rationale. I especially want to understand why a context parameter is needed at all: can't we just make the offset a global variable, given that we end up having only one mldsa-native function active at time anyway?
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 04:58:45 -05:00

Where does this number come from?

I would like to have some validation somewhere. Presumably as a static assertion that compares our value with some value defined by mldsa-native.

Where does this number come from? I would like to have some validation somewhere. Presumably as a static assertion that compares our value with some value defined by mldsa-native.
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 05:08:45 -05:00

Can we write this whole macro as T *v = tf_psa_crypto_pqcp_custom_alloc(sizeof(T), N, &context) where tf_psa_crypto_pqcp_custom_alloc is a static inline function? It would be more readable and easier to debug.

Can we write this whole macro as `T *v = tf_psa_crypto_pqcp_custom_alloc(sizeof(T), N, &context)` where `tf_psa_crypto_pqcp_custom_alloc` is a `static inline` function? It would be more readable and easier to debug.
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 05:14:02 -05:00

I don't like having alloc_offset double as an error code. It's error-prone. Here we could use a local variable. Where else is alloc_offset checked for negative values?

I don't like having `alloc_offset` double as an error code. It's error-prone. Here we could use a local variable. Where else is `alloc_offset` checked for negative values?
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 05:23:42 -05:00

I'm trying to understand the concurrency behavior. If I understand correctly:

  • When a thread calls one of the mldsa-native entry points, it passes a context with alloc_offset=0. Thus the first allocation using that context object locks the buffer, which is then for exclusive use inside this thread.
  • When that first allocation is freed, we release the lock. Assuming that allocations are arranged in a FILO order (i.e. like a stack), the first allocation is the last one to be freed, so that means the thread no longer needs the global buffer, so it's correct to release the lock.

This works, but why not instead have our code call

mbedtls_mutex_lock(&mbedtls_threading_pqcp_buffer_alloc_mutex);
the_mldsa_native_function(...);
mbedtls_mutex_unlock(&mbedtls_threading_pqcp_buffer_alloc_mutex);

? That would be simpler and wouldn't rely on the FILO order of allocations. I believe that the FILO order is likely to remain true as mldsa-native evolves, but it isn't currently guaranteed by the mldsa-native documentation.

I'm trying to understand the concurrency behavior. If I understand correctly: * When a thread calls one of the mldsa-native entry points, it passes a context with `alloc_offset=0`. Thus the first allocation using that context object locks the buffer, which is then for exclusive use inside this thread. * When that first allocation is freed, we release the lock. Assuming that allocations are arranged in a FILO order (i.e. like a stack), the first allocation is the last one to be freed, so that means the thread no longer needs the global buffer, so it's correct to release the lock. This works, but why not instead have our code call ``` mbedtls_mutex_lock(&mbedtls_threading_pqcp_buffer_alloc_mutex); the_mldsa_native_function(...); mbedtls_mutex_unlock(&mbedtls_threading_pqcp_buffer_alloc_mutex); ``` ? That would be simpler and wouldn't rely on the FILO order of allocations. I believe that the FILO order is likely to remain true as mldsa-native evolves, but it isn't currently guaranteed by the mldsa-native documentation.
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 05:26:20 -05:00

The value of context.alloc_offset is how the different allocations made from one mldsa-native call don't overlap, so we rely on the context not being copied.

So why not make it a global variable?

The value of `context.alloc_offset` is how the different allocations made from one mldsa-native call don't overlap, so we rely on the context not being copied. So why not make it a global variable?
@ -0,0 +21,4 @@
#include "threading_internal.h"
#endif
#if defined(MBEDTLS_THREADING_C)
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 05:31:13 -05:00

I wouldn't mind just saying that TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC is incompatible with multithreading for now, since it's intended for constrained environments. But if we do claim that it's compatible, I would like it to be tested.

I wouldn't mind just saying that `TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC` is incompatible with multithreading for now, since it's intended for constrained environments. But if we do claim that it's compatible, I would like it to be tested.
@ -2066,2 +2066,4 @@
//#define TF_PSA_CRYPTO_PQCP_OWN_SHAKE
/**
* \def TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC
gilles-peskine-arm (Migrated from github.com) commented 2026-05-18 13:42:28 -05:00

Please add interesting entries to tests/suites/test_suite_config.crypto_combinations.data: at least

depends_on:MBEDTLS_PSA_CRYPTO_C:TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED:!TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC

or maybe with/without threads.

Please add interesting entries to `tests/suites/test_suite_config.crypto_combinations.data`: at least ``` depends_on:MBEDTLS_PSA_CRYPTO_C:TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED:!TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC ``` or maybe with/without threads.
bjwtaylor (Migrated from github.com) reviewed 2026-05-19 07:19:36 -05:00
bjwtaylor (Migrated from github.com) left a comment

Some initial comments, but still need to do a full review.

Some initial comments, but still need to do a full review.
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bjwtaylor (Migrated from github.com) commented 2026-05-19 07:18:48 -05:00

Is there a reason we are ignoring the return of mbedtls_mutex_unlock?

Is there a reason we are ignoring the return of mbedtls_mutex_unlock?
bjwtaylor (Migrated from github.com) commented 2026-05-19 05:50:44 -05:00

Does this add a new failure mode MLD_ERR_OUT_OF_MEMORY, which is then misclassified on line drivers/pqcp/src/psa_crypto_mldsa.c:181 due to a catch all
188 /* At the time of writing, invalid signature is the only possible 189 * error condition. But this will change when we update mldsa-native 190 * with support for heap allocation of intermediate values. 191 */ 192 return PSA_ERROR_INVALID_SIGNATURE;
The comment suggest this is now out of date and needs updating?

Does this add a new failure mode MLD_ERR_OUT_OF_MEMORY, which is then misclassified on line drivers/pqcp/src/psa_crypto_mldsa.c:181 due to a catch all `188 /* At the time of writing, invalid signature is the only possible 189 * error condition. But this will change when we update mldsa-native 190 * with support for heap allocation of intermediate values. 191 */ 192 return PSA_ERROR_INVALID_SIGNATURE;` The comment suggest this is now out of date and needs updating?
bjwtaylor commented 2026-05-19 07:24:02 -05:00 (Migrated from github.com)

Also one general comment, do we need to add testing to validate a simultaneous signing/verification test case that forces the simultaneous use of the PQCP allocation buffer? Or is this out of scope?

Also one general comment, do we need to add testing to validate a simultaneous signing/verification test case that forces the simultaneous use of the PQCP allocation buffer? Or is this out of scope?
bensze01 (Migrated from github.com) reviewed 2026-05-19 08:21:12 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-19 08:21:11 -05:00

For code size. GCC doesn't know that the value of the global variable alloc_offset is guaranteed to be the same after a call to any function - even one that allocates from the buffer - since the default implementation uses on-stack allocation.

For code size. GCC doesn't know that the value of the global variable `alloc_offset` is guaranteed to be the same after a call to any function - even one that allocates from the buffer - since the default implementation uses on-stack allocation.
bensze01 (Migrated from github.com) reviewed 2026-05-19 08:22:49 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-19 08:22:49 -05:00

Making alloc_offset a pass-by-value parameter makes this clear for the compiler.

Making `alloc_offset` a pass-by-value parameter makes this clear for the compiler.
bensze01 (Migrated from github.com) reviewed 2026-05-19 08:25:54 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-19 08:25:54 -05:00

Yeah, i guess now that I'm defining macros for each entry point, this approach makes more sense.

Yeah, i guess now that I'm defining macros for each entry point, this approach makes more sense.
bensze01 (Migrated from github.com) reviewed 2026-05-19 08:27:23 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-19 08:27:23 -05:00

We can't really do much with the return value as it is at the moment - no way to communicate it to mldsa-native, etc.

We can't really do much with the return value as it is at the moment - no way to communicate it to mldsa-native, etc.
bensze01 (Migrated from github.com) reviewed 2026-05-19 08:31:04 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-05-19 08:31:03 -05:00

I introduced the error-code usage to have a way to fail all further allocations, as that reduced code size considerably. Without that, the generated code has to check each allocation individually to see if it succeeded, then calls mbedtls_zeroize for allocations.

I introduced the error-code usage to have a way to fail all further allocations, as that reduced code size considerably. Without that, the generated code has to check each allocation individually to see if it succeeded, then calls `mbedtls_zeroize` for allocations.
gilles-peskine-arm (Migrated from github.com) reviewed 2026-05-19 15:05:17 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-05-19 15:05:17 -05:00

I tried making the offset global and I see a size reduction: (positive = size gain for my version)

drivers/pqcp/src/pqcp_buffer_alloc.o: 0 -> 136 (diff: -136)
drivers/pqcp/src/psa_crypto_mldsa.o: 464 -> 488 (diff: -24)
drivers/pqcp/src/wrap_mldsa_native.o: 9612 -> 9188 (diff: 424)

https://github.com/gilles-peskine-arm/TF-PSA-Crypto/tree/mldsa-buffer-alloc-global

What downsides do you see to my version (in terms of general design, it would need some cleanup and documentation)?

I tried making the offset global and I see a size reduction: (positive = size gain for my version) ``` drivers/pqcp/src/pqcp_buffer_alloc.o: 0 -> 136 (diff: -136) drivers/pqcp/src/psa_crypto_mldsa.o: 464 -> 488 (diff: -24) drivers/pqcp/src/wrap_mldsa_native.o: 9612 -> 9188 (diff: 424) ``` https://github.com/gilles-peskine-arm/TF-PSA-Crypto/tree/mldsa-buffer-alloc-global What downsides do you see to my version (in terms of general design, it would need some cleanup and documentation)?
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-06-03 04:16:57 -05:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

## Pull request overview Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
@ -0,0 +1,82 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-03 04:16:56 -05:00

These allocator state variables don't need external linkage; leaving them non-static exports additional global symbols from the library unnecessarily. Mark them static to keep the symbols private to this translation unit.

These allocator state variables don't need external linkage; leaving them non-`static` exports additional global symbols from the library unnecessarily. Mark them `static` to keep the symbols private to this translation unit.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-03 04:16:57 -05:00

Comment typo: this underflow condition is an error case, so the comment should say it "shouldn't happen".

Comment typo: this underflow condition is an error case, so the comment should say it "shouldn't happen".
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-03 04:16:56 -05:00

Typo in comment: "occured" should be "occurred".

Typo in comment: "occured" should be "occurred".
@ -0,0 +21,4 @@
#include "threading_internal.h"
#endif
#if defined(MBEDTLS_THREADING_C)
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-03 04:16:56 -05:00

The lock/unlock macros use mbedtls_threading_pqcp_buffer_alloc_mutex, but that mutex is only declared/defined when MBEDTLS_PSA_CRYPTO_C is enabled (see platform/threading_internal.h). With MBEDTLS_THREADING_C enabled but MBEDTLS_PSA_CRYPTO_C disabled, this header will still define the locking macros and fail to compile due to an undeclared mutex symbol. Make the lock conditional on MBEDTLS_PSA_CRYPTO_C as well (or provide an extern in the non-PSA case).

The lock/unlock macros use `mbedtls_threading_pqcp_buffer_alloc_mutex`, but that mutex is only declared/defined when `MBEDTLS_PSA_CRYPTO_C` is enabled (see platform/threading_internal.h). With `MBEDTLS_THREADING_C` enabled but `MBEDTLS_PSA_CRYPTO_C` disabled, this header will still define the locking macros and fail to compile due to an undeclared mutex symbol. Make the lock conditional on `MBEDTLS_PSA_CRYPTO_C` as well (or provide an extern in the non-PSA case).
@ -8,7 +8,7 @@
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-03 04:16:55 -05:00

TF_PSA_CRYPTO_PQCP_DISABLE_CONTEXT_STUFFING is defined/undefined around #include "wrap_mldsa_native.h", but the macro isn't referenced anywhere, so this block has no effect and the comment is misleading. Please remove it (or add the corresponding conditional logic in the header, but currently there's nothing to disable).

`TF_PSA_CRYPTO_PQCP_DISABLE_CONTEXT_STUFFING` is defined/undefined around `#include "wrap_mldsa_native.h"`, but the macro isn't referenced anywhere, so this block has no effect and the comment is misleading. Please remove it (or add the corresponding conditional logic in the header, but currently there's nothing to disable).
bensze01 (Migrated from github.com) reviewed 2026-06-03 05:13:38 -05:00
@ -0,0 +21,4 @@
#include "threading_internal.h"
#endif
#if defined(MBEDTLS_THREADING_C)
bensze01 (Migrated from github.com) commented 2026-06-03 05:13:38 -05:00

Nothing includes wrap_mldsa_native.h if PSA_CRYPTO_C is disabled, so this should be fine.

Nothing includes `wrap_mldsa_native.h` if `PSA_CRYPTO_C` is disabled, so this should be fine.
bjwtaylor (Migrated from github.com) reviewed 2026-06-04 05:53:16 -05:00
bjwtaylor (Migrated from github.com) left a comment

Still need to do a full review, however an initial comment/question?

Still need to do a full review, however an initial comment/question?
bjwtaylor (Migrated from github.com) commented 2026-06-04 05:51:33 -05:00

Currently the returns from this function are ignored. As drivers/pqcp/src/psa_crypto_mldsa.c:151 immediately overrides the return with 0, is this ok?

Currently the returns from this function are ignored. As drivers/pqcp/src/psa_crypto_mldsa.c:151 immediately overrides the return with 0, is this ok?
bensze01 (Migrated from github.com) reviewed 2026-06-04 07:27:27 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-06-04 07:27:27 -05:00

I've kept the overflow-prevention check as part of the macro, just so we don't have to figure out always_inline for all the compilers we support. (GCC refused to inline the check otherwise)

I've kept the overflow-prevention check as part of the macro, just so we don't have to figure out `always_inline` for all the compilers we support. (GCC refused to inline the check otherwise)
bensze01 (Migrated from github.com) reviewed 2026-06-04 07:32:02 -05:00
bensze01 (Migrated from github.com) commented 2026-06-04 07:32:02 -05:00

No, that doesn't sound correct - this looks like I messed up during my last rebase.

No, that doesn't sound correct - this looks like I messed up during my last rebase.
bensze01 (Migrated from github.com) reviewed 2026-06-04 07:54:21 -05:00
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
bensze01 (Migrated from github.com) commented 2026-06-04 07:54:21 -05:00

Looking around a bit, I see that we have always_inline support for GCC (, Clang?) and IAR in alignment.h, while mldsa-native has support for GCC, Clang and MSVC.

If IAR supports _Pragma(), we could define an ALWAYS_INLINE macro, and not have to mess around with #if/#else.

Looking around a bit, I see that we have `always_inline` support for GCC (, Clang?) and IAR in `alignment.h`, while mldsa-native has support for GCC, Clang and MSVC. If IAR supports `_Pragma()`, we could define an `ALWAYS_INLINE` macro, and not have to mess around with `#if/#else`.
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-06-05 12:53:40 -05:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

## Pull request overview Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-05 12:53:39 -05:00

Macro name has a typo ("VERFIY") which makes the allocator size helpers harder to search for and easy to misread. Rename it to "VERIFY" and update the one use accordingly.

Macro name has a typo ("VERFIY") which makes the allocator size helpers harder to search for and easy to misread. Rename it to "VERIFY" and update the one use accordingly.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-05 12:53:40 -05:00

The #endif comment doesn't match the opening #if condition (it mentions MBEDTLS_THREADING_PTHREAD but the guard is TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC && MBEDTLS_THREADING_C). This can mislead future edits when adjusting the conditionals.

The `#endif` comment doesn't match the opening `#if` condition (it mentions `MBEDTLS_THREADING_PTHREAD` but the guard is `TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC && MBEDTLS_THREADING_C`). This can mislead future edits when adjusting the conditionals.
@ -64,6 +88,7 @@ void smoke_87(data_t *seed)
NULL, 0, public_key), 0);
exit:
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-06-05 12:53:40 -05:00

signature_length is initialized to SIZE_MAX, but in error paths tf_psa_crypto_pqcp_mldsa87_signature_internal() is not guaranteed to write to it. Since the test asserts it becomes 0 on failure, initialize it to 0 so the test doesn't depend on callee behavior in failure cases.

`signature_length` is initialized to `SIZE_MAX`, but in error paths `tf_psa_crypto_pqcp_mldsa87_signature_internal()` is not guaranteed to write to it. Since the test asserts it becomes 0 on failure, initialize it to 0 so the test doesn't depend on callee behavior in failure cases.
bensze01 (Migrated from github.com) reviewed 2026-06-05 12:58:29 -05:00
@ -64,6 +88,7 @@ void smoke_87(data_t *seed)
NULL, 0, public_key), 0);
exit:
bensze01 (Migrated from github.com) commented 2026-06-05 12:58:28 -05:00

I think the current code is fine. Please shout if you agree with Copilot.

I think the current code is fine. Please shout if you agree with Copilot.
bensze01 (Migrated from github.com) reviewed 2026-06-05 13:06:56 -05:00
@ -64,6 +88,7 @@ void smoke_87(data_t *seed)
NULL, 0, public_key), 0);
exit:
bensze01 (Migrated from github.com) commented 2026-06-05 13:06:55 -05:00

Actually, thinking about this a bit further, this will become a problem if we ever increase the buffer size to be larger than what is required for signature_internal, as the mldsa-native function will complete successfully, but we will get MLD_ERR_FAIL as the return value, due to the poison being detected by tf_psa_crypto_pqcp_alloc_done()

Actually, thinking about this a bit further, this will become a problem if we ever increase the buffer size to be larger than what is required for `signature_internal`, as the mldsa-native function will complete successfully, but we will get `MLD_ERR_FAIL` as the return value, due to the poison being detected by `tf_psa_crypto_pqcp_alloc_done()`
gilles-peskine-arm (Migrated from github.com) requested changes 2026-06-08 12:36:46 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

Mostly LGTM for the execution. I like that you're testing all the new failure conditions. But there is a design choice that I think doesn't work, as it's a recurring theme in my change requests: the allocator instrumentation should be tied to entering/leaving driver entry points, rather than entering/leaving mldsa-native functions.

Also, since https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/770 got merged first, there are now more functions to instrument.

Mostly LGTM for the execution. I like that you're testing all the new failure conditions. But there is a design choice that I think doesn't work, as it's a recurring theme in my change requests: the allocator instrumentation should be tied to entering/leaving driver entry points, rather than entering/leaving mldsa-native functions. Also, since https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/770 got merged first, there are now more functions to instrument.
@ -63,6 +63,13 @@
/* Disable SUPERCOP interface. We don't care about that. */
gilles-peskine-arm (Migrated from github.com) commented 2026-06-06 13:14:45 -05:00

As noted below, we'll sometimes need the pattern

  1. start allocator
  2. do stuff using allocator
  3. call mldsa-native function
  4. do stuff using allocator
  5. stop allocator

So putting start/stop-allocator in wrappers doesn't always work. So I think instead we should have the pattern of starting and stopping the allocator at the beginning/end of driver entry points. This would have the major benefit of not having the extra layer of wrapper functions and macros.

As noted [below](https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786/changes#r3367986910), we'll sometimes need the pattern 1. start allocator 2. do stuff using allocator 3. call mldsa-native function 4. do stuff using allocator 5. stop allocator So putting start/stop-allocator in wrappers doesn't always work. So I think instead we should have the pattern of starting and stopping the allocator at the beginning/end of driver entry points. This would have the major benefit of not having the extra layer of wrapper functions and macros.
@ -0,0 +1,82 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-06-05 13:06:03 -05:00

Please follow the naming convention: psa_status_t status = PSA_xxx, int ret = MLD_xxx (or int ret = MBEDTLS_ERR_xxx, but that's not relevant here).

But why would this error be translated, anyway? Why not call tf_psa_crypto_pqcp_alloc_done() directly from psa_crypto_mldsa.c and use its status directly?

Please follow the naming convention: `psa_status_t status = PSA_xxx`, `int ret = MLD_xxx` (or `int ret = MBEDTLS_ERR_xxx`, but that's not relevant here). But why would this error be translated, anyway? Why not call `tf_psa_crypto_pqcp_alloc_done()` directly from `psa_crypto_mldsa.c` and use its status directly?
gilles-peskine-arm (Migrated from github.com) commented 2026-06-05 13:19:49 -05:00

We shouldn't use MLD_ERR_FAIL. That's too vague. This is a “can't happen” error, so it should map to PSA_ERROR_CORRUPTION_DETECTED or perhaps PSA_ERROR_BAD_STATE. This is especially problematic with verify, where MLD_ERR_FAIL maps to PSA_ERROR_INVALID_SIGNATURE.

We shouldn't use `MLD_ERR_FAIL`. That's too vague. This is a “can't happen” error, so it should map to `PSA_ERROR_CORRUPTION_DETECTED` or perhaps `PSA_ERROR_BAD_STATE`. This is especially problematic with verify, where `MLD_ERR_FAIL` maps to `PSA_ERROR_INVALID_SIGNATURE`.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-06 13:04:33 -05:00

Note that we'll want to use tf_psa_crypto_pqcp_alloc_buffer in our own code, not just inside mldsa-native functions. Specifically, we have a request to avoid keys on the stack in seed_to_public_key() and tf_psa_crypto_mldsa_sign_message(), and at least in seed_to_public_key(), the plan is to use tf_psa_crypto_pqcp_alloc_buffer.

Note that we'll want to use `tf_psa_crypto_pqcp_alloc_buffer` in our own code, not just inside mldsa-native functions. Specifically, we have a request to avoid keys on the stack in `seed_to_public_key()` and `tf_psa_crypto_mldsa_sign_message()`, and at least in `seed_to_public_key()`, the plan is to use `tf_psa_crypto_pqcp_alloc_buffer`.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-06 13:06:10 -05:00

“This shouldn't happen” → “This should be the case”? The way it's worded now, it seems like it should not happen that there is room.

“This shouldn't happen” → “This should be the case”? The way it's worded now, it seems like it should not happen that there is room.
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-06-07 14:00:56 -05:00

How did you determine these values? Through manual reading of the library source, or using tooling?

We need to know in case mldsa-native evolves and the memory usage changes.

How did you determine these values? Through manual reading of the library source, or using tooling? We need to know in case mldsa-native evolves and the memory usage changes.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-07 14:23:33 -05:00

This is supposed to be sufficient for all operations. It's very likely that verify will always be cheaper than sign, but I'm not so sure about keypair. Please at least add static asserts, or a chain of #if, to ensure that the value is always the largest size, or at least that the compilation will fail if it isn't.

This is supposed to be sufficient for all operations. It's very likely that verify will always be cheaper than sign, but I'm not so sure about keypair. Please at least add static asserts, or a chain of `#if`, to ensure that the value is always the largest size, or at least that the compilation will fail if it isn't.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-08 12:33:04 -05:00

Why not use MLD_TOTAL_ALLOC_87 and friends?

Why not use `MLD_TOTAL_ALLOC_87` and friends?
gilles-peskine-arm (Migrated from github.com) commented 2026-06-07 14:18:02 -05:00

Note: also verify_internal since https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/770.

Note: also `verify_internal` since https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/770.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-08 12:03:55 -05:00

Can we have an extra mode POISON_ON_START which just pre-allocates a certain number of bytes, and which is tested even when threading is disabled?

Can we have an extra mode `POISON_ON_START` which just pre-allocates a certain number of bytes, and which is tested even when threading is disabled?
gilles-peskine-arm (Migrated from github.com) commented 2026-06-08 12:08:58 -05:00

I'm in two minds about injecting failures through mutex functions. On the one hand, it's handy that it works without any special requirements on the library code. On the other hand, it does mean that we only test the threading case, so we might miss if something critical is guarded by MBEDTLS_THREADING_C when it shouldn't. Also, the fact that mutex functions are called through function pointers is a legacy quirk that we may want to change at some point. Overall it's a code smell. We have MBEDTLS_TEST_HOOKS for this sort of stuff.

I'm in two minds about injecting failures through mutex functions. On the one hand, it's handy that it works without any special requirements on the library code. On the other hand, it does mean that we only test the threading case, so we might miss if something critical is guarded by `MBEDTLS_THREADING_C` when it shouldn't. Also, the fact that mutex functions are called through function pointers is a legacy quirk that we may want to change at some point. Overall it's a code smell. We have `MBEDTLS_TEST_HOOKS` for this sort of stuff.
@ -64,6 +88,7 @@ void smoke_87(data_t *seed)
NULL, 0, public_key), 0);
exit:
gilles-peskine-arm (Migrated from github.com) commented 2026-06-05 15:17:02 -05:00

In high-level APIs, we want to ensure that *foo_length <= foo_size even on error, so we initialize foo_length = SIZE_MAX in tests and assert foo_length <= foo_size after calling the function.

Here, this is a low-level API, so we don't have to ensure that property, but it's still not a bad idea.

There's no reason to initialize to 0, but we could choose not to assert signature_length on failure.

In high-level APIs, we want to ensure that `*foo_length <= foo_size` even on error, so we initialize `foo_length = SIZE_MAX` in tests and assert `foo_length <= foo_size` after calling the function. Here, this is a low-level API, so we don't have to ensure that property, but it's still not a bad idea. There's no reason to initialize to 0, but we could choose not to assert `signature_length` on failure.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-08 12:27:55 -05:00

Somewhat related to https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786/changes#r3368000351 and https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786/changes#r3364569741 : testing at the level of mldsa-native function calls, rather than pqcp driver entry points, reduces the test coverage. In particular, it doesn't test error propagation. It misses bugs such as the one fixed in https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/778.

We should have this testing in test_suite_psa_crypto_mldsa in addition to here, or instead of here. I don't think we gain much by having them in two places, so I think “instead of” is the right way.

Somewhat related to https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786/changes#r3368000351 and https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786/changes#r3364569741 : testing at the level of mldsa-native function calls, rather than pqcp driver entry points, reduces the test coverage. In particular, it doesn't test error propagation. It misses bugs such as the one fixed in https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/778. We should have this testing in `test_suite_psa_crypto_mldsa` in addition to here, or instead of here. I don't think we gain much by having them in two places, so I think “instead of” is the right way.
bjwtaylor (Migrated from github.com) reviewed 2026-06-15 08:18:18 -05:00
bjwtaylor (Migrated from github.com) left a comment
No description provided.
Approved subject to Gilles comments.
bensze01 commented 2026-06-18 13:10:28 -05:00 (Migrated from github.com)

I've pushed the bulk of the rework - I'll add the multipart testcases, and POISON_ON_START later tonight.

I've pushed the bulk of the rework - I'll add the multipart testcases, and `POISON_ON_START` later tonight.
gilles-peskine-arm (Migrated from github.com) requested changes 2026-06-19 11:50:46 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

I have reviewed everything at ef8131f74c1cd30410509ded6e14882ead0ae12d except the changes in .data files, i.e. basically everything except the new poisoning tests. I haven't reviewed that part partly because that's all I have time for today, and partly because as noted below I don't like adding more parameters to the existing functions, but I want to sleep on it.

Other than this aspect, I'm happy with the design and the execution except in a few minor places. (Also the CI is unhappy.)

I have reviewed everything at ef8131f74c1cd30410509ded6e14882ead0ae12d except the changes in `.data` files, i.e. basically everything except the new poisoning tests. I haven't reviewed that part partly because that's all I have time for today, and partly because [as noted below](https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786#discussion_r3443704577) I don't like adding more parameters to the existing functions, but I want to sleep on it. Other than this aspect, I'm happy with the design and the execution except in a few minor places. (Also the CI is unhappy.)
@ -0,0 +1,82 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 11:27:08 -05:00

Minor: this hasn't been addressed.

Minor: this hasn't been addressed.
@ -382,12 +416,17 @@ psa_status_t tf_psa_crypto_mldsa_sign_setup(
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:27:34 -05:00

Minor: most places prioritize alloc_status over the “normal” status, but here it's the opposite. That's not really important since apart from programming errors in mldsa-native or in our code, the only case where they're both nonzero should be when they're both INSUFFICIENT_MEMORY.

Minor: most places prioritize `alloc_status` over the “normal” status, but here it's the opposite. That's not really important since apart from programming errors in mldsa-native or in our code, the only case where they're both nonzero should be when they're both `INSUFFICIENT_MEMORY`.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:42:40 -05:00
    if (status != PSA_SUCCESS || alloc_status != PSA_SUCCESS) {
```suggestion if (status != PSA_SUCCESS || alloc_status != PSA_SUCCESS) { ```
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:22:02 -05:00

Minor: “we may allocate memory” originally only referred to using mbedtls_calloc inside our own code. Now it's both that and the allocator inside mldsa-native which requires us to call tf_psa_crypto_pqcp_alloc_done(). It would help to update this comment.

Minor: “we may allocate memory” originally only referred to using `mbedtls_calloc` inside our own code. Now it's both that and the allocator inside mldsa-native which requires us to call `tf_psa_crypto_pqcp_alloc_done()`. It would help to update this comment.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:23:53 -05:00

Minor: please add a comment reminding that from that point onwards, we must not return without calling tf_psa_crypto_pqcp_alloc_done().

Same in verify_finish.

Minor: please add a comment reminding that from that point onwards, we must not return without calling `tf_psa_crypto_pqcp_alloc_done()`. Same in `verify_finish`.
@ -514,3 +514,35 @@ component_test_pqcp_own_shake_no_builtin () {
msg "test: TF_PSA_CRYPTO_PQCP_OWN_SHAKE, built-in SHA3/SHAKE disabled"
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:46:58 -05:00

The mldsa-native shake implementation doesn't use MLD_ALLOC. So, from a white-box perspective, we don't need to test the combination of TF_PSA_CRYPTO_PQCP_OWN_SHAKE and TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC. Given that we're trying to keep the number of all.sh components down, please remove this one, as well as the corresponding line in test_suite_config.crypto_combinations.data.

The mldsa-native shake implementation doesn't use `MLD_ALLOC`. So, from a white-box perspective, we don't need to test the combination of `TF_PSA_CRYPTO_PQCP_OWN_SHAKE` and `TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC`. Given that we're trying to keep the number of `all.sh` components down, please remove this one, as well as the corresponding line in `test_suite_config.crypto_combinations.data`.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:50:40 -05:00

We're trying to keep the number of all.sh components down, so I think that three components for one new option is too much. Test hooks aren't supposed to change how the code works by design (they're supposed to need some runtime action to change the behavior), so I think we don't need to test both with and without hooks. Testing both with and without threads does make sense, mostly to check that we're protecting lock/unlock calls with defined(MBEDTLS_THREADING_C).

So how about just testing hooks+threads, and prod+singlethreaded? (Impacts both all.sh and test_suite_config.crypto_combinations.data.)

We're trying to keep the number of `all.sh` components down, so I think that three components for one new option is too much. Test hooks aren't supposed to change how the code works by design (they're supposed to need some runtime action to change the behavior), so I think we don't need to test both with and without hooks. Testing both with and without threads does make sense, mostly to check that we're protecting lock/unlock calls with `defined(MBEDTLS_THREADING_C)`. So how about just testing hooks+threads, and prod+singlethreaded? (Impacts both `all.sh` and `test_suite_config.crypto_combinations.data`.)
@ -19,0 +34,4 @@
} while (0)
#else
#define PQCP_INIT()
#define PQCP_DONE()
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 11:38:23 -05:00

The CI points out that MLD_DEFAULT_ALIGN isn't available here.

The CI points out that `MLD_DEFAULT_ALIGN` isn't available here.
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:54:55 -05:00

Minor: maybe we should put this boilerplate into a pair of macros PQCP_INIT(); PQCP_DONE() (like we have PSA_INIT() and PSA_DONE())?

Minor: maybe we should put this boilerplate into a pair of macros `PQCP_INIT(); PQCP_DONE()` (like we have `PSA_INIT()` and `PSA_DONE()`)?
@ -48,3 +71,4 @@
/* Calculate the expanded key pair from the seed */
TEST_EQUAL(tf_psa_crypto_pqcp_mldsa87_keypair_internal(
public_key, secret_key,
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 09:55:49 -05:00

We can't use TEST_EQUAL after the exit: label: that would cause an infinite loop on failure. We need to mark the test as failed, but keep running normally. See how PSA_DONE() does it.

We can't use `TEST_EQUAL` after the `exit:` label: that would cause an infinite loop on failure. We need to mark the test as failed, but keep running normally. See how `PSA_DONE()` does it.
@ -12,0 +53,4 @@
0x7a, 0x2d, 0xb1, 0xf4, 0x46, 0x00, 0x27, 0xa9, 0x16, 0x2b, 0xc9, 0x9b, 0xda, 0x74, 0x25, 0x5c,
0xc3, 0x36, 0x20, 0x6b, 0xfa, 0x3d, 0x55, 0xff, 0x70, 0x66, 0x8b, 0xaf, 0x97, 0x57, 0xcf, 0x5a,
0xd0, 0x42, 0x7e, 0x37, 0x85, 0x66, 0xb3, 0xe6, 0x5e, 0x5d, 0x5c, 0x8f, 0xf4, 0xe8, 0x21, 0x87,
0xa6, 0xe1, 0xbe, 0xee, 0x53, 0x15, 0xc3, 0xaf, 0xdb, 0xd1, 0xf9, 0x0b, 0xcf, 0xe8, 0x97, 0xec,
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 11:48:28 -05:00

I'm unsure about the test arrangements. I like the poisoning mechanism. What I don't like much is that it's added to all the test functions. It has quite a few downsides:

  • More complex code in functions whose main goal is to test other aspects: data validation, normal operation or both.
  • More complex test data.
  • In particular the test generator needs to change, which is a bit of a hassle due to the repository split. (You need to make the new behavior an option to the test generator in scripts/maintainer/generate_mldsa_tests.py, like what's already there with multipart=True.)

I'm not fully convinced, but I think it would be better to have dedicated functions for the poisoning. These functions wouldn't need a variety of test data, and they don't need to test the invalid inputs that we reject before calling mldsa-native functions.

I'm unsure about the test arrangements. I like the poisoning mechanism. What I don't like much is that it's added to all the test functions. It has quite a few downsides: * More complex code in functions whose main goal is to test other aspects: data validation, normal operation or both. * More complex test data. * In particular the test generator needs to change, which is a bit of a hassle due to the repository split. (You need to make the new behavior an option to the test generator in `scripts/maintainer/generate_mldsa_tests.py`, like what's already there with `multipart=True`.) I'm not fully convinced, but I think it would be better to have dedicated functions for the poisoning. These functions wouldn't need a variety of test data, and they don't need to test the invalid inputs that we reject before calling mldsa-native functions.
@ -12,0 +296,4 @@
0xdc, 0x94, 0xf9, 0x48, 0xfc, 0xdc, 0x7a, 0xd7, 0x50, 0xf7, 0xb9, 0xe4, 0x1b, 0xb9, 0x6b, 0x91,
0x42, 0xd4, 0x45, 0x5d, 0x19, 0x8d, 0xba, 0x08, 0xaa, 0xcd, 0x2b, 0xf1, 0x62, 0x6f, 0xb8, 0x85,
0x3b, 0xff, 0xd6, 0x08, 0x36, 0x92, 0x50, 0x58, 0x9e, 0x77, 0x3e, 0x89, 0x3d, 0x25, 0xad, 0xe7,
0x91, 0xef, 0xcc, 0xdb, 0x00, 0xfd, 0x98, 0x9c, 0x4a, 0x4d, 0x99, 0xa0, 0xff, 0xf7, 0x30, 0xe6,
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 10:00:48 -05:00

Missing poison_cleanup in sign_setup.

Missing `poison_cleanup` in `sign_setup`.
@ -12,0 +529,4 @@
static void poison_cleanup(void)
{
#if defined(MBEDTLS_TEST_HOOKS)
gilles-peskine-arm (Migrated from github.com) commented 2026-06-19 11:38:38 -05:00
#if defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC) && defined(MBEDTLS_TEST_HOOKS)
```suggestion #if defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC) && defined(MBEDTLS_TEST_HOOKS) ```
gilles-peskine-arm (Migrated from github.com) requested changes 2026-07-01 08:57:25 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

I'm fully happy with the design. I want a few more explanations in some places, a little more test coverage, and there are a couple of points I'm unsure of. The rest of my comments are optional.

I'm fully happy with the design. I want a few more explanations in some places, a little more test coverage, and there are a couple of points I'm unsure of. The rest of my comments are optional.
@ -0,0 +9,4 @@
defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC)
#include "wrap_mldsa_native.h"
#include "src/common.h"
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:40:19 -05:00

Non-blocker: It bothers me a little that we're including an internal header of mldsa-native, only to get the definition of MLD_ALIGN. I'm not sure whether we really should rely on their definition of MLD_ALIGN — for example, they don't support IAR.

If we make our own equivalent macro, it won't affect uses of MLD_ALIGN inside mldsa-native anyway, so we'd need to patch mldsa-native for IAR support. However, IAR tests have passed. I think that's because the internal uses of MLD_ALIGN are for performance only.

Here, MLD_ALIGN is needed for correctness, not just performance, on architectures where 32-bit words need to be aligned on a 4-byte boundary. (TODO: test an IAR build. Note, if it doesn't work, that's an acceptable bug for now, although we should file it as a GitHub issue.) So arguably we should do our own stuff, which would have the benefit of not including a private header.

Suggestion: instead of MLD_ALIGN, use

static union {
    uint8_t buffer[TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE];
    uintmax_t for_alignment_only;
} tf_psa_crypto_pqcp_alloc_buffer;

which is fully portable and avoids an external dependency.

Non-blocker: It bothers me a little that we're including an internal header of mldsa-native, only to get the definition of `MLD_ALIGN`. I'm not sure whether we really should rely on their definition of `MLD_ALIGN` — for example, they don't support IAR. If we make our own equivalent macro, it won't affect uses of `MLD_ALIGN` inside mldsa-native anyway, so we'd need to patch mldsa-native for IAR support. However, IAR tests have passed. I think that's because the internal uses of `MLD_ALIGN` are for performance only. Here, `MLD_ALIGN` is needed for correctness, not just performance, on architectures where 32-bit words need to be aligned on a 4-byte boundary. (TODO: test an IAR build. Note, if it doesn't work, that's an acceptable bug for now, although we should file it as a GitHub issue.) So arguably we should do our own stuff, which would have the benefit of not including a private header. Suggestion: instead of `MLD_ALIGN`, use ``` static union { uint8_t buffer[TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE]; uintmax_t for_alignment_only; } tf_psa_crypto_pqcp_alloc_buffer; ``` which is fully portable and avoids an external dependency.
@ -0,0 +1,88 @@
/* A simple bump allocator for mldsa-native, allocating from a global buffer */
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:31:59 -05:00

Please at least add a comment explaining that this is sized for applications that use all the features, and that applications that don't use all operations could use a smaller size. In particular, applications that only do verification (e.g. a bootloader) could use MLD_TOTAL_ALLOC_87_VERIFY.

We don't yet have a way to declare “I only want verification” in the configuration. But maybe we should expose TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE as a config option for now?

Please at least add a comment explaining that this is sized for applications that use all the features, and that applications that don't use all operations could use a smaller size. In particular, applications that only do verification (e.g. a bootloader) could use `MLD_TOTAL_ALLOC_87_VERIFY`. We don't yet have a way to declare “I only want verification” in the configuration. But maybe we should expose `TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE` as a config option for now?
@ -16,6 +16,27 @@
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:25:51 -05:00

Please remove this redundant definition.

Please remove this redundant definition.
@ -45,6 +66,9 @@ void smoke_87(data_t *seed)
TEST_CALLOC(public_key, public_key_length);
TEST_CALLOC(signature, signature_size);
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:27:20 -05:00

Non-blocker: That's a lot of hassle: having to extend the include path, and directly including an internal header that may change. All for a single check with limited value.

This check only depends on compile-time values, so it would be better implemented as a static assert. We can do it in the definition of TF_PSA_CRYPTO_PQCP_CUSTOM_ALLOC:

        MBEDTLS_STATIC_ASSERT(TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE % MLD_DEFAULT_ALIGN == 0, \
                              "TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE is not a multiple of MLD_DEFAULT_ALIGN"); \
        if ((N) > 0 && (N) <= TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE / sizeof(T)) { \
            (v) = tf_psa_crypto_pqcp_alloc_push(MLD_ALIGN_UP(sizeof(T) * (N))); \
        } \
Non-blocker: That's a lot of hassle: having to extend the include path, and directly including an internal header that may change. All for a single check with limited value. This check only depends on compile-time values, so it would be better implemented as a static assert. We can do it in the definition of `TF_PSA_CRYPTO_PQCP_CUSTOM_ALLOC`: ``` MBEDTLS_STATIC_ASSERT(TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE % MLD_DEFAULT_ALIGN == 0, \ "TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE is not a multiple of MLD_DEFAULT_ALIGN"); \ if ((N) > 0 && (N) <= TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE / sizeof(T)) { \ (v) = tf_psa_crypto_pqcp_alloc_push(MLD_ALIGN_UP(sizeof(T) * (N))); \ } \ ```
@ -7,8 +7,535 @@
* validation.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:47:09 -05:00

Minor: we're already in an #if defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC) block. It doesn't really matter, but it's weird that poison_setup and poison_cleanup are inconsistent.

Minor: we're already in an `#if defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC)` block. It doesn't really matter, but it's weird that `poison_setup` and `poison_cleanup` are inconsistent.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:53:48 -05:00

Minor: this looks like a job for switch.

Minor: this looks like a job for `switch`.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:54:42 -05:00

Shouldn't we assert poison_mode == 0 when the poisoning code wouldn't work?

Shouldn't we assert `poison_mode == 0` when the poisoning code wouldn't work?
@ -10,2 +11,4 @@
#include <pqcp_buffer_alloc.h>
#include <psa_crypto_mldsa.h>
enum poison_mode {
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:22:40 -05:00

Minor: it's often convenient to have a defined value for “go through the poisoning code, but don't actually disrupt operations”, conventionally 0. I don't see an immediate use for it though, since the poisoning test functions just wrap around the non-poisoning test functions.

Minor: it's often convenient to have a defined value for “go through the poisoning code, but don't actually disrupt operations”, conventionally 0. I don't see an immediate use for it though, since the poisoning test functions just wrap around the non-poisoning test functions.
@ -12,0 +33,4 @@
* before we reach the method being tested.
*
* Requires MBEDTLS_TEST_HOOKS */
POISON_ON_UNLOCK,
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:24:45 -05:00

Please document what the poisoning modes do. For example, POISON_LOCK allocates poison_bytes bytes on allocation start in the driver entry point (which could be subtly different from reserving at the beginning of each test case, or before each call to an mldsa-native function).

Please document what the poisoning modes do. For example, `POISON_LOCK` allocates `poison_bytes` bytes on allocation start in the driver entry point (which could be subtly different from reserving at the beginning of each test case, or before each call to an mldsa-native function).
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:27:15 -05:00

It's not clear to me what problem POISON_ON_UNLOCK catches, that POISON_ON_LOCK wouldn't catch.

It's not clear to me what problem `POISON_ON_UNLOCK` catches, that `POISON_ON_LOCK` wouldn't catch.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 07:57:05 -05:00

Here and in the other functions, why not point to seed_1 and so on directly?

Maybe we should take a copy systematically for inputs that we tweak. But right now none of the inputs are tweaked.

Here and in the other functions, why not point to `seed_1` and so on directly? Maybe we should take a copy systematically for inputs that we tweak. But right now none of the inputs are tweaked.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:10:47 -05:00

For test coverage, we should test some of the failure cases of verification: some error paths might not be deallocating properly. At least we should try a public key that's too short.

Either by calling test_export_public_key multiple times (with mbedtls_test_set_step for good failure reporting), or with separate test cases with an extra parameter to indicate the input variant.

Also applies to multipart verification.

For export-public and sign, the error cases should not be reachable from the API, so testing them isn't important.

For test coverage, we should test some of the failure cases of verification: some error paths might not be deallocating properly. At least we should try a public key that's too short. Either by calling `test_export_public_key` multiple times (with `mbedtls_test_set_step` for good failure reporting), or with separate test cases with an extra parameter to indicate the input variant. Also applies to multipart verification. For export-public and sign, the error cases should not be reachable from the API, so testing them isn't important.
@ -0,0 +1,74 @@
MLDSA-87 export public key: poison on lock, sufficient memory
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:13:32 -05:00

Non-blocker: this file is written in a very systematic way, so it would be nice to have a script.

Non-blocker: this file is written in a very systematic way, so it would be nice to have a script.
@ -0,0 +37,4 @@
# The setup step only requires MLD_TOTAL_ALLOC_87_KEYPAIR bytes of buffer memory, the signature generation happens in finish()
MLDSA-87 sign setup: poison on lock, sufficient memory
depends_on:TF_PSA_CRYPTO_PQCP_MLDSA_87_ENABLED:TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC:MBEDTLS_TEST_HOOKS
poison_sign_setup:POISON_ON_LOCK:TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE-MLD_TOTAL_ALLOC_87_KEYPAIR:PSA_SUCCESS
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:18:46 -05:00

Non-blocker: The fact that sign_setup requires MLD_TOTAL_ALLOC_87_KEYPAIR bytes rather than MLD_TOTAL_ALLOC_87_SIGN warrants a comment.

Non-blocker: The fact that `sign_setup` requires `MLD_TOTAL_ALLOC_87_KEYPAIR` bytes rather than `MLD_TOTAL_ALLOC_87_SIGN` warrants a comment.
@ -0,0 +53,4 @@
# We don't test with full-buffer poisoning, because it would cause a failure in tf_psa_crypto_mldsa_sign_setup(), before the test gets to finish()
# MLD_CONFIG_REDUCE_RAM makes keypair() take more memory than sign(), so we must disable this test.
MLDSA-87 sign deterministic multipart finish: poison on lock, insufficient memory
gilles-peskine-arm (Migrated from github.com) commented 2026-07-01 08:28:29 -05:00

Why test insufficient memory for this function, but only full buffer for the others?

Why test insufficient memory for this function, but only full buffer for the others?
bensze01 (Migrated from github.com) reviewed 2026-07-02 07:20:28 -05:00
@ -0,0 +53,4 @@
# We don't test with full-buffer poisoning, because it would cause a failure in tf_psa_crypto_mldsa_sign_setup(), before the test gets to finish()
# MLD_CONFIG_REDUCE_RAM makes keypair() take more memory than sign(), so we must disable this test.
MLDSA-87 sign deterministic multipart finish: poison on lock, insufficient memory
bensze01 (Migrated from github.com) commented 2026-07-02 07:20:28 -05:00

A full buffer would fail at the setup() step - that's why I've modified it, so it only fails at finish()

A full buffer would fail at the `setup()` step - that's why I've modified it, so it only fails at `finish()`
bensze01 (Migrated from github.com) reviewed 2026-07-02 07:22:45 -05:00
@ -12,0 +33,4 @@
* before we reach the method being tested.
*
* Requires MBEDTLS_TEST_HOOKS */
POISON_ON_UNLOCK,
bensze01 (Migrated from github.com) commented 2026-07-02 07:22:45 -05:00

Hmm. I can only think of a few edge cases. Eg: in https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786#discussion_r3512945190 I could choose a smaller buffer size that passed setup(), but if the memory required for setup() was larger, I might not be able to choose a small enough poison size that only fails on finish().

Hmm. I can only think of a few edge cases. Eg: in https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/786#discussion_r3512945190 I could choose a smaller buffer size that passed `setup()`, but if the memory required for `setup()` was larger, I might not be able to choose a small enough poison size that only fails on `finish()`.
bensze01 (Migrated from github.com) reviewed 2026-07-02 07:24:22 -05:00
@ -7,8 +7,535 @@
* validation.
bensze01 (Migrated from github.com) commented 2026-07-02 07:24:22 -05:00

Poisoning with a size of 0 bytes is equivalent to doing nothing with any mode, which is why I only asserted on that parameter.

Poisoning with a size of `0` bytes is equivalent to doing nothing with any mode, which is why I only asserted on that parameter.
bensze01 (Migrated from github.com) reviewed 2026-07-02 07:27:42 -05:00
bensze01 (Migrated from github.com) commented 2026-07-02 07:27:42 -05:00

I couldn't define the buffers as const, because the test system rejects const parameters.

My other options were casting away the const modifier, or using mutable global variables, and I thought a failure or segfault would be harder to debug. But I'm willing to go for either solution, if you'd prefer that.

I couldn't define the buffers as `const`, because the test system rejects `const` parameters. My other options were casting away the `const` modifier, or using mutable global variables, and I thought a failure or segfault would be harder to debug. But I'm willing to go for either solution, if you'd prefer that.
bensze01 (Migrated from github.com) reviewed 2026-07-02 07:48:38 -05:00
@ -0,0 +9,4 @@
defined(TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC)
#include "wrap_mldsa_native.h"
#include "src/common.h"
bensze01 (Migrated from github.com) commented 2026-07-02 07:48:38 -05:00

For the record, AFAIU IAR supports _Pragma("data_alignment=32")

For the record, AFAIU IAR supports `_Pragma("data_alignment=32")`
gilles-peskine-arm (Migrated from github.com) reviewed 2026-07-02 08:08:04 -05:00
gilles-peskine-arm (Migrated from github.com) commented 2026-07-02 08:08:04 -05:00

Ah, good point.

I don't like making the globals mutable, because a problem in a test case could contaminate subsequent test cases.

I don't like casting away the const either, but less so. I think it would be valid C since the pointer is only temporarily stored in an object with a pointer-to-non-const type, and then passed to a function that takes it with a pointer-to-const type.

Ideally data_t::x would be a pointer-to-const, but that would probably require changing a lot of code.

So I lean towards casting away the const. But I'm not opposed to copying, as long as there's a comment explaining why.

Ah, good point. I don't like making the globals mutable, because a problem in a test case could contaminate subsequent test cases. I don't like casting away the const either, but less so. I think it would be valid C since the pointer is only temporarily stored in an object with a pointer-to-non-const type, and then passed to a function that takes it with a pointer-to-const type. Ideally `data_t::x` would be a pointer-to-const, but that would probably require changing a lot of code. So I lean towards casting away the const. But I'm not opposed to copying, as long as there's a comment explaining why.
gilles-peskine-arm (Migrated from github.com) reviewed 2026-07-02 11:48:01 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

(reviewed up to 8942ba92ae, no new comments)

(reviewed up to 8942ba92ae3c7db06e737f9a693d873d59390aeb, no new comments)
bensze01 (Migrated from github.com) reviewed 2026-07-06 03:21:02 -05:00
@ -45,6 +66,9 @@ void smoke_87(data_t *seed)
TEST_CALLOC(public_key, public_key_length);
TEST_CALLOC(signature, signature_size);
bensze01 (Migrated from github.com) commented 2026-07-06 03:21:02 -05:00

The include path had to be modified so that I could include wrap_mldsa_native.h and MLD_TOTAL_ALLOC_87_* in tests/suites/test_suite_psa_crypto_mldsa.poison.data

The include path had to be modified so that I could include `wrap_mldsa_native.h` and `MLD_TOTAL_ALLOC_87_*` in `tests/suites/test_suite_psa_crypto_mldsa.poison.data`
bensze01 (Migrated from github.com) reviewed 2026-07-06 03:36:34 -05:00
bensze01 (Migrated from github.com) commented 2026-07-06 03:36:34 -05:00

I think the existing tests should catch this in tf_psa_crypto_pqcp_alloc_done, as long as we verify that tf_psa_crypto_pqcp_alloc_done() detects memory leaks in the buffer.

I think the existing tests should catch this in `tf_psa_crypto_pqcp_alloc_done`, as long as we verify that `tf_psa_crypto_pqcp_alloc_done()` detects memory leaks in the buffer.
bensze01 (Migrated from github.com) reviewed 2026-07-06 04:09:23 -05:00
bensze01 (Migrated from github.com) commented 2026-07-06 04:09:23 -05:00

Ah, I guess some failure cases might potentially fail to call tf_psa_crypto_pqcp_alloc_done().

Ah, I guess some failure cases might potentially fail to call `tf_psa_crypto_pqcp_alloc_done()`.
bensze01 (Migrated from github.com) reviewed 2026-07-06 13:58:38 -05:00
bensze01 (Migrated from github.com) commented 2026-07-06 13:58:38 -05:00

For the multipart case, the key size is checked in setup(), but that step doesn't use the buffer allocator.

For the multipart case, the key size is checked in setup(), but that step doesn't use the buffer allocator.
bensze01 (Migrated from github.com) reviewed 2026-07-06 15:41:19 -05:00
@ -12,0 +33,4 @@
* before we reach the method being tested.
*
* Requires MBEDTLS_TEST_HOOKS */
POISON_ON_UNLOCK,
bensze01 (Migrated from github.com) commented 2026-07-06 15:41:19 -05:00

The edge case I described above happens if MLD_CONFIG_REDUCE_RAM is enabled.

The edge case I described above happens if `MLD_CONFIG_REDUCE_RAM` is enabled.
gilles-peskine-arm (Migrated from github.com) requested changes 2026-07-07 01:40:13 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

LGTM once the build failure is resolved and some documentation improvements are made

LGTM once the build failure is resolved and some documentation improvements are made
@ -0,0 +14,4 @@
#if !defined(TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE)
#define TF_PSA_CRYPTO_PQCP_ALLOC_BUFFER_SIZE MLD_TOTAL_ALLOC_87
#endif
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 01:36:30 -05:00

We need to keep

#if !defined(OPTION_WITH_VALUE)
#define OPTION_WITH_VALUE default value
#endif

since users don't have to define the option if they're happy with the default value.

We need to keep ``` #if !defined(OPTION_WITH_VALUE) #define OPTION_WITH_VALUE default value #endif ``` since users don't have to define the option if they're happy with the default value.
@ -2068,0 +2074,4 @@
* the stack.
*
* \warning This option is experimental. It may change or be removed without
* notice.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 01:38:51 -05:00

We should mention that the default value is generally suitable, and the only reason to deviate from the default is if your application doesn't use all the operations.

Also, the plausible values are MLD_TOTAL_ALLOC_xxx macros which are not defined by the TF-PSA-Crypto code and are not in scope when the config file is parsed. We should explain where to find the available macros in the source tree.

We should mention that the default value is generally suitable, and the only reason to deviate from the default is if your application doesn't use all the operations. Also, the plausible values are `MLD_TOTAL_ALLOC_xxx` macros which are not defined by the TF-PSA-Crypto code and are not in scope when the config file is parsed. We should explain where to find the available macros in the source tree.
@ -9,2 +9,4 @@
#include <wrap_mldsa_native.h>
#include <pqcp_buffer_alloc.h>
#include <psa_crypto_mldsa.h>
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 01:21:15 -05:00

That's how it works internally, but not why you'd use it. The useful high-level description is that it consumes poison_bytes extra bytes for the duration of the operation.

That's how it works internally, but not why you'd use it. The useful high-level description is that it consumes `poison_bytes` extra bytes for the duration of the operation.
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 01:26:18 -05:00

This is somewhat similar to reducing the buffer size, but not quite. It would be useful to explain how.

This is somewhat similar to reducing the buffer size, but not quite. It would be useful to explain how.
@ -12,0 +33,4 @@
* before we reach the method being tested.
*
* Requires MBEDTLS_TEST_HOOKS */
POISON_ON_UNLOCK,
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 01:34:12 -05:00

Future maintainers should be able to understand this by reading the comments on POISON_ON_xxx. Please expand on these comments.

Future maintainers should be able to understand this by reading the comments on `POISON_ON_xxx`. Please expand on these comments.
gilles-peskine-arm (Migrated from github.com) requested changes 2026-07-07 06:10:16 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

I'd like an improvement to the public documentation, then LGTM

I'd like an improvement to the public documentation, then LGTM
@ -2065,6 +2065,50 @@
*/
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 06:01:55 -05:00
 * The size of the global buffer used by mldsa-native for large allocations
 * when #TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC is enabled.
 * This has no effect when #TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC is disabled.
```suggestion * The size of the global buffer used by mldsa-native for large allocations * when #TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC is enabled. * This has no effect when #TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC is disabled. ```
@ -2068,0 +2077,4 @@
* notice.
*
* Module: drivers/pqcp/src/pqcp_buffer_alloc.c
*
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 06:09:38 -05:00
 *
 * Requires: TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED
 *
```suggestion * * Requires: TF_PSA_CRYPTO_PQCP_MLDSA_ENABLED * ```
@ -2068,0 +2102,4 @@
* notice.
*
* Module: drivers/pqcp/src/pqcp_buffer_alloc.c
*
gilles-peskine-arm (Migrated from github.com) commented 2026-07-07 06:09:13 -05:00
 *
 * Requires: TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC
 *
```suggestion * * Requires: TF_PSA_CRYPTO_PQCP_BUFFER_ALLOC * ```
gilles-peskine-arm (Migrated from github.com) approved these changes 2026-07-07 06:45:34 -05:00
gilles-peskine-arm (Migrated from github.com) left a comment

LGTM

LGTM
bjwtaylor (Migrated from github.com) approved these changes 2026-07-07 07:13:44 -05:00
gilles-peskine-arm commented 2026-07-07 08:14:22 -05:00 (Migrated from github.com)

We're under a merge ban for the release. We'll merge this PR as soon as the merge ban ends.

We're under a merge ban for the release. We'll merge this PR as soon as the merge ban ends.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-Mbed-TLS/TF-PSA-Crypto!786
No description provided.