-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HMAC-SHA-256
test failures on upcoming gcc-15
(after partial union initialization changes)
#9814
Comments
Here is my understanding which fields do and do not get initialized. // at tests/src/psa_exercise_key.c:
psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT;
// library/psa_crypto.c:
if (operation.hash_ctx.id != 0) { return error; }
//...
// include/psa/crypto_struct.h:
#define PSA_MAC_OPERATION_INIT { 0, 0, 0, { 0 } }
// include/psa/crypto.h:
typedef struct psa_mac_operation_s psa_mac_operation_t;
// include/psa/crypto_struct.h:
struct psa_mac_operation_s {
unsigned int id;
uint8_t mac_size;
unsigned int is_sign : 1;
psa_driver_mac_context_t ctx;
};
// include/psa/crypto_driver_contexts_composites.h:
typedef union {
unsigned dummy; /* Make sure this union is always non-empty */
mbedtls_psa_mac_operation_t mbedtls_ctx;
} psa_driver_mac_context_t;
// include/psa/crypto_builtin_composites.h:
typedef struct {
psa_algorithm_t alg;
union {
unsigned dummy; /* Make the union non-empty even with no supported algorithms. */
mbedtls_psa_hmac_operation_t hmac;
mbedtls_cipher_context_t cmac;
} ctx;
} mbedtls_psa_mac_operation_t;
// include/psa/crypto_builtin_composites.h
typedef struct {
/** The HMAC algorithm in use */
psa_algorithm_t alg;
/** The hash context. */
struct psa_hash_operation_s hash_ctx;
/** The HMAC part of the context. */
uint8_t opad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
} mbedtls_psa_hmac_operation_t;
// include/psa/crypto_types.h
typedef uint32_t psa_algorithm_t;
// include/psa/crypto_struct.h
struct psa_hash_operation_s {
/** Unique ID indicating which driver got assigned to do the
* operation. Since driver contexts are driver-specific, swapping
* drivers halfway through the operation is not supported.
* ID values are auto-generated in psa_driver_wrappers.h.
* ID value zero means the context is not valid or not assigned to
* any driver (i.e. the driver context is not active, in use). */
unsigned int id;
psa_driver_hash_context_t ctx;
}; If we compressit into a single declaration it looks this way: struct {
unsigned int id; // initialized below
uint8_t mac_size; // initialized below
unsigned int is_sign : 1; // initialized below
union {
unsigned dummy; // initialized below
struct {
uint32_t alg; // initialized below, alias of `dummy`
// anything below is NOT initialized
union {
unsigned dummy;
struct {
uint32_t alg;
struct {
unsigned int id; // <- we are about to use this field
psa_driver_hash_context_t ctx;
} hash_ctx;
uint8_t opad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
} hmac;
// ..
} mbedtls_ctx;
} ctx;
} operation = {
0, // id
0, // mac_size
0, // is_sign
{ 0 } // ctx.dummy
};
if (operation.hash_ctx.id != 0) { return error; } I hope that makes it clearer. |
Thanks for letting us know! This is a library bug:
or
are both supposed to be correct code. The setup function should work with whatever gets initialized. The fix should be pretty straightforward (put that memset in the setup function) once we figure out which functions are affected. Unfortunately, none of the compilers we test with take advantage of the possibility of optimizing union initializations, so our testing can't find this bug. (The only compiler I had encountered that does this optimization, at least for our code base, is CompCert.) Eventually we'll test with GCC 15, but we prefer not to hook pre-release versions of compilers in our CI. |
Temporarily make a recent GCC snapshot available for testing. We make it available separately, not as gcc-latest, because it is known to break current branches (Mbed-TLS/mbedtls#9814). Once #9814 is fixed in all tested branches (including non-ancient pull requests), which will surely be after the GCC 15 release, we should switch gcc-latest to GCC 15 (or above). Signed-off-by: Gilles Peskine <[email protected]>
Non-regression for Mbed-TLS#9814 Signed-off-by: Gilles Peskine <[email protected]>
Temporarily make a recent GCC snapshot available for testing. We make it available separately, not as gcc-latest, because it is known to break current branches (Mbed-TLS/mbedtls#9814). Once #9814 is fixed in all tested branches (including non-ancient pull requests), which will surely be after the GCC 15 release, we should switch gcc-latest to GCC 15 (or above). Signed-off-by: Gilles Peskine <[email protected]>
As far as I understand, Clang made a similar optimization to union initialization, but backed it out. There, it was an originally unintended consequence of an LLVM optimization. The optimization was present in some 18.x releases (it's not clear to me exactly which ones), but was patched by many distributions, and was removed before 19.0. For example, on Godbolt (thanks to @bensze01 for that example), in |
Some notes on how we're going to fix that. There are several things we can do (not mutually exclusive):
[API] The PSA Crypto API specification explains that you can initialize operation objects in multiple ways (e.g. |
Note that designated initializers are C99 and mbedtls already requires C99 per Line 50 in e9d036a
|
Oh right, my bad. But in C++ they've only been introduced recently, in C++20. And our public headers are meant to be compatible with C++. We've never officially said which version of C++, but C++20 is still a bit too recent to require. So we can't use designated initializers in public headers, I was just misremembering why. |
GCC 15 makes a change to union initialisation and exposes a bug in mbedtls. Build with the new -fzero-init-padding-bits=unions flag if supported to fix the testsuite until the upstream bug is fixed. Bug: Mbed-TLS/mbedtls#9814 Closes: https://bugs.gentoo.org/946544 Signed-off-by: Sam James <[email protected]>
Note: when reading the example it wasn't immediately obvious to me where the nesting was happening - that's because
I seem to be a bit slow this morning, but how exactly is it an ABI change? |
In practice, the layout in memory is very likely to be the same. But a compiler is allowed to compile By the way, I wasn't fully precise: it is an API change! It changes the meaning of |
Ah OK, yes, I was thinking we'd get the same memory layout, but of course the standard makes to guarantees about that.
That's acceptable in the upcoming major release, but still not super user-friendly: people who already use {0} or {} will need to find every place they do so and replace it with the proper macro. If they miss a place, the outcome is likely that their code will compile but be incorrect. I get a feeling that's a situation where someone (either us with the first and last approach, or all users with the middle approach) will need to make sure they're doing the right thing in all places, under penalty of code that compiles but is not correct (with some compiles). It's probably better for us to be taking that burden - and hopefully at some point we can have tests ensuring we do. |
Summary
gcc-15
recently merged https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=0547dbb725b6d8e878a79e28a2e171eafcfbc1aa into it'smaster
branch. Nextgcc-15
release will breakHMAC
tests onmbedtls
as:An example
psa_crypto-suite
failure is:System information
Mbed TLS version (number or commit id): 2ca6c28
Operating system and version: linux-6.11.8,
NixOS
with modifiedgcc
frommaster
branch.Configuration (if not default, please attach
mbedtls_config.h
): default.Compiler and options (if you used a pre-built binary, please indicate how you obtained it):
gcc
frommaster
branch.Additional environment information:
gcc
frommaster
branch.Expected behavior
The tests should pass.
Actual behavior
cmake
reports the following failures:Steps to reproduce
Pick latest
gcc
master
commit and buildmbedtls
against it.Additional information
gcc
changed partial union initialization handling in https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=0547dbb725b6d8e878a79e28a2e171eafcfbc1aaThat means that
PSA_MAC_OPERATION_INIT
is not a correct initializer for all active fields ofpsa_mac_operation_t
.For example in
tests/src/psa_exercise_key.c
psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT;
and leaves hash contexts (used as&hmac->hash_ctx
) with uninitialized data (specificallyhmac->hash_ctx.id
).valgrind
shows a good location of under-initialized union fields as:Here
psa_hash_setup (psa_crypto.c:2298)
tries to accessoperation->hash_ctx.id
.The following change works around some of the test failures (the full fix needs a lot more of those):
But I think it needs a wider set of fixes to account for partial enum initializations all over the place.
Another workaround is to use newly added
-fzero-init-padding-bits=unions
compiler flag.What would be the best fix here?
Thanks!
The text was updated successfully, but these errors were encountered: