-
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
Make PSA global_data thread safe #8901
Make PSA global_data thread safe #8901
Conversation
b5049d6
to
055fad5
Compare
d4c1a31
to
8997508
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few changes needed, and I have some suggestions and questions
include/mbedtls/threading.h
Outdated
* A mutex used to make the PSA global_data struct members thread safe. | ||
* | ||
* This mutex must be held when any read or write to a any of the PSA | ||
* global_data structure members. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are actually two very different psa_global_data_t
structs, each relating to a variable called global_data
. One is defined in psa_crypto.c
, the other in psa_crypto_slot_management.c
. This mutex only covers the former struct. To alleviate confusion I think it would help to say "used to make the members of the global_data struct defined in psa_crypto.c thread safe"
* | ||
* This mutex must be held when any read or write to a any of the PSA | ||
* global_data structure members. */ | ||
extern mbedtls_threading_mutex_t mbedtls_threading_psa_globaldata_mutex; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further to my above comment. global_data.key_slots_initialized
is not protected by this PR in its current state. This variable is written to in psa_crypto_init
and mbedtls_psa_crypo_free
under the new mutex. It is read in psa_reserve_free_key_slot
and psa_get_and_lock_key_slot
.
We can either protect it with your new mutex by making a getter function, or we can protect it with the key slot mutex (key_slot_mutex is currently held in the call to reserve_free_key_slot
but not get_and_lock_key_slot
). A case could be made for either option:
- Code wise, it is slightly cleaner to protect
key_slots_initialized
with the global data mutex. - Documentation wise, protecting it with the key slot mutex is cleaner as then each mutex protects the entirety of one struct.
if (init_context->do_shutdown) { | ||
/* Note that shutting down PSA crypto is *not* guaranteed to be thread | ||
* safe if *any* operations are currently in progress. I am merely doing | ||
* this here to test the safety of global flags that are written in | ||
* psa_crypto_init() / mbedtls_psa_crypto_free(). */ | ||
mbedtls_psa_crypto_free(); | ||
} | ||
|
||
/* Given that another thread may have called mbedtls_psa_crypto_free() by | ||
* this point on another thread, we can only guarantee the return codes | ||
* from the following tests if we are not also testing shutdown. The tests | ||
* are still worth doing though, so that TSAN can catch potential issues. */ | ||
|
||
/* Test getting if drivers are initialised. */ | ||
int can_do = psa_can_do_hash(PSA_ALG_NONE); | ||
|
||
if (!init_context->do_shutdown) { | ||
TEST_ASSERT(can_do == 1); | ||
} | ||
|
||
#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) | ||
|
||
/* Test getting global_data.rng_state. */ | ||
status = mbedtls_psa_crypto_configure_entropy_sources(NULL, NULL); | ||
|
||
if (!init_context->do_shutdown) { | ||
/* Bad state due to entropy sources already being setup in | ||
* psa_crypto_init() */ | ||
TEST_EQUAL(status, PSA_ERROR_BAD_STATE); | ||
} | ||
#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ | ||
|
||
/* Test using the PSA RNG if not testing shutdown (RNG is not safe to use if | ||
* we shut down PSA whilst using it). */ | ||
if (!init_context->do_shutdown) { | ||
status = psa_generate_random(random, sizeof(random)); | ||
|
||
TEST_EQUAL(status, PSA_SUCCESS); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: We can shorten this by explicitely splitting the cases, but if you think the current style is cleaner then keep it as is
if (init_context->do_shutdown) {
/* Note that shutting down PSA crypto is *not* guaranteed to be thread
* safe if *any* operations are currently in progress. I am merely doing
* this here to test the safety of global flags that are written in
* psa_crypto_init() / mbedtls_psa_crypto_free(). */
mbedtls_psa_crypto_free();
goto exit;
} else {
/* Test getting if drivers are initialized.
* We cannot test this if do_shutdown is true, as another thread
* may have already called mbedtls_psa_crypto_free
* which uninitializes the drivers. TSAN can catch potential issues
* here, so we should still do this test. */
TEST_ASSERT(psa_can_do_hash(PSA_ALG_NONE) == 1);
#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
....
#endif
....
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has kinda changed in the latest iteration. I was, however deliberately running the flag test checks at the same time as doing startup / shutdown of PSA.
@@ -10,6 +10,9 @@ deinit_without_init:0 | |||
PSA deinit twice | |||
deinit_without_init:1 | |||
|
|||
PSA threaded init checks | |||
psa_threaded_init:100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a depends_on:MBEDTLS_THREADING_PTHREAD
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is done on the test function itself, rather than the data. The test cannot run without threading, regardless of what the data is set to.
library/psa_crypto.c
Outdated
|
||
return initialized; | ||
} | ||
|
||
#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) | ||
mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = | ||
&global_data.rng.drbg; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: is it okay that the accesses to MBEDTLS_PSA_RANDOM_STATE/mbedtls_psa_random_state are not done under this mutex? I can see a point in the PR description about global_data.rng.drbg
, but I'm a bit confused about the state of thread-safety of RNG.
I.e., is it possible that if multiple threads are each doing ecjpake operations one may have non thread-safe rng behaviour at the blinding stage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, given the way that the state is passed around as a pointer, I don't see how I could protect all the uses. There is already a mutex within mbedtls_ctr_drbg_context
which should protect a lot of these uses, but otherwise its going to be extremely difficult to add more protection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the PSA RNG is implemented on top of CTR_DRBG and HMAC_DRBG, i.e. when MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
is disabled, the PSA part of the code doesn't need to take any precautions when calling get_random
, because the CTR_DRBG and HMAC_DRBG modules have their own mutex guarding the RNG state. The PSA code must not call init/seed/free concurrently with those, but that's only ever done as part of psa_crypto_init
and free
which needs locking for other reasons anyway.
The external RNG case is its own task, which I think isn't a 3.6.0 objective?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, its not part of this work, thanks for clarifying that.
Note: has conflicts |
Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Unfortunately this requires holding the mutex for the entire psa_crypto_init() function, as calling psa_crypto_free() from another thread should block until init has ended, then run. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Reads and writes of rng_state in psa_crypto_init() and psa_crypto_free() were already covered by mutex. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Writes to this in psa_crypto_init() were again already covered. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
f8de5a1
to
9a73a7f
Compare
Internal only for now, but can be made external with some more work. Break up psa_crypto_init into chunks to prevent deadlocks when initialising RNG, likewise break up mbedtls_crypto_free() to stop having to hold more than one mutex at a time. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Use the global data mutex, as the key slot mutex has to be held in some of the functions where we are testing the flag, and we already hold the global data mutex when calling the functions where the flag is set. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Signed-off-by: Paul Elliott <paul.elliott@arm.com>
9a73a7f
to
0493ab5
Compare
Internal CI failures appear to be spurious (I can't even see what its complaining about, apparently the build failed, but there is no evidence of that) |
The internal CI is still running, but https://jenkins-mbedtls.oss.arm.com/blue/organizations/jenkins/mbed-tls-pr-head/detail/PR-8901-head/8/pipeline/1128 (Windows-2013-Debug-Win32-cmake-retarget) failed due to a build error
I have no idea what it means, but it's clearly not related to this PR, and I doubt that it's reproducible. |
The internals of the rng should be guarded by their own mutex in the |
All nits addressed, only minor changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
0bebb69
library/psa_crypto.c
Outdated
#define PSA_CRYPTO_DEF_SUBSYSTEM_TRANSACTION_INITIALIZED 0x04 | ||
|
||
const uint8_t PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED = | ||
PSA_CRYPTO_DEF_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've now got both #define
and const uint8_t
, whereas we originally just had only the #define
s. IMO it's not worth the extra set of identifiers for the small amount of typing we get, and I'd rather we just rolled back some of a5189c49fbad6e5700e7a0807750a50ff21898b7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, do you mind if I force push?
Catch potential invalid calls to init. Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Signed-off-by: Paul Elliott <paul.elliott@arm.com>
Signed-off-by: Paul Elliott <paul.elliott@arm.com>
0bebb69
to
b24e36d
Compare
Removed commit to change over to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
78064ac
Description
Make the contents of PSA
global_data
thread safe. Resolves #8425. Resolves #7945Notes:
It should now be safe to call psa_crypto_init() on multiple threads, either concurrently or sequentially.
Although some protection has now been done on
mbedtls_psa_crypto_free()
it is still not entirely thread safe, even if no operations are running at the time. This is annoying as I could have used this to improve testing, however we never claimed it would be thread safe.The Macro PSA_DONE() is still definitely not thread safe, due to the checks it does.
In this PR we only attempt to make access to global_data.rng_state and global_data.rng thread safe. We are not attempting to make the random number generator thread safe, as that was (mostly) done elsewhere. I have seen an existing issue with the RNG whereby the mutex gets deleted just prior to another thread using it, so again, this is not something that can be tested alongside shutdown (but this unfortunately means we are not testing access to
global_data.rng.drbg
particularly thoroughly).psa_crypto_init()
had to be split into blocks due to issues with deadlock when the rng startup would end up calling into md.c which would callpsa_can_do_hash()
, which would attempt to lock the same mutex. The only solution to this as far as I could see was to use multiple mutexes (re-entrant mutexes are going to make cross platform support difficult, one time initialisation did not entirely solve the issue), which necessitated the split. I attempted to do the split as per suggestions in Define the PSA subsystem initialization interface in Mbed TLS #6007 - Note I did not yet make this a public interface, so improvements can be made prior to making this public (work which is not really in the scope of this PR)The testing in this PR is slightly tortured, admittedly. Any suggestions for testing improvements would be welcomed, but be aware that we are quite restricted in what we can or can not do (see above)
PR checklist
Please tick as appropriate and edit the reasons (e.g.: "backport: not needed because this is a new feature")
provided, ornot required (part of the multithreading EPIC)done, ornot required (new features), or not required