Skip to content
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

Merged
merged 2 commits into from Jan 31, 2019
Merged

Conversation

RonEld
Copy link
Contributor

@RonEld RonEld commented Nov 20, 2018

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

  • Tests
  • Documentation
  • Changelog updated
  • Backported

Steps to test or reproduce

Run the test_suite_mpi on embedded platforms.

@RonEld RonEld added bug mbed TLS team needs-review Every commit must be reviewed by at least two team members, needs-backports Backports are missing or are pending review and approval. component-crypto Crypto primitives and low-level interfaces labels Nov 20, 2018
@RonEld RonEld changed the title 2627 Refactor mpi_write_hlp to not be recursive Nov 20, 2018
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.
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.
Copy link
Contributor

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 );
Copy link
Contributor

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?

Copy link
Contributor Author

@RonEld RonEld Nov 20, 2018

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

Copy link
Contributor

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 );
Copy link
Contributor

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.

Copy link
Contributor Author

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 );
Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, length is dynamic.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor

@hanno-becker hanno-becker left a 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.

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@hanno-arm I addressed your comments

Copy link
Contributor

@hanno-becker hanno-becker left a 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.

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@hanno-arm I fixed according to your comment

Copy link
Contributor

@hanno-becker hanno-becker left a 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.

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@hanno-arm I addressed your comment, but honestly I think it is adding dead code, as in this static function, length will always be smaller than buflen (or equal at the last iteration)

library/bignum.c Outdated
MBEDTLS_MPI_CHK( mbedtls_mpi_div_int( X, NULL, X, radix ) );
do
{
if( length < buflen )
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor

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?

@hanno-becker
Copy link
Contributor

hanno-becker commented Nov 20, 2018

@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 length will never exceed buflen? The caller checks buflen against a shifted version of mbedtls_mpi_get_bitlen(), while the evolution of length in this function depends on iterated calls to mbedtls_mpi_div_int() and mbedtls_mpi_cmp_int(). We rely on all of these functions to work properly if we want to reason that we don't risk an overflow. In my opinion, that's too much risk compared to adding the check. I fully agree that we should reduce code-size whenever we can, but it must not come at the cost of too complex reasoning about the safety of the resulting code.

@@ -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 ) );
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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 )
Copy link
Contributor

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.

Copy link
Contributor

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...

Copy link
Contributor

@hanno-becker hanno-becker left a 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.

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@hanno-arm I don't mind doing these changes, I just think they should be tracked in a different github issue

Copy link
Contributor

@hanno-becker hanno-becker left a 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!

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@hanno-arm Thanks for the review!
I created #2216 to track the other two optimization suggestions

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 )
Copy link
Contributor

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.

Copy link
Contributor

@hanno-becker hanno-becker left a 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 );
Copy link
Contributor

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 ) ).

Copy link
Contributor Author

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 );
Copy link
Contributor

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 ) );
Copy link
Contributor

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?

Copy link
Contributor Author

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

@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@Hanno I modified according to your comments

hanno-becker
hanno-becker previously approved these changes Nov 20, 2018
@andresag01 andresag01 self-requested a review November 20, 2018 17:05
@RonEld
Copy link
Contributor Author

RonEld commented Nov 20, 2018

@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

Copy link
Contributor

@andresag01 andresag01 left a 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 ) );
Copy link
Contributor

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 );
Copy link
Contributor

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.

Copy link
Contributor Author

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 )
Copy link
Contributor

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...

@andresag01
Copy link
Contributor

@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

Sorry, I had already started reviewing by the time you posted the message...

@RonEld
Copy link
Contributor Author

RonEld commented Nov 21, 2018

@andresag01 I have addressed your comments.
Note that #2214 (comment) will be addressed as part of hanno's work on #2216

hanno-becker
hanno-becker previously approved these changes Nov 21, 2018
Copy link
Contributor

@hanno-becker hanno-becker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

andresag01
andresag01 previously approved these changes Nov 26, 2018
Ron Eldor added 2 commits November 27, 2018 10:34
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.
@RonEld
Copy link
Contributor Author

RonEld commented Nov 27, 2018

@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
Please review

@RonEld
Copy link
Contributor Author

RonEld commented Nov 27, 2018

A backport to mbedtls-2.7 is in #2234

Copy link
Contributor

@hanno-becker hanno-becker left a 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.

@hanno-becker hanno-becker removed needs-review Every commit must be reviewed by at least two team members, needs-backports Backports are missing or are pending review and approval. labels Jan 14, 2019
@hanno-becker
Copy link
Contributor

Ping @Patater for gatekeeping.

@simonbutcher
Copy link
Contributor

CI is failing on DTLS fragmentations issues:

DTLS proxy: 3d, min handshake, client-initiated renego
DTLS proxy: 3d, FS, ticket
DTLS proxy: 3d, min handshake, server-initiated renego, nbio

The issue appears to be due to timeouts, therefore re-running the CI.

@simonbutcher simonbutcher added the needs-ci Needs to pass CI tests label Jan 27, 2019
@Patater Patater added the approved Design and code approved - may be waiting for CI or backports label Jan 30, 2019
@Patater Patater merged commit 8a6917d into Mbed-TLS:development Jan 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Design and code approved - may be waiting for CI or backports bug component-crypto Crypto primitives and low-level interfaces needs-ci Needs to pass CI tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mpi_write_hlp is not efficient and can cause stack overflow
5 participants