-
Notifications
You must be signed in to change notification settings - Fork 52
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
Refactor benchmark to mitigate memory problems #194
Refactor benchmark to mitigate memory problems #194
Conversation
@andresag01 Before I do a full review, I have a question and a couple of comments:
Since these last two PRs already exist, I think it would be much simpler to review if you revert your changes related to the todo struct and memory_buffer_alloc |
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.
I have added some comments to the code. I'd like to finish the review after @andresag01 has addressed @RonEld 's comment, so that I review the right changes.
benchmark/main.cpp
Outdated
|
||
/* Clear some memory that was used to prepare the context */ | ||
#if defined(MBEDTLS_ECP_C) | ||
void ecp_clear_precomputed(mbedtls_ecp_group *grp) { |
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.
I don't know, which coding style is supposed to be used in the examples repository, but both Mbed TLS and Mbed OS expect function braces to be in a separate line
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.
Correct, I am trying to use the Mbed OS style, but missed a lot of cases. I will just run astyle to format everything automatically
else \ | ||
{ \ | ||
mbedtls_printf( "%6lu ms/" TYPE, ms ); \ | ||
MEMORY_MEASURE_PRINT( sizeof( TYPE ) + 1 ); \ |
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.
It is nuclear to me, why the memory usage printing was removed. Is it due to the changed allocation schemes? Can it be stated that the values acquired from mbedtls_memory_buffer_alloc_max_get( ... )
aren't useful now?
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.
MEMORY_MEASURE_PRINT is dependent on memory_buffer_alloc. However, the way it was used here causes memory issues, so I am removing it. There is also a ticket to do that...
benchmark/main.cpp
Outdated
mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp, myrand, NULL ) != 0 ) | ||
{ | ||
return( 1 ); | ||
ret =mbedtls_ecp_copy( &ecdh.Qp, &ecdh.Q ); |
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.
A space is missing here
unsigned long i; \ | ||
Timeout t; \ | ||
\ | ||
mbedtls_printf(HEADER_FORMAT, TITLE); \ |
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.
The code formatting is not consistent with the previous implementation.
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.
It is not because I am trying to use the Mbed OS style as this is an Mbed OS example
I think that #116 was a good step, but looking at the assembly source for GCC the stack allocation was still very high. Also, I investigated further and some primitive overflow the stack even if they are the only benchmark in the function. So I decided to split everything and tag functions with noinline in the hope that this takes care of the problem once and for all. Also, I moved one or two of the most memory consuming contexts to dynamic allocation as it would not work otherwise.
My original plan was to just fix the worst memory issues, but then I ended up making so many changes that I just decided to include fixes for those two issues as well here. In fact, this PR is essentially a rewrite of the application rather than a "fix". I included changes for issues such as todo and memory_buffer_alloc because it was simple to do it here and would be more time consuming if we had to resolve conflicts twice across three PRs. |
Yes, Mbed-TLS/mbedtls#1937 should resolve the overflow in the SHA functions |
@RonEld: Actually I did not have an issue with the hash primitives. However, something like Blowfish is more problematic. |
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.
General comments:
- In main(), we shouldn't exit on error. I know this is the current behaviour, but since we are rewriting, let's just go to next test
- In main(), There is a combination of
BENCHMARK_FUNC_CALL
and calling helper functions. I think there should be helper functions for all. - I think a helper function for every algorithm is a little too much, but not a blocker
- We should surround the whole helper functions with the relevant
#if defined
- Please return the implementation of
PRINT_ERROR
and call this MACRO upon failure, for better error code readability - I am currently workiong on a feature not available PR (in the mbedtls repository), but it would be nice if the existing FEATURE NOT SUPPORTED error codes would be already integrated here
benchmark/main.cpp
Outdated
for (i = 1, alarmed = 0, t.attach(alarm, 1.0); !alarmed; i++) \ | ||
{ \ | ||
if ((ret = (CODE)) != 0) { \ | ||
mbedtls_printf("%s returned -0x%04X\n", #CODE, -ret); \ |
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.
It should be like the previous implementation of PRINT_ERROR
, in case MBEDTLS_ERROR_C
is defined, call mbedtls_strerror
for better readability of the error code
#endif | ||
MBED_NOINLINE static int benchmark_arc4() { | ||
int ret = 0; | ||
#if defined(MBEDTLS_ARC4_C) |
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.
why not surround the whole function with #if defined(MBEDTLS_ARC4_C)
?
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.
Because then I would have to duplicate the preprocessor check here and in the main() function.
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.
I know, but it would still result in a smaller code, and not calling empty functions. It depends on the compiler, if it can optimize these functions out
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.
I think code size here is not really a problem. When the feature is not defined as a macro then the compiler (GCC) will generate the following code for an empty function:
0000181c <benchmark_havege()>:
181c: 2000 movs r0, #0
181e: 4770 bx lr
Then there will be the following call in the main:
36aa: f7fe f8b7 bl 181c <benchmark_havege()>
36ae: b9b8 cbnz r0, 36e0 <main+0x37c>
Also, I checked and when multiple functions are empty, then the compiler will reuse one of the empty functions instead of emitting multiple ones to reduce code size. In any case, the overhead is 2.5 words (10 bytes) which take about 8-10 clock cycles to execute in a Cortex-M0...
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.
Have you tried all toolchains? ARM, GCC_ARM and IAR?
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.
I tried one version of GCC_ARM only. However, I do not expect the code to be too different with the others. After all, if a feature is not enabled, its corresponding function will be empty and there is only one assignment and a return in there. Then in main there is only a call to that function and a check which map to the instructions above (or something similar to that).
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.
I agree there shouldn't be much difference, personally, I prefer not to have a call to empty functions, but that's a matter of style, it's not a blocker for me
benchmark/main.cpp
Outdated
if( todo->md5 ) | ||
TIME_AND_TSC( "MD5", mbedtls_md5( buf, BUFSIZE, tmp ) ); | ||
#endif | ||
MBED_NOINLINE static int benchmark_des3() { |
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.
why not surround the whole function with #if defined(MBEDTLS_DES_C) && defined(MBEDTLS_CIPHER_MODE_CBC)
?
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.
Please refer to my other comment
benchmark/main.cpp
Outdated
// Please refer to https://github.com/ARMmbed/mbedtls/issues/1200 for more information. | ||
(void)ctx; | ||
memset( tmp, 0xBB, sizeof( tmp ) ); | ||
MBED_NOINLINE static int benchmark_des() { |
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.
why not surround the whole function with #if defined(MBEDTLS_DES_C) && defined(MBEDTLS_CIPHER_MODE_CBC)
?
benchmark/main.cpp
Outdated
|
||
memset( buf, 0, sizeof( buf ) ); | ||
memset( tmp, 0, sizeof( tmp ) ); | ||
MBED_NOINLINE static int benchmark_des3_cmac() { |
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.
why not surround the whole function with #if defined(MBEDTLS_DES_C) && defined(MBEDTLS_CIPHER_MODE_CBC) && defined(MBEDTLS_CMAC_C)
?
benchmark/main.cpp
Outdated
mbedtls_gcm_free( &gcm ); | ||
ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, tmp, keysize); | ||
if (ret != 0) { | ||
mbedtls_printf("mbedtls_gcm_setkey() returned -0x%04X\n", -ret); |
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.
Please skip in case of feature not supported
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.
I think AES has the feature unavailable macro error code, but as far as I know GCM does not have it yet. I think the PR that adds it is: Mbed-TLS/mbedtls#1716. Is this correct? If so, then I will not add the check because the symbol MBEDTLS_ERR_GCM_FEATURE_UNAVAILABLE does not exist yet. Or is it possible that this function returns MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE?
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.
The problem here is, GCM can return either MBEDTLS_AES_FEATURE_UNAVAILABLE
, if no alternative GCM exists, or the undefined yet MBEDTLS_GCM_FEATURE_UNAVAILABLE
, if there is an alternative gcm.
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.
Thanks for clarifying. I have added the checks for the AES feature unavailable only.
|
||
MBED_NOINLINE static int benchmark_aes_ccm() { | ||
int ret = 0; | ||
#if defined(MBEDTLS_AES_C) && defined(MBEDTLS_CCM_C) |
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.
why not surround the whole function with #if defined(MBEDTLS_AES_C) && defined(MBEDTLS_CCM_C)
?
|
||
MBED_NOINLINE static int benchmark_aes_cmac() { | ||
int ret = 0; | ||
#if defined(MBEDTLS_AES_C) && defined(MBEDTLS_CMAC_C) |
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.
why not surround the whole function with #if defined(MBEDTLS_AES_C) && defined(MBEDTLS_CMAC_C)
?
benchmark/main.cpp
Outdated
return ret; | ||
} | ||
|
||
MBED_NOINLINE static int benchmark_camellia() { |
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.
why not surround the whole function with #if defined(MBEDTLS_CAMELLIA_C) && defined(MBEDTLS_CIPHER_MODE_CBC)
?
benchmark/main.cpp
Outdated
} | ||
|
||
TIME_PUBLIC( "ECDHE-Curve25519" |
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.
I know this is the original behaviour, but if we are alreadey doing a rewrite, please don't exit on failure, we should continue to next test.
@andresag01 The FEATURE_NOT_SUPPORTED support is nice to have, but I will work on it once Mbed-TLS/mbedtls#1716 is merged, so this isn't a blocker for this PR |
@RonEld: I believe I have addressed all (or at least most of) your comments. |
I have tested the changes in K64F with GCC_ARM and it seems to work fine. However, when I try it with ARMCC I get the following errors:
It now seems that we are running out of heap, but I am not very sure why it succeeds in GCC_ARM if the size of data + bss is larger than with ARMCC, so I would expect the heap space to be smaller. I will take a look at that as soon as I get the change. |
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.
I haven't specifically mentioned CCM and CMAC as possible to get MBEDTLS_AES_FEATURE_NOT_SUPPORTED
.
Sorry for not mentioning it specifically before
benchmark/main.cpp
Outdated
memset(tmp, 0, sizeof(tmp)); | ||
|
||
ret = mbedtls_ccm_setkey(&ccm, MBEDTLS_CIPHER_ID_AES, tmp, keysize); | ||
if (ret != 0) { |
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.
CCM could also return MBEDTLS_AES_FEATURE_NOT_SUPPORTED
in case no alternative CCM
goto exit; | ||
} | ||
|
||
BENCHMARK_FUNC_CALL(title, |
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.
CMAC could also return MBEDTLS_AES_FEATURE_NOT_SUPPORTED
in case no alternative CMAC.
This is problematic because of the MACRO though
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.
Is this still a problem?
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, the macro does make it a bit awkward to add the extra checks. I did add this line of code to check for MBEDTLS_AES_FEATURE_NOT_SUPPORTED on calls like this.
I agree that the code is not ideal, but my main motivation for this refactor was to patch up the reliability issues. So I tried to minimize the development time by no changing all these macros. Perhaps we should consider replacing them when it is needed to check more feature not supported errors.
Note this PR also fixes an issue of failure in hmac_drbg , when |
@RonEld: Please note that this fix is already included in this PR |
The failures currently reported by the Ci are unrelated to this change. There seems to be an issue with the CI |
@RonEld: I added the extra AES unavailable checks to CCM, CMAC and CTR_DRBG |
|
||
static unsigned char buf[BUFSIZE]; | ||
static unsigned char tmp[200]; | ||
static char title[TITLE_LEN]; |
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 is a lot of static memory use. Why not use heap or stack instead?
Do we need tmp
and buf
at the same time, or could the users of what's currently buf
use tmp
instead?
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.
tmp
and buf
are used at the same time. buf
is the buffer of length 1024 that is the input \ output of the current operation.
tmp
is an additional buffer used for the current operation, such as key, iv, signature etc..
I think, however, that the size of tmp
should be decreased significantly. I am not sure htat more than 32 bytes is really used
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 is a lot of static memory use. Why not use heap or stack instead?
I would like to avoid putting these into the stack because that would increase the likely of stack overflows, which is one of the motivations for this PR. These are currently as globals because there are shared by all functions, and in any case they were allocated statically in the original benchmark, so I just left it like that. I could move it to the heap if you feel strongly about it, I don't mind either way...
Do we need tmp and buf at the same time, or could the users of what's currently buf use tmp instead?
As @RonEld explained, we do need both buf and tmp. I will try to reduce the buffer sizes though.
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.
Have you tried reducing the tmp
buffer size? From my brief look, it is used for small buffers, such as keys and ivs.
I think that 150 bytes can also be reduced. Couldn't 64 bytes be enough?
benchmark/main.cpp
Outdated
use_len = len; | ||
if( use_len > sizeof(int) ) | ||
if(use_len > sizeof(int)) { |
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.
Add a space after if
.
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.
I think this review comment is based on an old commit. Please note that later on there is a commit that fixes these style problems by running astyle with the Mbed OS configuration file.
benchmark/main.cpp
Outdated
#else | ||
#define ecp_clear_precomputed( g ) | ||
#endif | ||
MBED_NOINLINE static int benchmark_arc4() { |
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.
Why "NOINLINE"? If you allow the compiler to inline this, and it ends up as an empty function (by configuration choices), there shouldn't be any effect (no stub function made that does nothing).
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.
The NOINLINE is because without it the compiler will inline functions into main() with high optimization levels and dramatically increase the stack size causing the application to fail...
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.
In a later commit I changed location of the preprocessor checks to surround the entire function and the call in main, so there will not be an empty function added when the feature is disabled.
Note that @RonEld had a similar comment, so I changed this. As I discussed with him, I prefer the other approach because it decreases the number of preprocessor checks with only negligible effect in code size and runtime. Please find below my answer to @RonEld's original comment:
I think code size here is not really a problem. When the feature is not defined as a macro then the compiler (GCC) will generate the following code for an empty function:
0000181c <benchmark_havege()>:
181c: 2000 movs r0, #0
181e: 4770 bx lr
Then there will be the following call in the main:
36aa: f7fe f8b7 bl 181c <benchmark_havege()>
36ae: b9b8 cbnz r0, 36e0 <main+0x37c>
Also, I checked and when multiple functions are empty, then the compiler will reuse one of the empty functions instead of emitting multiple ones to reduce code size. In any case, the overhead is 2.5 words (10 bytes) which take about 8-10 clock cycles to execute in a Cortex-M0...
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.
Too bad the compiler doesn't realize it doesn't have enough stack available to inline functions (or not). OK.
benchmark/main.cpp
Outdated
mbedtls_ecdh_gen_public( &ecdh.grp, &ecdh.d, &ecdh.Qp, myrand, NULL ) != 0 ) | ||
{ | ||
return( 1 ); | ||
ret =mbedtls_ecp_copy( &ecdh.Qp, &ecdh.Q ); |
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 isn't Mbed OS style...
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.
I think this review comment is based on an old commit. Please note that later on there is a commit that fixes these style problems by running astyle with the Mbed OS configuration file.
#define PRINT_ERROR \ | ||
mbedtls_strerror( ret, ( char * )tmp, sizeof( tmp ) ); \ | ||
mbedtls_printf( "FAILED: %s\r\n", tmp ); | ||
#define BENCHMARK_FUNC_CALL(TITLE, CODE) \ |
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.
For additional code savings, and easier maintainability (no giant macros), this could be made into a static function that accepts a function pointer and a string or two. This would mean wrapping a function call you want to benchmark with another function that doesn't take any parameters.
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.
I agree, and perhaps adding a list of the optional valid return codes that the function can return, such as the different FEATURE_UNAVAILABLE errors ( TBD for the implementation of the return codes)
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.
@Patater, @RonEld: I understand your concerns, but there were a lot of other issues to fix with this application. While code size is important, the main problems I had to look into was the RAM problems. To save time I decided to not do something like what you are suggesting and preserve the main structure of each test case, but inside separate functions.
How strongly about this? Should we just leave it like this for now?
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.
Did fixing the RAM issues increase the code footprint? If not, I think we can pursue additional code savings later.
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 the code in the latest version of this PR is not really comparable with the original version of the application because a lot more code has been added to improve reliability (for example, all the error checks). However, I am fairly certain that if we removed this, the code would have increased because there are a lot more function prologues and epilogues, but I expect the amount of code regarding these macros to be relatively similar.
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.
I have added a couple of observation on alternative CCM and GCM. No action required.
However, I believe that returning the MBEDTLS_AES_FEATURE_UNAVAILABLE
on 256 bit key will cause the benchmark to stop operation, instead of skipping the test.
Also, ctr_drbg uses 256 bit keys (until Mbed-TLS/mbedtls#1902 is merged) so AES Feature unavailable for ctr_drbg code is not a valid error and should not have special treatment, IMHO.
Another suggestion \ question was about reusing the title
buffer
@@ -156,7 +156,11 @@ do { \ | |||
\ | |||
for (i = 1, alarmed = 0, t.attach(alarm, 1.0); !alarmed; i++) \ | |||
{ \ | |||
if ((ret = (CODE)) != 0) { \ | |||
ret = CODE; \ | |||
if (ret == MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE) { \ |
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.
Have you tested this with changing the AES code to return MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE
on 256 bit key? I think this will cause the benchmark to stop instead of skipping the test.
In addition, in future Unavailable errors, this will probably not work when we don't loop and overwrite the ret in next call, but this is not part of this PR
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.
Have you tested this with changing the AES code to return MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE on 256 bit key? I think this will cause the benchmark to stop instead of skipping the test.
I did test it and it seems to work.
In addition, in future Unavailable errors, this will probably not work when we don't loop and overwrite the ret in next call, but this is not part of this PR
Not sure what you mean. Please bear in mind that this piece of code is inside a while(0) loop. The break statement exits that while(0) (which is in the macro and will always be there. I dont think it really matters whether the whole macro itself is used within a loop or not.
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.
what I mean is, for AES256, for example:
Let's say that AES256_CBC will return the MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE
, so the set key will succeed, but the mbedtls_aes_crypt_cbc
will return the error code. ret
is now the error MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE
, and we finish the loop in line 435 causing the function benchmark_aes_cbc()
to return an error code, changing the exit_code
to MBEDTLS_EXIT_FAILURE
. We will not exit the test, but I wouldn't consider this a failure.
As for other modules, which will have a future NOT SUPPORTED error, it's basically the same, the specific benchmark test function will return an error code, even if we don't really consider a FEATURE UNAVAILABLE error a real error code
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, I see what you mean. I will fix that.
@@ -519,6 +527,8 @@ MBED_NOINLINE static int benchmark_aes_gcm() | |||
|
|||
ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, tmp, keysize); | |||
if (ret == MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE) { | |||
/* Do not consider this as a failure */ |
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.
Note: Obviously this will work only for AES feature unavailable, and not in case an alternative gcm implementation returns the uncreated yet MBEDTLS_GCM_FEATURE_UNAVAILABLE
This is not a review comment, but an notice for future implementation
@@ -560,7 +570,11 @@ MBED_NOINLINE static int benchmark_aes_ccm() | |||
memset(tmp, 0, sizeof(tmp)); | |||
|
|||
ret = mbedtls_ccm_setkey(&ccm, MBEDTLS_CIPHER_ID_AES, tmp, keysize); | |||
if (ret != 0) { | |||
if (ret == MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE) { |
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.
Note: Obviously this will work only for AES feature unavailable, and not in case an alternative ccm implementation returns the uncreated yet MBEDTLS_CCM_FEATURE_UNAVAILABLE
This is not a review comment, but an notice for future implementation
@@ -747,6 +747,7 @@ MBED_NOINLINE static int benchmark_ctr_drbg() | |||
{ | |||
int ret = 0; | |||
const char *nopr_title = "CTR_DRBG (NOPR)"; | |||
const char *pr_title = "CTR_DRBG (PR)"; |
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.
why not use the existing title
buffer, and just use the snprintf to write to it the relevant title?
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.
I am not sure what is the point of using title and snprintf(). There is nothing to format here, these two variables are just a short hand to not have to write the string twice in the function body.
benchmark/main.cpp
Outdated
@@ -755,6 +756,7 @@ MBED_NOINLINE static int benchmark_ctr_drbg() | |||
if (ret == MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE) { |
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.
MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE is not a recoverable error, for ctr_drbg, so I don't think it requires special treatment for this error
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.
I am not sure what you mean. The special treatment is only so that the console printout shows up nicely in the terminal. Perhaps there is something I am missing?...
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.
I think that in all the other MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE
we should print and skip the specific test, as it's a reasonable error to return.
For ctr_drbg, it means that ctr_drbg will fail, as it uses AES256 only, unless configured to 128 bit, as introduced in Mbed-TLS/mbedtls#1902
So, this error is not a valid error for ctr_drbg, IMHO.
If it's only for the printout, well, the PRINT_ERROR
will handle this, no?
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.
I still do not understand what the change request is here. We are already printing out the test and the string "Feature Unavailable" when AES is not available. This is equivalent to skip because we are not letting the benchmark application failed for that. I do not see the point of printing the actual error string corresponding to MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE and this would be inconsistent with the checks in other parts of this application
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.
Because, when we skip a test, it's a valid error, and the return code of the benchmark should be success.
On ctr_drbg, on the other hand, when MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE
is returned, it is a real error, and we should fail the test, and developers should be aware that they will need to modify thei ctr_drbg to use 128 bit key size(after Mbed-TLS/mbedtls#1902 is merged)
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, I see what you mean, but I do not agree with it. If ctr_drbg does not run because it has no AES that is not a bug or a real failure according to what we have done in the benchmark application. Its just that the primitive is unavailable under the current configuration. Isnt this comparable to saying that AES-256 is not available so its an actual error because the user should change to something like AES-128?
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, because for ctr_drbg, the usage of AES256 is embedded within the algorithm and not configurable, and not under the user's discretion.
After it will be configurable to 128, I still think that it should be an error, and not just skipped, because it's an algorithm using AES, and not just a primitive AES, but I won't insist on it, after it will be configurable
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.
It's not configurable to use AES-128, yet. After it is configurable, we can make this an error. Is any further action needed 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.
I think that we should leave it as it is until the new feature is merged. @RonEld, would you still like to pursue the change? I am happy to make the change if you think its necessary.
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.
Correct, this is not configurable. This is why It is a failure error, that should cause the CI to fail, and we should not have special treatment for this specific error, in case of ctr drbg
d0858c1
to
c1a1abe
Compare
Rebased the changes to sync with latest merged to master. |
Review comments have had responses.
@RonEld - Can you please re-review? We need to get this merged as the fixes will restore us to working on some broken targets. |
@sbutcher-arm: This PR already has 2 approvals, so removing the 'needs review' tag. |
retest |
So this PR seems to fix the tests previously failing on the following targets:
The top two were memory problems with ECDSA, and the final one However, we still have failing targets:
The KW24D just doesn't have enough memory for the example, so we'll need to work out a way of excluding that test. The others are network problems, and I suspect So this PR does fix what it said it did and is good to merge, but unfortunately reveals that some failing tests were hiding other failing tests. So that means it's good to merge - but we have more bugs to fix on some targets. I'll raise those separately. |
retest |
Retesting to see if the issue with |
This PR entirely refactors the benchmark example to fix the memory usage issues in Mbed OS (particularly stack overflows). The idea is to run the benchmark for each module in a completely different (noinline) function. Also, some objects are moved to either to the global section or are allocated dynamically.
This PR also includes changes to:
Also addresses: #52, #29, #15 and #184
NOTE: This PR is currently under development. I have checked that it works in K64F, but I am waiting for the CI to complete to check whether any other memory issues are detected in the other targets. Regardless, the main structure of the change is in place and can be reviewed.