-
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
Refactor mpi_write_hlp to not be recursive #2214
Conversation
ChangeLog
Outdated
@@ -77,6 +77,10 @@ Bugfix | |||
replacements of standard calloc/free functions through the macros | |||
MBEDTLS_PLATFORM_CALLOC_MACRO and MBEDTLS_PLATFORM_FREE_MACRO. | |||
Reported by ole-de and ddhome2006. Fixes #882, #1642 and #1706. | |||
* Refactor `mpi_write_hlp()` to not be recursive, to fix stack overflows. |
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.
That's a too detailed ChangeLog entry, the first line suffices.
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.
Yeah, I thought about it
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 personally like a bit of information regarding the reasoning of a change, but I would phrase it differently. For example:
Reduce stack usage of `mpi_write_hlp()` by eliminating recursion. Fixes #2190.
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 like that.
library/bignum.c
Outdated
{ | ||
int ret; | ||
mbedtls_mpi_uint r; | ||
size_t length = 0; | ||
char *p_end = *p + buflen - 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.
Consider failing if buflen == 0
to avoid an underflow.
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 considered it, but since the calling function checks the buffer length here, I thought I shouldn't
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.
Yes, for a static function that should be enough. Could you add a comment instead?
library/bignum.c
Outdated
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, X, radix ) ); | ||
MBEDTLS_MPI_CHK( mbedtls_mpi_div_int( X, NULL, X, radix ) ); | ||
/* | ||
* Write the residue in the current position, as an ASCII character. |
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.
Minor: Double white space
library/bignum.c
Outdated
if( r < 10 ) | ||
*p_end-- = (char)( r + 0x30 ); | ||
else | ||
*p_end-- = (char)( r + 0x37 ); |
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 pre-existing, but what is this line supposed to do?
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.
As I wrote in the comment( It took me a while to understand as well):
"Write the residue in the current position, as an ASCII character."
Digits start from 0x30
, and A
start from 0x41. So, 0x37 + A is 0x41
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 need to repeat a comment here. Anyway, what you wrote afterwards clarifies things, thanks.
library/bignum.c
Outdated
*(*p)++ = (char)( r + 0x30 ); | ||
else | ||
*(*p)++ = (char)( r + 0x37 ); | ||
memmove( *p, p_end + 1, length ); |
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.
Minor improvement: Consider setting p_end = p + buflen
in the beginning, and using *--p_end = ...
in the loop. This allows to use p_end
instead of p_end + 1
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 like this approach better
library/bignum.c
Outdated
if( mbedtls_mpi_cmp_int( X, 0 ) != 0 ) | ||
MBEDTLS_MPI_CHK( mpi_write_hlp( X, radix, p ) ); | ||
length++; | ||
} while( mbedtls_mpi_cmp_int( X, 0 ) != 0 && length <= buflen ); |
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.
Blocker: If length > buflen
, we'll leave the loop but nonetheless call memmove()
with length
as the length parameter.
We should fail if length > buflen
.
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.
Same answer as #2214 (comment)
I'll add a comment
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, length
is dynamic.
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.
But it starts from 0, and dependent on buflen
.
buflen
cannot be shorter than n
, as checked in https://github.com/ARMmbed/mbedtls/blob/development/library/bignum.c#L552
n
should be at most length
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 that case, one shouldn't check for length <= buflen
. It is highly unintuitive and error-prone to have a buffer length parameter and nonetheless omit bounds checks because one assumes it's large enough. There is technical justification here because of the memmove()
, but nonetheless it's dangerous.
Please remove the check length <= buflen
from the loop condition and instead check for length < buflen
at the beginning of the loop body. This also removes the need for the precondition buflen > 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.
I thought about not adding it at all, but then again, it's always better not to increase the size.
I'll make 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.
Yes, very true - if we can, we should be keep the code size down, and if it wasn't for the memmove()
I'd be fine with using the fact that we have sufficient space as a precondition. But with the buffer length parameter in place, I think it's misleading to make this assumption.
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 @RonEld, overall the approach looks good to me. I requested some changes, the most relevant ones being a shortening of the ChangeLog entry, and the removal of a potential buffer overflow.
@hanno-arm I addressed your comments |
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 the changes, @RonEld, looks fine apart from the bounds check which should be streamlined as indicated in the comment. This also makes the precondition (and hence the comment) on buflen
redundant.
@hanno-arm I fixed according to your comment |
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's an infinite loop if length
ever gets to buflen
. Suggestion: Check for length >= buflen
in the beginning of the loop body and return MBEDTLS_ERR_MPI_BUFFER_TO_SMALL
in this case.
@hanno-arm I addressed your comment, but honestly I think it is adding dead code, as in this static function, |
library/bignum.c
Outdated
MBEDTLS_MPI_CHK( mbedtls_mpi_div_int( X, NULL, X, radix ) ); | ||
do | ||
{ | ||
if( length < buflen ) |
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.
Minor: It would be more readable to write
if( length >= buflen )
return( MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL );
and then do the rest.
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
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.
Could you fix this, and then we leave the other optimizations for another issue?
@RonEld Thanks. I agree that it should never happen, but apart from the reason for including the check I've already given, is it really that obvious that |
@@ -588,7 +604,7 @@ int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, | |||
if( T.s == -1 ) | |||
T.s = 1; | |||
|
|||
MBEDTLS_MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); |
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.
Let's save some bytes and RAM here instead, by not using T
. Suggestion: Document that mpi_write_hlp()
ignores the sign and just pass X
instead of the local copy T
.
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.
That's actually wrong, because we need a copy in order to successively divide T
by the radix.
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.
Probably, but maybe it's better to define T
in mpi_write_hlp()
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.
I like @RonEld suggestion
@@ -588,7 +604,7 @@ int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, | |||
if( T.s == -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.
Also, if we want to save code, I think we should call mpi_write_hlp()
even for radix 16
and get rid of the radix == 16
branch - it might not be as fast as the hand-coded version, but mbedtls_mpi_write_string()
is not time-critical anyway.
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 could do that, but I have no strong opinions about it, so this is not a blocker for my approval...
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.
Requested two code and RAM saving optimizations in the caller mbedtls_write_mpi()
that are worth doing along the way. If you insist, @RonEld, that's of course a pre-existing issue and need not be addressed in this PR, but I'd be happy if you could spare the time nonetheless.
@hanno-arm I don't mind doing these changes, I just think they should be tracked in a different github issue |
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.
Looks good to me!
@hanno-arm Thanks for the review! |
library/bignum.c
Outdated
*/ | ||
static int mpi_write_hlp( mbedtls_mpi *X, int radix, char **p ) | ||
static int mpi_write_hlp( mbedtls_mpi *X, int radix, char **p, const size_t buflen ) |
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.
Minor: This line is too long.
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 only now noticed that there's an overly long line introduced in the PR. Please break it.
library/bignum.c
Outdated
if( r < 10 ) | ||
*(--p_end) = (char)( r + 0x30 ); | ||
else | ||
*(--p_end) = (char)( r + 0x37 ); |
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 easier to read if we write (char)( 'a' + ( r - 10 ) )
.
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 it will be more readable if we change 10
to 0xA
library/bignum.c
Outdated
* Write the residue in the current position, as an ASCII character. | ||
*/ | ||
if( r < 10 ) | ||
*(--p_end) = (char)( r + 0x30 ); |
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 slightly easier to read if we write (char)( '0' + r )
.
library/bignum.c
Outdated
else | ||
*(*p)++ = (char)( r + 0x37 ); | ||
length++; | ||
} while( mbedtls_mpi_cmp_int( X, 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.
Minor: I think usually we are explicit about the != 0
. Could you add it?
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.
yes, it's a typo from my previous modification
@Hanno I modified according to your comments |
@andresag01 If you haven't started reviewing yet, I will squash the code commits tomorrow, to a single commit, and update the ChangeLog as you requested |
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 changes look good to me, just made a few minor suggestions.
@@ -588,7 +604,7 @@ int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, | |||
if( T.s == -1 ) | |||
T.s = 1; | |||
|
|||
MBEDTLS_MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); |
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 like @RonEld suggestion
library/bignum.c
Outdated
{ | ||
int ret; | ||
mbedtls_mpi_uint r; | ||
size_t length = 0; | ||
char *p_end = *p + buflen; | ||
|
||
if( radix < 2 || radix > 16 ) | ||
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); |
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 code is a bit pointless. This function is only a helper called within this compilation module and the caller (i.e. mbedtls_mpi_write_string()) already has the same check, so I think this is effectively dead 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 guess the original intent of this check was if this helper function would be called from a different function in the future, to check this function's preconditions
@@ -588,7 +604,7 @@ int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, | |||
if( T.s == -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.
We could do that, but I have no strong opinions about it, so this is not a blocker for my approval...
Sorry, I had already started reviewing by the time you posted the message... |
@andresag01 I have addressed your comments. |
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
Refactor `mpi_write_hlp()` to not be recursive, to fix stack overflows. Iterate over the `mbedtls_mpi` division of the radix requested, until it is zero. Each iteration, put the residue in the next LSB of the output buffer. Fixes Mbed-TLS#2190
Update the ChangeLog with the fix.
@andresag01 @hanno-arm I have squashed the PR into two commits. A refernce branch for comparison before the commit is in https://github.com/RonEld/mbedtls/tree/2627_reference |
A backport 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.
Looks good to me! Sorry for the long delay.
Ping @Patater for gatekeeping. |
CI is failing on DTLS fragmentations issues:
The issue appears to be due to timeouts, therefore re-running the CI. |
Description
Refactor
mpi_write_hlp()
to not be recursive, to fix stack overflows.Iterate over the
mbedtls_mpi
division of the radix requested,until it is zero. Each iteration, put the residue in the next LSB
of the output buffer. Fixes #2190
Status
READY
Requires Backporting
Yes
Which branch?
mbedtls-2.1
mbedtls-2.7
Migrations
NO
Additional comments
Tested on Linux machine, K64F, NRF52840_DK
Todos
Steps to test or reproduce
Run the
test_suite_mpi
on embedded platforms.