Skip to content

Comments

Memory leak in i2d_ASN1_bio_stream when using SMIME_write_CMS#14844

Closed
Basskrapfen wants to merge 5 commits intoopenssl:masterfrom
Basskrapfen:mem_leak_i2d_ASN1_bio_stream
Closed

Memory leak in i2d_ASN1_bio_stream when using SMIME_write_CMS#14844
Basskrapfen wants to merge 5 commits intoopenssl:masterfrom
Basskrapfen:mem_leak_i2d_ASN1_bio_stream

Conversation

@Basskrapfen
Copy link

When creating a signed S/MIME message using SMIME_write_CMS(), the state machine in the background does not handle error cases completely. This results in a small memory leak in i2d_ASN1_bio_stream / asn_mime.c

In BIO_new_NDEF, ndef_aux is initialized and set to the bio stream via
ndef_aux = OPENSSL_zalloc(sizeof(*ndef_aux));
to bio->ex_arg

This memory is normally freed in asn1_bio_flush_ex (bio_asn1.c, cleanup parameter set to true).

This call is triggered via BIO_flush() which ends up in the asn1_bio_ctrl (bio_asn1.c) in the case the cmd is BIO_CTRL_FLUSH and state is ASN1_STATE_POST_COPY.

The state is also updated in the same function and asn1_bio_write.

In our use case the reading from the bio fails, the state is therefore still ASN1_STATE_START when BIO_flush() is called by i2d_ASN1_bio_stream. This results in calling asn1_bio_flush_ex cleanup but will only reset retry flags as the state is not ASN1_STATE_POST_COPY. Therefore 48 bytes (Linux x86_64) leaked since the ndef_prefix_free / ndef_suffix_free callbacks are not executed and the ndef_aux structure is not freed.

Patch is attached which makes sure the free callbacks are called. Another option would be to improve the state machine but this looks harder to get it right.

CLA: trivial

@t8m t8m added the cla: trivial One of the commits is marked as 'CLA: trivial' label Apr 12, 2021
@slontis
Copy link
Member

slontis commented Apr 13, 2021

This PR needs to raised against master not 1_1_1..

Copy link
Member

@slontis slontis Apr 13, 2021

Choose a reason for hiding this comment

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

This looks like a double free here for the normal case?
ctx->prefix_free is not set to NULL after the cleanup().

Copy link
Author

Choose a reason for hiding this comment

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

The implementation ndef_prefix_free() seems to be safe to be called multiple times. It is freeing "ndef_aux->derbuf" and setting the pointer to NULL afterwards. Calling OPENSSL_free() with NULL should be safe as far as I know.

Alternative would be to zero the free function ptr after it was called. It looks like the state machine cannot be reset to the beginning.

What do you suggest?

Copy link
Member

@slontis slontis Apr 13, 2021

Choose a reason for hiding this comment

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

Coding style (same for suffix)

if (ctx->prefix_free != NULL)
    ctx->prefix_free(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg);

Copy link
Member

Choose a reason for hiding this comment

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

I would really like to see a test for this..
Especially if ctx->ex_buf, ctx->ex_len, ctx->ex_arg are being shared between the prefix and suffix.

Copy link
Author

@Basskrapfen Basskrapfen Apr 29, 2021

Choose a reason for hiding this comment

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

@slontis about the test, can I just extend the cms app with a flag to fail reading from the input?

Copy link
Member

Choose a reason for hiding this comment

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

A low level test added to bio_memleak_test .c would be sufficient..

Copy link
Member

Choose a reason for hiding this comment

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

If you already had some data you should be able to debug into it to see what needs to be set up for a simpler test.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I've added a small unit test which checks for the case. If you comment the "ctx->prefix_free" calls which the patch added, you can verify the memory leak.

@t8m
Copy link
Member

t8m commented Apr 13, 2021

IMO this does not qualify for CLA: trivial.

@Basskrapfen
Copy link
Author

Is the approach to fix the issue OK in general? If yes I can continue to improve the patch.

@slontis
Copy link
Member

slontis commented Apr 13, 2021

It looks ok to me.

@Basskrapfen Basskrapfen force-pushed the mem_leak_i2d_ASN1_bio_stream branch from e945bd0 to 397a65a Compare April 29, 2021 15:55
@Basskrapfen Basskrapfen changed the base branch from OpenSSL_1_1_1-stable to master April 29, 2021 15:56
@t8m
Copy link
Member

t8m commented Apr 30, 2021

@Basskrapfen
Copy link
Author

Please remove the CLA: trivial and submit a CLA. https://www.openssl.org/policies/cla.html

Thanks for the hint, will do so.

@t8m
Copy link
Member

t8m commented May 5, 2021

There is still the CLA issue. Could you please sign a CLA and remove the CLA: trivial annotation from the first commit?
git rebase -i can help you with that. You could also squash the commits.

@Basskrapfen Basskrapfen force-pushed the mem_leak_i2d_ASN1_bio_stream branch from e496374 to 81500bf Compare June 22, 2021 13:34
@t8m t8m added branch: 1.1.1 Applies to OpenSSL_1_1_1-stable branch (EOL) branch: master Applies to master branch triaged: bug The issue/pr is/fixes a bug approval: review pending This pull request needs review by a committer approval: otc review pending and removed cla: trivial One of the commits is marked as 'CLA: trivial' approval: review pending This pull request needs review by a committer labels Jun 22, 2021
Copy link
Member

@t8m t8m left a comment

Choose a reason for hiding this comment

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

LGTM

@t8m t8m added approval: review pending This pull request needs review by a committer and removed approval: otc review pending labels Jun 24, 2021
@github-actions github-actions bot added the severity: fips change The pull request changes FIPS provider sources label Jun 24, 2021
@github-actions github-actions bot removed the severity: fips change The pull request changes FIPS provider sources label Jun 29, 2021
@paulidale paulidale added approval: done This pull request has the required number of approvals and removed approval: review pending This pull request needs review by a committer labels Jul 1, 2021
@openssl-machine openssl-machine added approval: ready to merge The 24 hour grace period has passed, ready to merge and removed approval: done This pull request has the required number of approvals labels Jul 2, 2021
@openssl-machine
Copy link
Collaborator

This pull request is ready to merge

openssl-machine pushed a commit that referenced this pull request Jul 2, 2021
When creating a signed S/MIME message using SMIME_write_CMS()
if the reading from the bio fails, the state is therefore
still ASN1_STATE_START when BIO_flush() is called by i2d_ASN1_bio_stream().
This results in calling asn1_bio_flush_ex cleanup but will only
reset retry flags as the state is not ASN1_STATE_POST_COPY.
Therefore 48 bytes (Linux x86_64) leaked since the
ndef_prefix_free / ndef_suffix_free callbacks are not executed
and the ndef_aux structure is not freed.

By always calling free function callback in asn1_bio_free() the
memory leak is fixed.

Reviewed-by: Paul Dale <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
(Merged from #14844)
@t8m
Copy link
Member

t8m commented Jul 2, 2021

I've squashed the commits and used the description from this PR to improve the commit message. Merged. Thank you for the contribution.

@t8m t8m closed this Jul 2, 2021
@t8m
Copy link
Member

t8m commented Jul 2, 2021

As this does not cherry-pick cleanly, would you like to create a PR cherry picking the commit for 1.1.1, or should we do that?

@Basskrapfen
Copy link
Author

As this does not cherry-pick cleanly, would you like to create a PR cherry picking the commit for 1.1.1, or should we do that?

I can try cherry picking it for the 1.1.1 branch. Thanks a lot again for the support!

@Basskrapfen Basskrapfen deleted the mem_leak_i2d_ASN1_bio_stream branch July 5, 2021 13:19
devnexen pushed a commit to devnexen/openssl that referenced this pull request Jul 7, 2021
When creating a signed S/MIME message using SMIME_write_CMS()
if the reading from the bio fails, the state is therefore
still ASN1_STATE_START when BIO_flush() is called by i2d_ASN1_bio_stream().
This results in calling asn1_bio_flush_ex cleanup but will only
reset retry flags as the state is not ASN1_STATE_POST_COPY.
Therefore 48 bytes (Linux x86_64) leaked since the
ndef_prefix_free / ndef_suffix_free callbacks are not executed
and the ndef_aux structure is not freed.

By always calling free function callback in asn1_bio_free() the
memory leak is fixed.

Reviewed-by: Paul Dale <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
(Merged from openssl#14844)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch branch: 1.1.1 Applies to OpenSSL_1_1_1-stable branch (EOL) triaged: bug The issue/pr is/fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants