From 1e80109d707d83d17f1bd0ab107647ba9e4b9805 Mon Sep 17 00:00:00 2001 From: thb-sb Date: Tue, 5 Sep 2023 20:01:19 +0200 Subject: [PATCH] Fix various memory leaks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes various memory leaks that have been found using ASan. Note: I will soon add a CI job for testing oqs-provider against memory leaks with ASan. # 1. Memory leak in `oqs_test_signatures`. In `oqs_test_signatures`, [`OPENSSL_free`] was used with `EVP_PKEY_CTX`. However, the right destructor function for `EVP_PKEY_CTX` is [`EVP_PKEY_CTX_free`]. This commmit fixes this minor bug. [`OPENSSL_free`](https://www.openssl.org/docs/man3.0/man3/OPENSSL_free.html) [`EVP_PKEY_CTX_free`](https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_CTX_free.html) ## 2. a selftest key that wasn't freed. In `oqsprov/oqsprov_keys.c`, `d2i_PrivateKey` is used to verify that we the key we've just generated is sound. However, the returned object wasn't freed. ASan trace that helped me finding this one: ``` Indirect leak of 96 byte(s) in 4 object(s) allocated from: #0 0x5651eef1428e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_signatures+0xb928e) (BuildId: 6cc7ebd4bc73a0c78b2ab8d7b9dcb8b165d88e74) #1 0x7fb7a23a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7fb7a23a94a2 in CRYPTO_zalloc /home/pawn/work/openssl/crypto/mem.c:197:11 #3 0x7fb7a209cdcd in BN_new /home/pawn/work/openssl/crypto/bn/bn_lib.c:247:16 #4 0x7fb7a2224a00 in ossl_ec_GFp_mont_group_set_curve /home/pawn/work/openssl/crypto/ec/ecp_mont.c:169:11 #5 0x7fb7a220f5fa in EC_GROUP_set_curve /home/pawn/work/openssl/crypto/ec/ec_lib.c:562:12 #6 0x7fb7a2202d75 in EC_GROUP_new_curve_GFp /home/pawn/work/openssl/crypto/ec/ec_cvt.c:61:10 #7 0x7fb7a220184d in ec_group_new_from_data /home/pawn/work/openssl/crypto/ec/ec_curve.c:3179:22 #8 0x7fb7a2200ff9 in EC_GROUP_new_by_curve_name_ex /home/pawn/work/openssl/crypto/ec/ec_curve.c:3287:19 #9 0x7fb7a2201ed9 in EC_GROUP_new_by_curve_name /home/pawn/work/openssl/crypto/ec/ec_curve.c:3303:12 #10 0x7fb7a21f84e2 in EC_GROUP_new_from_ecpkparameters /home/pawn/work/openssl/crypto/ec/ec_asn1.c:863:20 #11 0x7fb7a21f8f28 in d2i_ECPrivateKey /home/pawn/work/openssl/crypto/ec/ec_asn1.c:956:22 #12 0x7fb7a270b4b5 in der2key_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_der2key.c:220:19 #13 0x7fb7a226eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #14 0x7fb7a226d106 in OSSL_DECODER_from_bio /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:82:10 #15 0x7fb7a226f007 in OSSL_DECODER_from_data /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:157:9 #16 0x7fb7a200e123 in d2i_PrivateKey_decoder /home/pawn/work/openssl/crypto/asn1/d2i_pr.c:74:11 #17 0x7fb7a200dbba in d2i_PrivateKey_ex /home/pawn/work/openssl/crypto/asn1/d2i_pr.c:162:11 #18 0x7fb7a200e3d2 in d2i_PrivateKey /home/pawn/work/openssl/crypto/asn1/d2i_pr.c:172:12 #19 0x7fb79dc6a22e in oqsx_key_gen_evp_key /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:1036:25 #20 0x7fb79dc68f3f in oqsx_key_gen /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:1077:16 #21 0x7fb79dc7164a in oqsx_genkey /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:496:9 #22 0x7fb79dc6d9f8 in oqsx_gen /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:509:12 #23 0x7fb7a23373c7 in evp_keymgmt_gen /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:366:12 #24 0x7fb7a2334b58 in evp_keymgmt_util_gen /home/pawn/work/openssl/crypto/evp/keymgmt_lib.c:517:20 #25 0x7fb7a235a775 in EVP_PKEY_generate /home/pawn/work/openssl/crypto/evp/pmeth_gn.c:189:13 #26 0x5651eef4f2bd in test_oqs_signatures /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:39:48 #27 0x5651eef4ee3d in main /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:114:17 #28 0x7fb7a1b671c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 ``` ## 3. a `EVP_PKEY_CTX` that wasn't freed. In `oqsx_key_free`, `key->oqsx_provider_ctx.oqsx_evp_ctx->ctx` is freed if the keytype is `ECP_HYB_KEM` or `ECX_HYB_KEM`. However, `key->oqsx_provider_ctx.oqsx_evp_ctx->ctx` is also used for signatures. In that case, a call to `EVP_PKEY_CTX_free` was missing. ASan trace that helped me finding this one: ``` Indirect leak of 7 byte(s) in 2 object(s) allocated from: #0 0x55b6fc8d427e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_signatures+0xb927e) (BuildId: 3a8bff15d3315ba7ac1746fdc7b67eb464c607a1) #1 0x7ff34dfa946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7ff34dfac50a in CRYPTO_strndup /home/pawn/work/openssl/crypto/o_str.c:43:11 #3 0x7ff34df9d152 in ossl_algorithm_get1_first_name /home/pawn/work/openssl/crypto/core_algorithm.c:195:11 #4 0x7ff34df353a1 in keymgmt_from_algorithm /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:50:31 #5 0x7ff34df15e44 in construct_evp_method /home/pawn/work/openssl/crypto/evp/evp_fetch.c:219:14 #6 0x7ff34df9e3ce in ossl_method_construct_this /home/pawn/work/openssl/crypto/core_fetch.c:109:19 #7 0x7ff34df9d740 in algorithm_do_map /home/pawn/work/openssl/crypto/core_algorithm.c:77:13 #8 0x7ff34df9ce88 in algorithm_do_this /home/pawn/work/openssl/crypto/core_algorithm.c:122:15 #9 0x7ff34dfcce3e in ossl_provider_doall_activated /home/pawn/work/openssl/crypto/provider_core.c:1423:14 #10 0x7ff34df9ca64 in ossl_algorithm_do_all /home/pawn/work/openssl/crypto/core_algorithm.c:162:9 #11 0x7ff34df9dd64 in ossl_method_construct /home/pawn/work/openssl/crypto/core_fetch.c:153:5 #12 0x7ff34df13b6f in inner_evp_generic_fetch /home/pawn/work/openssl/crypto/evp/evp_fetch.c:312:23 #13 0x7ff34df13226 in evp_generic_fetch /home/pawn/work/openssl/crypto/evp/evp_fetch.c:364:14 #14 0x7ff34df36a42 in EVP_KEYMGMT_fetch /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:220:12 #15 0x7ff34df5d6e4 in int_ctx_new /home/pawn/work/openssl/crypto/evp/pmeth_lib.c:280:23 #16 0x7ff34df5f0a9 in EVP_PKEY_CTX_new_id /home/pawn/work/openssl/crypto/evp/pmeth_lib.c:469:12 #17 0x7ff349865bcb in oqsx_hybsig_init /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:578:20 #18 0x7ff349865157 in oqsx_key_new /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:791:16 #19 0x7ff3498715a3 in oqsx_genkey /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:487:16 #20 0x7ff34986da08 in oqsx_gen /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:509:12 #21 0x7ff34df373c7 in evp_keymgmt_gen /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:366:12 #22 0x7ff34df34b58 in evp_keymgmt_util_gen /home/pawn/work/openssl/crypto/evp/keymgmt_lib.c:517:20 #23 0x7ff34df5a775 in EVP_PKEY_generate /home/pawn/work/openssl/crypto/evp/pmeth_gn.c:189:13 #24 0x55b6fc90f2ad in test_oqs_signatures /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:39:48 #25 0x55b6fc90ee2d in main /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:114:17 #26 0x7ff34d8461c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 ``` ## 4. Missing `EVP_PKEY_CTX_free` in case of error. In `oqsx_hybsig_init`, a `EVP_PKEY_CTX` is initialized using `EVP_PKEY_CTX_new_id`. However, later in the code, some extra work is done, and in case of error, the `EVP_PKEY_CTX` wasn't freed. (I couldn't recover the ASan message for this oneā€¦) ## 5. `OPENSSL_free` instead of `EVP_PKEY_free`. In `oqsx_key_gen`, when a classical private key is generated, the field `classical_pkey` of a `OQSX_KEY` is set with a value of type `EVP_PKEY`. However, in `oqsx_key_free`, `OPENSSL_free` was used on `classical_pkey` instead of `EVP_PKEY_free`. ASan trace that helped me finding this one: ``` #0 0x555a6b1b728e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_signatures+0xb928e) (BuildId: 6cc7ebd4bc73a0c78b2ab8d7b9dcb8b165d88e74) #1 0x7f59a93a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7f59a93a94a2 in CRYPTO_zalloc /home/pawn/work/openssl/crypto/mem.c:197:11 #3 0x7f59a909cdcd in BN_new /home/pawn/work/openssl/crypto/bn/bn_lib.c:247:16 #4 0x7f59a920b955 in ossl_ec_group_new_ex /home/pawn/work/openssl/crypto/ec/ec_lib.c:61:25 #5 0x7f59a920dd55 in EC_GROUP_dup /home/pawn/work/openssl/crypto/ec/ec_lib.c:273:14 #6 0x7f59a9207774 in EC_KEY_set_group /home/pawn/work/openssl/crypto/ec/ec_key.c:733:18 #7 0x7f59a975b3b5 in ec_gen_assign_group /home/pawn/work/openssl/providers/implementations/keymgmt/ec_kmgmt.c:1236:12 #8 0x7f59a9758bdb in ec_gen /home/pawn/work/openssl/providers/implementations/keymgmt/ec_kmgmt.c:1274:11 #9 0x7f59a93373c7 in evp_keymgmt_gen /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:366:12 #10 0x7f59a9334b58 in evp_keymgmt_util_gen /home/pawn/work/openssl/crypto/evp/keymgmt_lib.c:517:20 #11 0x7f59a935a775 in EVP_PKEY_generate /home/pawn/work/openssl/crypto/evp/pmeth_gn.c:189:13 #12 0x7f59a935b404 in EVP_PKEY_keygen /home/pawn/work/openssl/crypto/evp/pmeth_gn.c:274:12 #13 0x7f59a4c69a51 in oqsx_key_gen_evp_key /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:1012:12 #14 0x7f59a4c68faf in oqsx_key_gen /home/pawn/work/oqs-provider/oqsprov/oqsprov_keys.c:1084:16 #15 0x7f59a4c716ca in oqsx_genkey /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:496:9 #16 0x7f59a4c6da78 in oqsx_gen /home/pawn/work/oqs-provider/oqsprov/oqs_kmgmt.c:509:12 #17 0x7f59a93373c7 in evp_keymgmt_gen /home/pawn/work/openssl/crypto/evp/keymgmt_meth.c:366:12 #18 0x7f59a9334b58 in evp_keymgmt_util_gen /home/pawn/work/openssl/crypto/evp/keymgmt_lib.c:517:20 #19 0x7f59a935a775 in EVP_PKEY_generate /home/pawn/work/openssl/crypto/evp/pmeth_gn.c:189:13 #20 0x555a6b1f2890 in test_oqs_signatures /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:68:44 #21 0x555a6b1f1e3d in main /home/pawn/work/oqs-provider/test/oqs_test_signatures.c:114:17 #22 0x7f59a8c461c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 ``` ## 6. Some `OPENSSL_free` were missing in `oqs_test_kems.c` ``` ==569479==ERROR: LeakSanitizer: detected memory leaks Direct leak of 346035 byte(s) in 42 object(s) allocated from: #0 0x559c76ef524e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_kems+0xb924e) (BuildId: 76a98f5859ba3c90334cef4286e89aa76436b1ae) #1 0x7f04407a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x559c76f30389 in test_oqs_kems /home/pawn/work/oqs-provider/test/oqs_test_kems.c:44:26 #3 0x559c76f2fdfd in main /home/pawn/work/oqs-provider/test/oqs_test_kems.c:93:17 #4 0x7f04400461c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Direct leak of 2690 byte(s) in 42 object(s) allocated from: #0 0x559c76ef524e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_kems+0xb924e) (BuildId: 76a98f5859ba3c90334cef4286e89aa76436b1ae) #1 0x7f04407a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x559c76f304a5 in test_oqs_kems /home/pawn/work/oqs-provider/test/oqs_test_kems.c:47:29 #3 0x559c76f2fdfd in main /home/pawn/work/oqs-provider/test/oqs_test_kems.c:93:17 #4 0x7f04400461c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Direct leak of 2690 byte(s) in 42 object(s) allocated from: #0 0x559c76ef524e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_kems+0xb924e) (BuildId: 76a98f5859ba3c90334cef4286e89aa76436b1ae) #1 0x7f04407a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x559c76f303e4 in test_oqs_kems /home/pawn/work/oqs-provider/test/oqs_test_kems.c:45:29 #3 0x559c76f2fdfd in main /home/pawn/work/oqs-provider/test/oqs_test_kems.c:93:17 #4 0x7f04400461c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 ``` ## 7. Missing `X509_PUBKEY_free` in `oqsx_d2i_PUBKEY`. This was probably the hardest one for me to track down. In `oqsx_d2i_PUBKEY`, a call is done to `oqsx_d2i_X509_PUBKEY_INTERNAL`. However, the returned value (of type `X509_PUBKEY`) wasn't freed. ASan: ``` Direct leak of 48 byte(s) in 1 object(s) allocated from: #0 0x55610d68132e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_endecode+0xb932e) (BuildId: 53bfaa0a3b850edc7328f30be8b047ab6675ba3c) #1 0x7f68999a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7f68999a94a2 in CRYPTO_zalloc /home/pawn/work/openssl/crypto/mem.c:197:11 #3 0x7f6895295897 () #4 0x7f6895295ce4 () #5 0x7f6895296a6c () #6 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #7 0x7f6899d13383 in spki2typespki_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_spki2typespki.c:111:10 #8 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #9 0x7f6899d114d3 in pem2der_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_pem2der.c:204:14 #10 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #11 0x7f689986d106 in OSSL_DECODER_from_bio /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:82:10 #12 0x55610d6bd407 in decode_EVP_PKEY_prov /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:151:10 #13 0x55610d6bc75b in test_oqs_encdec /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:188:14 #14 0x55610d6bbf37 in main /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:240:17 #15 0x7f68991671c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Indirect leak of 1312 byte(s) in 1 object(s) allocated from: #0 0x55610d68132e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_endecode+0xb932e) (BuildId: 53bfaa0a3b850edc7328f30be8b047ab6675ba3c) #1 0x7f68999a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7f68995d6b99 in ossl_c2i_ASN1_BIT_STRING /home/pawn/work/openssl/crypto/asn1/a_bitstr.c:117:13 #3 0x7f689961f659 in asn1_ex_c2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:885:14 #4 0x7f689961bbc9 in asn1_d2i_ex_primitive /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:818:10 #5 0x7f6899618054 in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:217:16 #6 0x7f689961e02f in asn1_template_noexp_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:682:15 #7 0x7f689961a7b4 in asn1_template_ex_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:558:16 #8 0x7f689961986a in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:422:19 #9 0x7f68996175c8 in asn1_item_ex_d2i_intern /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:118:10 #10 0x7f68996177da in ASN1_item_d2i_ex /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:144:9 #11 0x7f6895295926 () #12 0x7f6895295ce4 () #13 0x7f6895296a6c () #14 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #15 0x7f6899d13383 in spki2typespki_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_spki2typespki.c:111:10 #16 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #17 0x7f6899d114d3 in pem2der_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_pem2der.c:204:14 #18 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #19 0x7f689986d106 in OSSL_DECODER_from_bio /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:82:10 #20 0x55610d6bd407 in decode_EVP_PKEY_prov /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:151:10 #21 0x55610d6bc75b in test_oqs_encdec /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:188:14 #22 0x55610d6bbf37 in main /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:240:17 #23 0x7f68991671c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Indirect leak of 24 byte(s) in 1 object(s) allocated from: #0 0x55610d68132e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_endecode+0xb932e) (BuildId: 53bfaa0a3b850edc7328f30be8b047ab6675ba3c) #1 0x7f68999a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7f68999a94a2 in CRYPTO_zalloc /home/pawn/work/openssl/crypto/mem.c:197:11 #3 0x7f68995fde90 in ASN1_STRING_type_new /home/pawn/work/openssl/crypto/asn1/asn1_lib.c:350:11 #4 0x7f689962a7dd in ASN1_BIT_STRING_new /home/pawn/work/openssl/crypto/asn1/tasn_typ.c:31:1 #5 0x7f68995d6a0f in ossl_c2i_ASN1_BIT_STRING /home/pawn/work/openssl/crypto/asn1/a_bitstr.c:98:20 #6 0x7f689961f659 in asn1_ex_c2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:885:14 #7 0x7f689961bbc9 in asn1_d2i_ex_primitive /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:818:10 #8 0x7f6899618054 in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:217:16 #9 0x7f689961e02f in asn1_template_noexp_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:682:15 #10 0x7f689961a7b4 in asn1_template_ex_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:558:16 #11 0x7f689961986a in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:422:19 #12 0x7f68996175c8 in asn1_item_ex_d2i_intern /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:118:10 #13 0x7f68996177da in ASN1_item_d2i_ex /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:144:9 #14 0x7f6895295926 () #15 0x7f6895295ce4 () #16 0x7f6895296a6c () #17 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #18 0x7f6899d13383 in spki2typespki_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_spki2typespki.c:111:10 #19 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #20 0x7f6899d114d3 in pem2der_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_pem2der.c:204:14 #21 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #22 0x7f689986d106 in OSSL_DECODER_from_bio /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:82:10 #23 0x55610d6bd407 in decode_EVP_PKEY_prov /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:151:10 #24 0x55610d6bc75b in test_oqs_encdec /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:188:14 #25 0x55610d6bbf37 in main /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:240:17 #26 0x7f68991671c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Indirect leak of 16 byte(s) in 1 object(s) allocated from: #0 0x55610d68132e in malloc (/home/pawn/work/oqs-provider/build/test/oqs_test_endecode+0xb932e) (BuildId: 53bfaa0a3b850edc7328f30be8b047ab6675ba3c) #1 0x7f68999a946b in CRYPTO_malloc /home/pawn/work/openssl/crypto/mem.c:190:12 #2 0x7f68999a94a2 in CRYPTO_zalloc /home/pawn/work/openssl/crypto/mem.c:197:11 #3 0x7f6899625ed4 in asn1_item_embed_new /home/pawn/work/openssl/crypto/asn1/tasn_new.c:136:21 #4 0x7f689962620e in ossl_asn1_item_ex_new_intern /home/pawn/work/openssl/crypto/asn1/tasn_new.c:52:12 #5 0x7f68996191a5 in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:366:21 #6 0x7f689961e02f in asn1_template_noexp_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:682:15 #7 0x7f689961a7b4 in asn1_template_ex_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:558:16 #8 0x7f689961986a in asn1_item_embed_d2i /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:422:19 #9 0x7f68996175c8 in asn1_item_ex_d2i_intern /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:118:10 #10 0x7f68996177da in ASN1_item_d2i_ex /home/pawn/work/openssl/crypto/asn1/tasn_dec.c:144:9 #11 0x7f6895295926 () #12 0x7f6895295ce4 () #13 0x7f6895296a6c () #14 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #15 0x7f6899d13383 in spki2typespki_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_spki2typespki.c:111:10 #16 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #17 0x7f6899d114d3 in pem2der_decode /home/pawn/work/openssl/providers/implementations/encode_decode/decode_pem2der.c:204:14 #18 0x7f689986eae8 in decoder_process /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:971:14 #19 0x7f689986d106 in OSSL_DECODER_from_bio /home/pawn/work/openssl/crypto/encode_decode/decoder_lib.c:82:10 #20 0x55610d6bd407 in decode_EVP_PKEY_prov /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:151:10 #21 0x55610d6bc75b in test_oqs_encdec /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:188:14 #22 0x55610d6bbf37 in main /home/pawn/work/oqs-provider/test/oqs_test_endecode.c:240:17 #23 0x7f68991671c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 SUMMARY: AddressSanitizer: 1400 byte(s) leaked in 4 allocation(s). ``` ## 8. Bonus point: fix a potential double-free. In `oqsx_key_gen`, when the keytype isn't `KEY_TYPE_HYB_SIG`, the generated `pkey` is freed, and the flow goes down to the `err` label: https://github.com/open-quantum-safe/oqs-provider/blob/cc3895f208730abfe1efc15f7c3ff4358ac6e4c0/oqsprov/oqsprov_keys.c#L1089-L1104 There is a potential double-free bug here: if `oqsx_key_gen_oqs` fails, then `EVP_PKEY_free` will be called twice on the same `pkey` pointer. Note that it is very unlikely that `oqsx_key_gen_oqs` fails, since it only calls `OQS_KEM_keypair`/`OQS_SIG_keypair`. --- .circleci/config.yml | 51 ++++++++++++++++++++++++++++++++++++ oqsprov/oqs_decode_der2key.c | 1 + oqsprov/oqsprov_keys.c | 20 +++++++++----- test/oqs_test_endecode.c | 35 ++++++++++++++----------- test/oqs_test_kems.c | 11 +++++--- test/oqs_test_signatures.c | 4 +-- 6 files changed, 96 insertions(+), 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 074d28fa..ce75b772 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -253,6 +253,56 @@ jobs: command: | find . -type f -and '(' -name '*.h' -or -name '*.c' -or -name '*.inc' ')' | xargs clang-format --dry-run --Werror + check-ASan-tests: + docker: + - image: cimg/base:2022.12 + environment: + CC: "clang" + CXX: "clang++" + ASAN_C_FLAGS: "-fsanitize=address -fno-omit-frame-pointer" + ASAN_OPTIONS: "detect_stack_use_after_return=1,detect_leaks=1" + steps: + - checkout + - run: + name: Install dependencies + command: sudo apt-get update && sudo apt-get install -y clang llvm ninja-build + - run: + name: Clone and build OpenSSL(3) master with ASan + command: | + git clone --branch master git://git.openssl.org/openssl.git openssl + cd openssl + mkdir install + ./Configure --debug --openssldir="$PWD/install" --prefix="$PWD/install" enable-asan + make -j$(nproc) + make install_sw + cd .. + - run: + name: Clone and build liboqs with ASan + command: | + git clone --depth 1 --branch main https://github.com/open-quantum-safe/liboqs.git liboqs + cd liboqs + mkdir build install + cmake -GNinja -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DOQS_USE_OPENSSL=OFF \ + -DCMAKE_C_FLAGS="${ASAN_C_FLAGS}" \ + -DCMAKE_EXE_LINKER_FLAGS="${ASAN_C_FLAGS}" \ + -DCMAKE_INSTALL_PREFIX="$PWD/install" + cmake --build build -j$(nproc) + cmake --install build + cd .. + - run: + name: Build oqs-provider with ASan + command: | + cmake -GNinja -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DOPENSSL_ROOT_DIR="$PWD/openssl/install" \ + -Dliboqs_DIR="$PWD/liboqs/install/lib/cmake/liboqs" \ + -DCMAKE_C_FLAGS="${ASAN_C_FLAGS}" \ + -DCMAKE_EXE_LINKER_FLAGS="${ASAN_C_FLAGS}" + cmake --build build -j$(nproc) + ctest --test-dir build/ + trigger-downstream-ci: docker: - image: cimg/base:2020.01 @@ -278,6 +328,7 @@ workflows: build: jobs: - check-clang-format + - check-ASan-tests - ubuntu: name: ubuntu-focal context: openquantumsafe diff --git a/oqsprov/oqs_decode_der2key.c b/oqsprov/oqs_decode_der2key.c index 35c79a5d..da4d666b 100644 --- a/oqsprov/oqs_decode_der2key.c +++ b/oqsprov/oqs_decode_der2key.c @@ -197,6 +197,7 @@ OQSX_KEY *oqsx_d2i_PUBKEY(OQSX_KEY **a, const unsigned char **pp, long length) xpk = oqsx_d2i_X509_PUBKEY_INTERNAL(pp, length, NULL); key = oqsx_key_from_x509pubkey(xpk, NULL, NULL); + X509_PUBKEY_free(xpk); if (key == NULL) return NULL; diff --git a/oqsprov/oqsprov_keys.c b/oqsprov/oqsprov_keys.c index 9eb15552..7947f27e 100644 --- a/oqsprov/oqsprov_keys.c +++ b/oqsprov/oqsprov_keys.c @@ -580,16 +580,20 @@ static int oqsx_hybsig_init(int bit_security, OQSX_EVP_CTX *evp_ctx, if (idx < 3) { // EC ret = EVP_PKEY_paramgen_init(evp_ctx->ctx); - ON_ERR_GOTO(ret <= 0, err); + ON_ERR_GOTO(ret <= 0, free_evp_ctx); ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(evp_ctx->ctx, evp_ctx->evp_info->nid); - ON_ERR_GOTO(ret <= 0, err); + ON_ERR_GOTO(ret <= 0, free_evp_ctx); ret = EVP_PKEY_paramgen(evp_ctx->ctx, &evp_ctx->keyParam); - ON_ERR_GOTO(ret <= 0 || !evp_ctx->keyParam, err); + ON_ERR_GOTO(ret <= 0 || !evp_ctx->keyParam, free_evp_ctx); } // RSA bit length set only during keygen + goto err; + +free_evp_ctx: + EVP_PKEY_CTX_free(evp_ctx->ctx); err: return ret; @@ -867,12 +871,14 @@ void oqsx_key_free(OQSX_KEY *key) else if (key->keytype == KEY_TYPE_ECP_HYB_KEM || key->keytype == KEY_TYPE_ECX_HYB_KEM) { OQS_KEM_free(key->oqsx_provider_ctx.oqsx_qs_ctx.kem); + } else + OQS_SIG_free(key->oqsx_provider_ctx.oqsx_qs_ctx.sig); + EVP_PKEY_free(key->classical_pkey); + if (key->oqsx_provider_ctx.oqsx_evp_ctx) { EVP_PKEY_CTX_free(key->oqsx_provider_ctx.oqsx_evp_ctx->ctx); EVP_PKEY_free(key->oqsx_provider_ctx.oqsx_evp_ctx->keyParam); OPENSSL_free(key->oqsx_provider_ctx.oqsx_evp_ctx); - } else - OQS_SIG_free(key->oqsx_provider_ctx.oqsx_qs_ctx.sig); - OPENSSL_free(key->classical_pkey); + } #ifdef OQS_PROVIDER_NOATOMIC CRYPTO_THREAD_lock_free(key->lock); #endif @@ -1036,6 +1042,7 @@ static EVP_PKEY *oqsx_key_gen_evp_key(OQSX_EVP_CTX *ctx, unsigned char *pubkey, EVP_PKEY *ck2 = d2i_PrivateKey(ctx->evp_info->keytype, NULL, &privkey_enc2, privkeylen); ON_ERR_SET_GOTO(!ck2, ret, -14, errhyb); + EVP_PKEY_free(ck2); } ENCODE_UINT32(pubkey, pubkeylen); ENCODE_UINT32(privkey, privkeylen); @@ -1087,6 +1094,7 @@ int oqsx_key_gen(OQSX_KEY *key) ret = oqsx_key_gen_oqs(key, 0); } else { EVP_PKEY_free(pkey); + pkey = NULL; ret = oqsx_key_gen_oqs(key, 1); } } else if (key->keytype == KEY_TYPE_SIG) { diff --git a/test/oqs_test_endecode.c b/test/oqs_test_endecode.c index 55fa0f84..220f7d0b 100644 --- a/test/oqs_test_endecode.c +++ b/test/oqs_test_endecode.c @@ -78,8 +78,7 @@ static EVP_PKEY *oqstest_make_key(const char *type, EVP_PKEY *template, static int encode_EVP_PKEY_prov(const EVP_PKEY *pkey, const char *format, const char *structure, const char *pass, - const int selection, void **encoded, - long *encoded_len) + const int selection, BUF_MEM **encoded) { OSSL_ENCODER_CTX *ectx; BIO *mem_ser = NULL; @@ -110,8 +109,9 @@ static int encode_EVP_PKEY_prov(const EVP_PKEY *pkey, const char *format, goto end; /* pkey was successfully encoded into the bio */ - *encoded = mem_buf->data; - *encoded_len = mem_buf->length; + *encoded = BUF_MEM_new(); + (*encoded)->data = mem_buf->data; + (*encoded)->length = mem_buf->length; /* Detach the encoded output */ mem_buf->data = NULL; @@ -159,9 +159,9 @@ static int decode_EVP_PKEY_prov(const char *input_type, const char *structure, pkey = NULL; end: - EVP_PKEY_free(pkey); BIO_free(encoded_bio); OSSL_DECODER_CTX_free(dctx); + EVP_PKEY_free(pkey); return ok; } @@ -169,40 +169,45 @@ static int test_oqs_encdec(const char *sigalg_name) { EVP_PKEY *pkey = NULL; EVP_PKEY *decoded_pkey = NULL; - void *encoded = NULL; - long encoded_len = 0; + BUF_MEM *encoded = NULL; size_t i; int ok = 0; for (i = 0; i < nelem(test_params_list); i++) { - pkey = oqstest_make_key(sigalg_name, NULL, NULL); if (pkey == NULL) goto end; - if (!encode_EVP_PKEY_prov( - pkey, test_params_list[i].format, test_params_list[i].structure, - test_params_list[i].pass, test_params_list[i].selection, - &encoded, &encoded_len)) { + if (!encode_EVP_PKEY_prov(pkey, test_params_list[i].format, + test_params_list[i].structure, + test_params_list[i].pass, + test_params_list[i].selection, &encoded)) { printf("Failed encoding %s", sigalg_name); goto end; } if (!decode_EVP_PKEY_prov( test_params_list[i].format, test_params_list[i].structure, test_params_list[i].pass, test_params_list[i].keytype, - test_params_list[i].selection, &decoded_pkey, encoded, - encoded_len)) { + test_params_list[i].selection, &decoded_pkey, encoded->data, + encoded->length)) { printf("Failed decoding %s", sigalg_name); goto end; } if (EVP_PKEY_eq(pkey, decoded_pkey) != 1) goto end; + EVP_PKEY_free(pkey); + pkey = NULL; + EVP_PKEY_free(decoded_pkey); + decoded_pkey = NULL; + BUF_MEM_free(encoded); + encoded = NULL; } ok = 1; end: EVP_PKEY_free(pkey); EVP_PKEY_free(decoded_pkey); + BUF_MEM_free(encoded); return ok; } @@ -252,11 +257,11 @@ int main(int argc, char *argv[]) errcnt++; } - OSSL_LIB_CTX_free(libctx); OSSL_PROVIDER_unload(dfltprov); OSSL_PROVIDER_unload(keyprov); if (OPENSSL_VERSION_PREREQ(3, 1)) OSSL_PROVIDER_unload(oqsprov); // avoid crash in 3.0.x + OSSL_LIB_CTX_free(libctx); OSSL_LIB_CTX_free(keyctx); TEST_ASSERT(errcnt == 0) diff --git a/test/oqs_test_kems.c b/test/oqs_test_kems.c index 28179e8c..4b734aa9 100644 --- a/test/oqs_test_kems.c +++ b/test/oqs_test_kems.c @@ -15,7 +15,9 @@ static int test_oqs_kems(const char *kemalg_name) EVP_MD_CTX *mdctx = NULL; EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *key = NULL; - unsigned char *out, *secenc, *secdec; + unsigned char *out = NULL; + unsigned char *secenc = NULL; + unsigned char *secdec = NULL; size_t outlen, seclen; int testresult = 1; @@ -34,7 +36,7 @@ static int test_oqs_kems(const char *kemalg_name) if (!testresult) goto err; - OPENSSL_free(ctx); + EVP_PKEY_CTX_free(ctx); ctx = NULL; testresult @@ -64,7 +66,10 @@ static int test_oqs_kems(const char *kemalg_name) err: EVP_PKEY_free(key); - OPENSSL_free(ctx); + EVP_PKEY_CTX_free(ctx); + OPENSSL_free(out); + OPENSSL_free(secenc); + OPENSSL_free(secdec); return testresult; } diff --git a/test/oqs_test_signatures.c b/test/oqs_test_signatures.c index 5aaf4dba..b1137839 100644 --- a/test/oqs_test_signatures.c +++ b/test/oqs_test_signatures.c @@ -57,7 +57,7 @@ static int test_oqs_signatures(const char *sigalg_name) EVP_MD_CTX_free(mdctx); EVP_PKEY_free(key); - OPENSSL_free(ctx); + EVP_PKEY_CTX_free(ctx); OPENSSL_free(sig); mdctx = NULL; key = NULL; @@ -84,7 +84,7 @@ static int test_oqs_signatures(const char *sigalg_name) EVP_MD_CTX_free(mdctx); EVP_PKEY_free(key); - OPENSSL_free(ctx); + EVP_PKEY_CTX_free(ctx); OPENSSL_free(sig); return testresult; }