Plug memory leaks in kx_ functions

If we, for some reason, fail to the error-path, then binaries allocated
are not being released properly back to the erlang runtime. Fix this
in the usual fashion by using a stack of undo-operations, and thread
them via goto.
This commit is contained in:
Jesper Louis Andersen 2020-01-21 13:01:48 +01:00
parent d850b16d1b
commit 899fbeefd3
2 changed files with 78 additions and 27 deletions

View File

@ -48,63 +48,104 @@ ERL_NIF_TERM enacl_crypto_kx_keypair(ErlNifEnv *env, int argc,
ERL_NIF_TERM
enacl_crypto_kx_server_session_keys(ErlNifEnv *env, int argc,
ERL_NIF_TERM const argv[]) {
ERL_NIF_TERM ret;
ErlNifBinary rx, tx, server_pk, server_sk, client_pk;
if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &server_pk)) ||
(!enif_inspect_binary(env, argv[1], &server_sk)) ||
(!enif_inspect_binary(env, argv[2], &client_pk)) ||
(server_pk.size != crypto_kx_PUBLICKEYBYTES) ||
(server_sk.size != crypto_kx_SECRETKEYBYTES) ||
(client_pk.size != crypto_kx_PUBLICKEYBYTES)) {
return enif_make_badarg(env);
}
if (argc != 3)
goto bad_arg;
if (!enif_inspect_binary(env, argv[0], &server_pk))
goto bad_arg;
if (!enif_inspect_binary(env, argv[1], &server_sk))
goto bad_arg;
if (!enif_inspect_binary(env, argv[2], &client_pk))
goto bad_arg;
if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
goto bad_arg;
if (server_sk.size != crypto_kx_SECRETKEYBYTES)
goto bad_arg;
if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
goto bad_arg;
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
return nacl_error_tuple(env, "alloc_failed");
ret = nacl_error_tuple(env, "alloc_failed");
goto done;
}
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
return nacl_error_tuple(env, "alloc_failed");
ret = nacl_error_tuple(env, "alloc_failed");
goto release_rx;
}
if (0 != crypto_kx_server_session_keys(rx.data, tx.data, server_pk.data,
server_sk.data, client_pk.data)) {
// suspicious client public key
return nacl_error_tuple(env, "invalid_client_public_key");
ret = nacl_error_tuple(env, "invalid_client_public_key");
goto release_tx;
}
return enif_make_tuple2(env, enif_make_binary(env, &rx),
ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
enif_make_binary(env, &tx));
goto done;
bad_arg:
return enif_make_badarg(env);
release_tx:
enif_release_binary(&tx);
release_rx:
enif_release_binary(&rx);
done:
return ret;
}
ERL_NIF_TERM
enacl_crypto_kx_client_session_keys(ErlNifEnv *env, int argc,
ERL_NIF_TERM const argv[]) {
ErlNifBinary rx, tx, client_pk, client_sk, server_pk;
ERL_NIF_TERM ret;
if ((argc != 3) || (!enif_inspect_binary(env, argv[0], &client_pk)) ||
(!enif_inspect_binary(env, argv[1], &client_sk)) ||
(!enif_inspect_binary(env, argv[2], &server_pk)) ||
(client_pk.size != crypto_kx_PUBLICKEYBYTES) ||
(client_sk.size != crypto_kx_SECRETKEYBYTES) ||
(server_pk.size != crypto_kx_PUBLICKEYBYTES)) {
return enif_make_badarg(env);
}
if (argc != 3)
goto bad_arg;
if (!enif_inspect_binary(env, argv[0], &client_pk))
goto bad_arg;
if (!enif_inspect_binary(env, argv[1], &client_sk))
goto bad_arg;
if (!enif_inspect_binary(env, argv[2], &server_pk))
goto bad_arg;
if (client_pk.size != crypto_kx_PUBLICKEYBYTES)
goto bad_arg;
if (client_sk.size != crypto_kx_SECRETKEYBYTES)
goto bad_arg;
if (server_pk.size != crypto_kx_PUBLICKEYBYTES)
goto bad_arg;
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &rx)) {
return nacl_error_tuple(env, "alloc_failed");
ret = nacl_error_tuple(env, "alloc_failed");
goto done;
}
if (!enif_alloc_binary(crypto_kx_SESSIONKEYBYTES, &tx)) {
return nacl_error_tuple(env, "alloc_failed");
ret = nacl_error_tuple(env, "alloc_failed");
goto release_rx;
}
if (0 != crypto_kx_client_session_keys(rx.data, tx.data, client_pk.data,
client_sk.data, server_pk.data)) {
// suspicious server public key
return nacl_error_tuple(env, "invalid_server_public_key");
ret = nacl_error_tuple(env, "invalid_server_public_key");
goto release_tx;
}
return enif_make_tuple2(env, enif_make_binary(env, &rx),
ret = enif_make_tuple2(env, enif_make_binary(env, &rx),
enif_make_binary(env, &tx));
goto done;
bad_arg:
return enif_make_badarg(env);
release_tx:
enif_release_binary(&tx);
release_rx:
enif_release_binary(&rx);
done:
return ret;
}

View File

@ -43,7 +43,8 @@ groups() ->
aead_xchacha20poly1305,
aead_chacha20poly1305,
pwhash,
sign]},
sign,
kx]},
[Neg, Pos].
@ -139,3 +140,12 @@ sign_chunked(S, M, N) ->
S2 = enacl:sign_update(S, M),
sign_chunked(S2, M, N-1).
kx(_Config) ->
#{ public := CPK, secret := CSK} = enacl:kx_keypair(),
#{ public := SPK, secret := SSK} = enacl:kx_keypair(),
#{ client_tx := CTX, client_rx := CRX} = enacl:kx_client_session_keys(CPK, CSK, SPK),
#{ server_tx := STX, server_rx := SRX} = enacl:kx_server_session_keys(SPK, SSK, CPK),
%% Verify we got a shared keypair
CTX = SRX,
STX = CRX,
ok.