-
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
Fix corner case uses of memory_buffer_alloc.c #778
Fix corner case uses of memory_buffer_alloc.c #778
Conversation
library/memory_buffer_alloc.c
Outdated
@@ -247,6 +249,8 @@ static void *buffer_alloc_calloc( size_t n, size_t size ) | |||
|
|||
if( n != 0 && len / n != size ) |
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 #639 it was proposed that if the size of the requested allocation is 0
then the allocator should return NULL
. The C99 standard states the following:
If the size of the space requested is zero, the behavior is implementation-
defined: either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.
The problem is that some parts of the library assume that allocating 0 will return a non-NULL value on success. Making this change causes test failures, so the behaviour is not changed as it is compliant with the C standard. Nevertheless, I went through the code, and it seems that this allocator actually tries to allocate a 0 length buffer, which is completely unnecessary. I think this behaviour should be modified. Any thoughts @sbutcher-arm and @yanesca?
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.
@andresag01 In my opinion, we should treat 0 size allocation as NULL, and fix the code where it is treated different
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.
@andresag01 - I read the bug differently. That's because calloc()
can return NULL
or non-NULL
values, we need to be able to handle both because we can't always depend on the underlying implementation to do what we expect. If, as you say, "some parts of the library assume that allocating 0 will return a non-NULL value", surely we need to fix this to accommodate both types of behaviour?
@andresag01 - I think you'd best raise a new bug to address other parts of the library.
I also think we need to treat a size 0 allocation as NULL.
library/memory_buffer_alloc.c
Outdated
@@ -247,6 +249,8 @@ static void *buffer_alloc_calloc( size_t n, size_t size ) | |||
|
|||
if( n != 0 && len / n != size ) |
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.
@andresag01 - I read the bug differently. That's because calloc()
can return NULL
or non-NULL
values, we need to be able to handle both because we can't always depend on the underlying implementation to do what we expect. If, as you say, "some parts of the library assume that allocating 0 will return a non-NULL value", surely we need to fix this to accommodate both types of behaviour?
@andresag01 - I think you'd best raise a new bug to address other parts of the library.
I also think we need to treat a size 0 allocation as NULL.
ebd3a96
to
ec683cd
Compare
@RonEld, @sbutcher-arm: I have modified the allocator to return NULL when allocating 0 bytes. And fixed two places in the library (pem.c and x509_crl.c) that worked under the assumption that allocating 0 returns a non-NULL value. |
@RonEld: Could you take a look a this again? |
library/memory_buffer_alloc.c
Outdated
|
||
if( verify_header( heap.first ) != 0 ) | ||
if( heap.first == NULL || verify_header( heap.first ) != 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.
although same as previous implementation, but I'll reuse prv
variable, instead of using heap.first
library/pem.c
Outdated
@@ -410,7 +410,7 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer, | |||
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL ); | |||
} | |||
|
|||
if( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) | |||
if( use_len != 0 && ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) |
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.
to make it more readable, please surround the statement ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL
with parentheses
library/x509_crl.c
Outdated
@@ -257,7 +257,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, | |||
{ | |||
int ret; | |||
size_t len; | |||
unsigned char *p, *end; | |||
unsigned char *p = NULL, *end; |
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 would also initialize end
library/x509_crl.c
Outdated
@@ -294,7 +294,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, | |||
/* | |||
* Copy raw DER-encoded CRL | |||
*/ | |||
if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) | |||
if( buflen != 0 && ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) |
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.
to make it more readable, please surround the statement ( p = mbedtls_calloc( 1, buflen ) ) == NULL
with parentheses
@@ -581,20 +584,24 @@ void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len ) | |||
mbedtls_platform_set_calloc_free( buffer_alloc_calloc, buffer_alloc_free ); | |||
#endif | |||
|
|||
if( (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE ) | |||
if( len < sizeof( memory_header ) + MBEDTLS_MEMORY_ALIGN_MULTIPLE ) |
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 the correct behavior? This function doesn't have a return code, but eventually, this is an error case. A user calling this function doesn't know that the input is incorrect
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 is the correct behaviour. The buffer provided to memory manage must be at least as high as the memory header. If this is not the case, the internal buffer_alloc_ctx
will be zeroized before returning and further calls to, say mbedtls_calloc()
will signal allocation failed
by returning a NULL
pointer.
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 thanks, I think you mean buffer_alloc_calloc()
though
@RonEld, @sbutcher-arm: I have reworked the PR to address comments. Please review. |
CI failed only due to timing tests (known issue), ok to merge. |
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 rejecting this PR for the same reason as #1009. In the 2.1 and 1.3 backports, ASan highlighted that we call memcpy(NULL, …, 0)
which is UB. For some reason ASan doesn't complain here, but the problem is present as well.
library/x509_crl.c
Outdated
@@ -294,7 +294,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, | |||
/* | |||
* Copy raw DER-encoded CRL | |||
*/ | |||
if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) | |||
if( buflen != 0 && ( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) ) |
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 check for buflen == 0
should be made earlier. See https://github.com/ARMmbed/mbedtls/pull/1009/files#r153007520
@gilles-peskine-arm: I have fixed the 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.
Thanks for updating. The behavior is ok but I would prefer a different way to write the code for clarity.
library/x509_crl.c
Outdated
if( buflen != 0 && ( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) ) | ||
if( buflen == 0 ) | ||
return( MBEDTLS_ERR_X509_INVALID_FORMAT ); | ||
else if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL ) |
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 doesn't change the behavior, but I'd prefer not to put an else
here, because it's misleading for readers: if buflen==0
, the code below the else
is no executed either. And since the result of the allocation is stored in a variable, there's no call for putting the assignment inside the if
, so I'd prefer
if( buflen == 0 )
return( MBEDTLS_ERR_X509_INVALID_FORMAT );
p = mbedtls_calloc( 1, buflen );
if( ( p == NULL )
return( MBEDTLS_ERR_X509_ALLOC_FAILED );
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 think this is a really minor thing about coding style. I prefer the way I originally wrote it, but I changed the code to suit your preferred style in the latest commit.
CI failed only due to timing tests (known issue), ok to merge. |
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.
After merging this PR with development, there is a test failure.
scripts/config.pl full
make clean; make
cd tests
./test_suite_pkparse -v
One test case fails:
Key ASN1 (Incorrect first tag) .................................... FAILED
mbedtls_pk_parse_key( &pk, buf, data_len, NULL, 0 ) == ( result )
at line 132, suites/test_suite_pkparse.function
To make this PR mergeable, please merge development
into it and fix this test failure.
The corner cases fixed include: * Allocating a buffer of size 0. With this change, the allocator now returns a NULL pointer in this case. Note that changes in pem.c and x509_crl.c were required to fix tests that did not work under this assumption. * Initialising the allocator with less memory than required for headers. * Fix header chain checks for uninitialised allocator.
@gilles-peskine-arm: Thanks for letting me know. The problem is yet another piece of code in the library attempting to allocate a 0-length buffer. I fixed it and will push the changes soon. I have also rebased the PR and run all.sh locally before passing it back to you. |
3d06b73
to
e9124b9
Compare
@gilles-peskine-arm: I have rebased on top of development and fixed the issue you raised. I will now update the backports |
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 approve the rebasing from my previous approval (
pr_778-1
) to c9d6226 based on the output ofinterdiff <(git diff mbedtls-2.7...c9d6226) <(git diff mbedtls-2.7...pr_778-1)
which shows only benign differences. - I approve the one additional commit e9124b9.
- This fixes the build failure Fix corner case uses of memory_buffer_alloc.c #778 (review).
@gilles-peskine-arm: Thanks for the review. I will do the backport to 2.7 as soon as I can. |
@andresag01 Since this PR forked before 2.7.0, you don't need to make a 2.7 backport: the same commit can be merged into 2.7 and development. We'll merge this PR into both |
@gilles-peskine-arm: Alright thanks. I will not do the backport then. |
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.
Approving after the backport and additional changes
This change fixes the following corner cases in memory_buffer_alloc.c:
verify_chain()
on an uninitialized chain. Would previously cause aNULL
dereference.This PR is based on the information recorded in #639.
NOTES: