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

[#1823] replace malloc/calloc/strdup/free with openssl allocator #1926

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

songlingatpan
Copy link

Replaced malloc, calloc, strdup, and free with the OpenSSL memory allocator to enable the caller to customize memory allocator, addressing issue #1823. This PR does not change the existing behavior or algorithms.

@songlingatpan
Copy link
Author

I will commit another change for copy_from_upstream.

Copy link
Member

@baentsch baentsch left a comment

Choose a reason for hiding this comment

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

@songlingatpan
Copy link
Author

@baentsch Question: 'bike', 'frodokem', 'ntruprime' is not from upstream.
To replace malloc with OQS_MEM_malloc, where should i modify the code? Thanks
liboqs/src/kem/frodokem/kem_frodokem976shake.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/frodokem/kem_frodokem640aes.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/frodokem/kem_frodokem1344shake.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/frodokem/kem_frodokem976aes.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/frodokem/kem_frodokem1344aes.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/frodokem/kem_frodokem640shake.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/kem.h:264:OQS_API void OQS_KEM_free(OQS_KEM *kem); liboqs/src/kem/ntruprime/kem_ntruprime_sntrup761.c:11: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/kem.c:493:OQS_API void OQS_KEM_free(OQS_KEM *kem) { liboqs/src/kem/bike/kem_bike.c:9: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/bike/kem_bike.c:34: OQS_KEM *kem = malloc(sizeof(OQS_KEM)); liboqs/src/kem/bike/kem_bike.c:59: OQS_KEM *kem = malloc(sizeof(OQS_KEM));

@baentsch
Copy link
Member

Question: 'bike', 'frodokem', 'ntruprime' is not from upstream.

Good point. @dstebila : There's surely upstreams for these algorithms, but they're not captured by OQS automation/copy_from_upstream. Question: Are these projects still maintained (and where) or is it OK to change code straight in liboqs for these algs? If the former, what would it take to bring them in via "copy_from_upstream"? If the latter, should we remove them completely from OQS?

@dstebila
Copy link
Member

Question: 'bike', 'frodokem', 'ntruprime' is not from upstream.

Good point. @dstebila : There's surely upstreams for these algorithms, but they're not captured by OQS automation/copy_from_upstream. Question: Are these projects still maintained (and where) or is it OK to change code straight in liboqs for these algs? If the former, what would it take to bring them in via "copy_from_upstream"? If the latter, should we remove them completely from OQS?

FrodoKEM is maintained at https://github.com/microsoft/PQCrypto-LWEKE/; I exported the code from there any manually added it to liboqs, as we didn't have as robust a copy_from_upstream at the time. FrodoKEM also needs to do an update from upstream, as there have been new variants introduced in the last year, but I don't have a plan for this update. So to avoid blocking on that, I would say it's fine to make the FrodoKEM changes directly here in this repository.

NTRUPrime had been coming from PQClean, but they have stopped supporting it. We only are keeping one variant of NTRUPrime because of its use in OpenSSH. I think we consider a timeline for sunsetting it. But in the interim, I think changes to NTRUPrime can be done directly here in this repository.

BIKE was contributed directly by the team at AWS. Our main contact for that has been @dkostic, but I'm not sure if he's still the right contact. Pinging @brian-jarvis-aws for some input.

@songlingatpan
Copy link
Author

@dstebila @baentsch @bhess @jschanck
Code change for openssl allocator has been done.
Please review the code.

In addition, there are quite some error handling and potential memory leak.
Would you like to fix them in the current PR? or create a separate PR for review?

Thanks

@songlingatpan
Copy link
Author

**baentsch ** requested changes

done

@dstebila
Copy link
Member

In addition, there are quite some error handling and potential memory leak. Would you like to fix them in the current PR? or create a separate PR for review?

I'd prefer to see those as a second PR, since those changes may be less mechanical and might require a closer look.

@dstebila dstebila added this to the 0.12.0 milestone Sep 17, 2024
@dstebila
Copy link
Member

I suggest we defer merging this until after the 0.11.0 release, otherwise we would need to cut a new release candidate and push the release back a week.

@songlingatpan
Copy link
Author

@dstebila @baentsch @bhess @jschanck
This PR is ready for review and merge.

@@ -32,7 +32,7 @@
#define ppad 769
#define endingmask _mm256_set_epi8(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
#define crypto_core_weight PQCLEAN_SNTRUP761_AVX2_crypto_core_weightsntrup761
#define p 761
#define p_param 761
Copy link
Member

Choose a reason for hiding this comment

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

I'm not immediately seeing the reason for this name change—could you explain why it's necessary?

Copy link
Author

Choose a reason for hiding this comment

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

There is some conflict with openssl.
In file included from /Volumes/GIT/git/oqs/liboqs/src/kem/ntruprime/pqclean_sntrup761_clean/kem.c:5: In file included from /Volumes/GIT/git/oqs/liboqs/src/common/pqclean_shims/randombytes.h:6: In file included from /Volumes/GIT/git/oqs/liboqs/build/include/oqs/rand.h:15: In file included from /Volumes/GIT/git/oqs/liboqs/build/include/oqs/common.h:30: In file included from /usr/local/opt/openssl@3/include/openssl/crypto.h:34: In file included from /usr/local/opt/openssl@3/include/openssl/safestack.h:24: /usr/local/opt/openssl@3/include/openssl/stack.h:45:60: error: expected ')' void *OPENSSL_sk_delete_ptr(OPENSSL_STACK *st, const void *p); ^ /Volumes/GIT/git/oqs/liboqs/src/kem/ntruprime/pqclean_sntrup761_clean/params.h:31:11: note: expanded from macro 'p' #define p 761 ^ /usr/local/opt/openssl@3/include/openssl/stack.h:45:28: note: to match this '(' void *OPENSSL_sk_delete_ptr(OPENSSL_STACK *st, const void *p); ^ 1 error generated. [121/1636] Building C object src/kem/frodokem/CMakeFiles/frodokem.dir/external/frodo1344aes.c.o ninja: build stopped: subcommand failed.

Copy link
Member

Choose a reason for hiding this comment

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

Hmmmm, I see. Is there a clean way to handle this with #include guards or something similar? Renaming seems like a brittle workaround. Suppose for argument's sake that a future version of OpenSSL changes the function parameter to const void *q—then everything would break here again.

Copy link
Member

Choose a reason for hiding this comment

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

What I wouldn't give for C++ namespaces... 😜

@SWilson4
Copy link
Member

Thanks for this work, @songlingatpan!

How would you feel about dropping the OQS_MEM_free function and instead using OQS_MEM_insecure_free (presumably modifying it to use OPENSSL_free)? I think it's a good idea to have a clear separation between "insecure" and "secure" frees, and we lose that if we introduce a not-explicitly-insecure free function with the same signature as OQS_MEM_insecure_free.

@SWilson4
Copy link
Member

After this lands, we may want to add some sort of static analysis to guard against raw malloc or free calls. Seems like something related to #1868.

Signed-off-by: Songling Han <[email protected]>
@songlingatpan songlingatpan marked this pull request as draft September 23, 2024 21:56
Signed-off-by: Songling Han <[email protected]>
@songlingatpan songlingatpan marked this pull request as ready for review September 23, 2024 22:51
@songlingatpan
Copy link
Author

Thanks for this work, @songlingatpan!

How would you feel about dropping the OQS_MEM_free function and instead using OQS_MEM_insecure_free (presumably modifying it to use OPENSSL_free)? I think it's a good idea to have a clear separation between "insecure" and "secure" frees, and we lose that if we introduce a not-explicitly-insecure free function with the same signature as OQS_MEM_insecure_free.

Addressed the comment in the latest PR. Please review it.

@songlingatpan
Copy link
Author

After this lands, we may want to add some sort of static analysis to guard against raw malloc or free calls. Seems like something related to #1868.

Correct. We should phase out direct calls to malloc, free, calloc, strdup, and realloc.

@dstebila
Copy link
Member

After this lands, we may want to add some sort of static analysis to guard against raw malloc or free calls. Seems like something related to #1868.

Correct. We should phase out direct calls to malloc, free, calloc, strdup, and realloc.

test_free in tests/test_code_conventions.py does this for free calls using an ad hoc script. It may be better to do this with CodeQL in the long run.

@songlingatpan
Copy link
Author

test_code_conventions.py

Addressed the comment by adding code in test_code_conventions.py.

if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
abort();
return NULL; //abort();
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this change effectively make OQS_checked_malloc and OQS_mem_malloc the same function (up to printing)?

Copy link
Author

Choose a reason for hiding this comment

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

True.
Many embedded systems have strict memory constraints, and encountering a malloc failure is not uncommon in such environments.

src/common/common.c Outdated Show resolved Hide resolved
@@ -289,7 +292,7 @@ void *OQS_MEM_checked_aligned_alloc(size_t alignment, size_t size) {
void *ptr = OQS_MEM_aligned_alloc(alignment, size);
if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
abort();
return NULL; //abort();
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here regarding OQS_MEM_checked_aligned_alloc and OQS_MEM_aligned_alloc.

Copy link
Author

Choose a reason for hiding this comment

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

yes. Please let me know if you agree to remove the checked functions. If so, I will go ahead and remove them.

Copy link
Member

Choose a reason for hiding this comment

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

We could—but then we'll be left with a bunch of common-code functions that rely on the "checked" functions aborting if allocation fails. If this code is left unchanged, won't it lead to more crashes and/or memory corruption? If it needs to be changed to handle unchecked functions, is it not simpler to keep the "checked" functions rather than factor out the check everywhere?

Copy link
Author

Choose a reason for hiding this comment

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

The caller must perform a NULL check before dereferencing the pointer. Additionally, in the case of a NULL check failure, the caller is responsible for releasing any allocated resources to prevent memory leaks.

Copy link
Member

Choose a reason for hiding this comment

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

The caller must perform a NULL check before dereferencing the pointer. Additionally, in the case of a NULL check failure, the caller is responsible for releasing any allocated resources to prevent memory leaks.

Sure—but those NULL checks are not currently present in the codebase, because the callers were relying on the checked_malloc functions. If we replace the checked_malloc functions with the unchecked versions in this PR, the NULL checks must also be added in this PR. For example this code will dereference NULL on malloc failure.

continue
if 'IGNORE free-check' in line:
# Check if we're inside a multi-line comment
if '/*' in content[:content.find(line)] and '*/' not in content[:content.find(line)]:
Copy link
Member

Choose a reason for hiding this comment

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

Just a note, this will produce a false positive in the following scenario:

/* comment */
/*
malloc(sizeof(int));
*/
OQS_MEM_malloc(sizeof(int));

I think you'd have to search backwards for the closest /*, but that seems like overkill.

Copy link
Author

Choose a reason for hiding this comment

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

@SWilson4 You are right. We can improve it later.

@songlingatpan
Copy link
Author

@dstebila @baentsch @SWilson4
Is there anything else I need to address before we proceed with merging this PR?

Thanks!

@SWilson4
Copy link
Member

SWilson4 commented Oct 2, 2024

@dstebila @baentsch @SWilson4 Is there anything else I need to address before we proceed with merging this PR?

Thanks!

Please take a look at my comments here and here and let me know what you think.

@songlingatpan
Copy link
Author

@dstebila @baentsch @SWilson4 Is there anything else I need to address before we proceed with merging this PR?
Thanks!

Please take a look at my comments here and here and let me know what you think.

It seems the conflicts issue only occurs for certain openssl version/platform. Didn't see conflicts any more after revert.

@songlingatpan
Copy link
Author

@SWilson4 Addressed your 2 comments.
Please let me know if I need to run copy_from_upstream.py or any other scripts to regenerate code before merge.
If so, what is the detail steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants