File 1409-crypto-Fix-memory-leak-in-crypto-engine_load.patch of Package erlang
From 2a4a01a7ab211584f45fcab8bd8e17763d1503eb Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Tue, 3 Mar 2026 18:49:40 +0100
Subject: [PATCH] crypto: Fix memory leak in crypto:engine_load
If it fails with a key without value in the middle of the command list.
---
lib/crypto/c_src/engine.c | 49 +++++++++++++++++---------------
lib/crypto/test/engine_SUITE.erl | 3 ++
2 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c
index d1db9f860a..20cc2cd74c 100644
--- a/lib/crypto/c_src/engine.c
+++ b/lib/crypto/c_src/engine.c
@@ -152,7 +152,8 @@ void destroy_engine_mutex(ErlNifEnv *env) {
}
#ifdef HAS_ENGINE_SUPPORT
-static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds, int i)
+static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, char **cmds,
+ unsigned int *cmds_top)
{
ERL_NIF_TERM head, tail;
const ERL_NIF_TERM *tmp_tuple;
@@ -161,7 +162,7 @@ static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, cha
char *tuple1 = NULL, *tuple2 = NULL;
if (enif_is_empty_list(env, term)) {
- cmds[i] = NULL;
+ cmds[*cmds_top] = NULL;
return 0;
}
@@ -179,30 +180,26 @@ static int get_engine_load_cmd_list(ErlNifEnv* env, const ERL_NIF_TERM term, cha
(void) memcpy(tuple1, tmpbin.data, tmpbin.size);
tuple1[tmpbin.size] = '\0';
- cmds[i] = tuple1;
- i++;
+ cmds[*cmds_top] = tuple1;
+ (*cmds_top)++;
if (!enif_inspect_binary(env, tmp_tuple[1], &tmpbin))
goto err;
if (tmpbin.size == 0) {
- cmds[i] = NULL;
+ tuple2 = NULL;
} else {
if ((tuple2 = enif_alloc(tmpbin.size + 1)) == NULL)
goto err;
(void) memcpy(tuple2, tmpbin.data, tmpbin.size);
tuple2[tmpbin.size] = '\0';
- cmds[i] = tuple2;
}
- i++;
- return get_engine_load_cmd_list(env, tail, cmds, i);
+ cmds[*cmds_top] = tuple2;
+ (*cmds_top)++;
+
+ return get_engine_load_cmd_list(env, tail, cmds, cmds_top);
err:
- if (tuple1 != NULL) {
- i--;
- enif_free(tuple1);
- }
- cmds[i] = NULL;
return -1;
}
@@ -490,11 +487,11 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF
#ifdef HAS_ENGINE_SUPPORT
ERL_NIF_TERM ret;
unsigned int cmds_len = 0;
+ unsigned int cmds_top = 0;
char **cmds = NULL;
struct engine_ctx *ctx;
unsigned int i;
int optional = 0;
- int cmds_loaded = 0;
/* Get Arguments */
ASSERT(argc == 3);
@@ -505,6 +502,10 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF
goto bad_arg;
PRINTF_ERR1("Engine Id: %s\r\n", ENGINE_get_id(ctx->engine));
+
+ if (!enif_get_int(env, argv[2], &optional))
+ goto err;
+
/* Command List */
if (!enif_get_list_length(env, argv[1], &cmds_len))
goto bad_arg;
@@ -517,19 +518,20 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF
goto err;
if ((cmds = enif_alloc((cmds_len + 1) * sizeof(char*))) == NULL)
goto err;
- if (get_engine_load_cmd_list(env, argv[1], cmds, 0))
- goto err;
- cmds_loaded = 1;
- if (!enif_get_int(env, argv[2], &optional))
+
+ cmds_top = 0;
+ if (get_engine_load_cmd_list(env, argv[1], cmds, &cmds_top))
goto err;
+ ASSERT(cmds_top == cmds_len);
for(i = 0; i < cmds_len; i+=2) {
+ ASSERT(cmds[i] != NULL);
PRINTF_ERR2("Cmd: %s:%s\r\n",
- cmds[i] ? cmds[i] : "(NULL)",
- cmds[i+1] ? cmds[i+1] : "(NULL)");
+ cmds[i],
+ cmds[i+1] ? cmds[i+1] : "(NULL)");
if(!ENGINE_ctrl_cmd_string(ctx->engine, cmds[i], cmds[i+1], optional)) {
PRINTF_ERR2("Command failed: %s:%s\r\n",
- cmds[i] ? cmds[i] : "(NULL)",
+ cmds[i],
cmds[i+1] ? cmds[i+1] : "(NULL)");
goto cmd_failed;
}
@@ -546,9 +548,10 @@ ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const ERL_NIF
ret = ERROR_Atom(env, "ctrl_cmd_failed");
done:
- if (cmds_loaded) {
- for (i = 0; cmds != NULL && cmds[i] != NULL; i++)
+ for (i = 0; i < cmds_top; i++) {
+ if (cmds[i]) {
enif_free(cmds[i]);
+ }
}
if (cmds != NULL)
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index 78ae351179..30866ede56 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -459,6 +459,7 @@ bad_arguments(Config) when is_list(Config) ->
error:badarg ->
ok
end
+
catch
error:notsup ->
{skip, "Engine not supported on this SSL version"};
@@ -515,6 +516,8 @@ pre_command_fail_bad_key(Config) when is_list(Config) ->
{ok, Engine} ->
case crypto:engine_load(<<"dynamic">>,
[{<<"SO_WRONG_PATH">>, Engine},
+ %% OTP-20014: Provoke memory leak
+ <<"key without value in middle of list">>,
{<<"ID">>, <<"MD5">>},
<<"LOAD">>],
[]) of
--
2.51.0