Skip to content

Commit

Permalink
Merge branch 'main' into feature/distributed-erlang
Browse files Browse the repository at this point in the history
Merge a number of fixes, including "fix destruction of SSL-related resources".
  • Loading branch information
bettio committed Dec 13, 2024
2 parents 652e677 + edbe90c commit 29fe0fa
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test-macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["macos-12", "macos-13", "macos-14", "macos-15"]
os: ["macos-13", "macos-14", "macos-15"]
otp: ["24", "25", "26", "27"]

steps:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ might lead to a crash in certain situations.
- Fixed several bugs in `http_server` (#1366)
- Fixed generic\_unix `socket_driver` to return `{gen_tcp, closed}` when socket is closed on Linux instead of `{gen_tcp, {recv, 104}}`
- Fixed a memory leak where modules were not properly destroyed when the global context is destroyd
- alisp: fix support to variables that are not binaries or integers.
- Fixed destruction of ssl-related resources
- Fix corruption when dealing with specific situations that involve more than 16 x registers when
certain VM instructions are used.

## [0.6.5] - 2024-10-15

Expand Down
8 changes: 3 additions & 5 deletions libs/alisp/src/alisp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ eval(S) when is_atom(S) ->
undefined -> throw({unbound, S});
Val -> Val
end;
eval(I) when is_integer(I) ->
I;
eval(B) when is_binary(B) ->
B;
eval(['do', Vars, [_TestExpr, _ReturnExpr] | _Exprs] = Do) ->
Restore = put_do_vars(Vars),
execute_do(Do, Restore);
Expand Down Expand Up @@ -68,7 +64,9 @@ eval([[symbol_pair, Module, Fn] | Args]) when is_atom(Module) andalso is_atom(Fn
fapply(Module, Fn, EvaluatedArgs);
eval([Fn | Args]) when is_atom(Fn) ->
EvaluatedArgs = eval_args(Args),
func_eval(Fn, EvaluatedArgs).
func_eval(Fn, EvaluatedArgs);
eval(NotList) when not is_list(NotList) ->
NotList.

func_eval('funcall', [Lambda | Args]) ->
fapply(Lambda, Args);
Expand Down
4 changes: 1 addition & 3 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3018,9 +3018,7 @@ static term nif_erlang_binary_to_term(Context *ctx, int argc, term argv[])

static term nif_erlang_term_to_binary(Context *ctx, int argc, term argv[])
{
if (argc != 1) {
RAISE_ERROR(BADARG_ATOM);
}
UNUSED(argc);
term t = argv[0];
term ret = externalterm_to_binary(ctx, t);
if (term_is_invalid_term(ret)) {
Expand Down
42 changes: 31 additions & 11 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,23 @@ typedef struct
int index;
} dreg_gc_safe_t;

#define X_REG_FLAG 1

static inline term *with_x_reg_flag(term *xptr)
{
return (term *) (((uintptr_t) xptr) | X_REG_FLAG);
}

static inline bool has_x_reg_flag(term *xptr)
{
return ((uintptr_t) xptr) & X_REG_FLAG;
}

static inline term *to_x_reg_ptr(term *xptr)
{
return (term *) (((uintptr_t) xptr) & ~((uintptr_t) X_REG_FLAG));
}

static dreg_t extended_register_ptr(Context *ctx, unsigned int index)
{
struct ListHead *item;
Expand Down Expand Up @@ -549,7 +566,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)

// TODO: fix register type heuristics, there is a chance that 'y' might be an "extended" x reg
#define T_DEST_REG_GC_SAFE(dreg_gc_safe) \
((dreg).base == x_regs) ? 'x' : 'y', ((dreg).index)
(has_x_reg_flag((dreg).base) ? 'x' : 'y'), ((dreg).index)

#define DECODE_COMPACT_TERM(dest_term, decode_pc) \
{ \
Expand Down Expand Up @@ -737,8 +754,9 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
#define READ_DEST_REGISTER(dreg) *(dreg)

#define READ_DEST_REGISTER_GC_SAFE(dreg_gc_safe) \
((dreg_gc_safe).base == x_regs ? x_regs[(dreg_gc_safe).index] : ctx->e[(dreg_gc_safe).index])

(has_x_reg_flag((dreg_gc_safe).base) ? \
*to_x_reg_ptr((dreg_gc_safe).base) : \
ctx->e[(dreg_gc_safe).index])

#define WRITE_REGISTER(dreg, value) \
{ \
Expand All @@ -747,8 +765,8 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)

#define WRITE_REGISTER_GC_SAFE(dreg_gc_safe, value) \
{ \
if ((dreg_gc_safe).base == x_regs) { \
x_regs[(dreg_gc_safe).index] = value; \
if (has_x_reg_flag((dreg_gc_safe).base)) { \
*(to_x_reg_ptr((dreg_gc_safe).base)) = value; \
} else { \
ctx->e[(dreg_gc_safe).index] = value; \
} \
Expand Down Expand Up @@ -800,14 +818,16 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
} \
}

// NOTE: (dreg_gc_safe).index is initialized for x registers even if not used to avoid compiler
// warnings about unitialized variables (-maybe-uninitialized)
#define DECODE_DEST_REGISTER_GC_SAFE(dreg_gc_safe, decode_pc) \
{ \
uint8_t first_byte = *(decode_pc)++; \
uint8_t reg_type = first_byte & 0xF; \
uint8_t reg_index = (first_byte >> 4); \
switch (reg_type) { \
case COMPACT_XREG: \
(dreg_gc_safe).base = x_regs; \
(dreg_gc_safe).base = with_x_reg_flag(&x_regs[reg_index]); \
(dreg_gc_safe).index = reg_index; \
break; \
case COMPACT_YREG: \
Expand All @@ -821,8 +841,8 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
if (IS_NULL_PTR(reg_base)) { \
RAISE_ERROR(OUT_OF_MEMORY_ATOM); \
} \
(dreg_gc_safe).base = reg_base; \
(dreg_gc_safe).index = 0; \
(dreg_gc_safe).base = with_x_reg_flag(reg_base); \
(dreg_gc_safe).index = reg_index; \
} else { \
VM_ABORT(); \
} \
Expand Down Expand Up @@ -6210,8 +6230,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)

#if MAXIMUM_OTP_COMPILER_VERSION >= 22
case OP_PUT_TUPLE2: {
GC_SAFE_DEST_REGISTER(dreg);
DECODE_DEST_REGISTER_GC_SAFE(dreg, pc);
DEST_REGISTER(dreg);
DECODE_DEST_REGISTER(dreg, pc);
DECODE_EXTENDED_LIST_TAG(pc);
uint32_t size;
DECODE_LITERAL(size, pc)
Expand All @@ -6237,7 +6257,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
}

#ifdef IMPL_EXECUTE_LOOP
WRITE_REGISTER_GC_SAFE(dreg, t);
WRITE_REGISTER(dreg, t);
#endif
break;
}
Expand Down
15 changes: 13 additions & 2 deletions src/libAtomVM/otp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ static void ctrdrbg_dtor(ErlNifEnv *caller_env, void *obj)
UNUSED(caller_env);

struct CtrDrbgResource *rsrc_obj = (struct CtrDrbgResource *) obj;
mbedtls_entropy_context *entropy_context = rsrc_obj->context.MBEDTLS_PRIVATE(p_entropy);
// Release the drbg first
mbedtls_ctr_drbg_free(&rsrc_obj->context);
// Eventually release the entropy
mbedtls_entropy_context *entropy_context = rsrc_obj->context.MBEDTLS_PRIVATE(p_entropy);
if (entropy_context) {
struct EntropyContextResource *entropy_obj = CONTAINER_OF(entropy_context, struct EntropyContextResource, context);
struct RefcBinary *entropy_refc = refc_binary_from_data(entropy_obj);
Expand All @@ -124,10 +124,10 @@ static void sslcontext_dtor(ErlNifEnv *caller_env, void *obj)
UNUSED(caller_env);

struct SSLContextResource *rsrc_obj = (struct SSLContextResource *) obj;
const mbedtls_ssl_config *config = rsrc_obj->context.MBEDTLS_PRIVATE(conf);
// Free the context first
mbedtls_ssl_free(&rsrc_obj->context);
// Eventually release the config
const mbedtls_ssl_config *config = rsrc_obj->context.MBEDTLS_PRIVATE(conf);
if (config) {
struct SSLConfigResource *config_obj = CONTAINER_OF(config, struct SSLConfigResource, config);
struct RefcBinary *config_refc = refc_binary_from_data(config_obj);
Expand All @@ -141,7 +141,15 @@ static void sslconfig_dtor(ErlNifEnv *caller_env, void *obj)
UNUSED(caller_env);

struct SSLConfigResource *rsrc_obj = (struct SSLConfigResource *) obj;
const mbedtls_ctr_drbg_context *ctr_drbg_context = rsrc_obj->config.MBEDTLS_PRIVATE(p_rng);
mbedtls_ssl_config_free(&rsrc_obj->config);

// Eventually release the ctrdrbg
if (ctr_drbg_context) {
struct CtrDrbgResource *rng_obj = CONTAINER_OF(ctr_drbg_context, struct CtrDrbgResource, context);
struct RefcBinary *config_refc = refc_binary_from_data(rng_obj);
refc_binary_decrement_refcount(config_refc, caller_env->global);
}
}

static const ErlNifResourceTypeInit EntropyContextResourceTypeInit = {
Expand Down Expand Up @@ -476,6 +484,9 @@ static term nif_ssl_conf_rng(Context *ctx, int argc, term argv[])
}
struct CtrDrbgResource *ctr_drbg_obj = (struct CtrDrbgResource *) rsrc_obj_ptr;

struct RefcBinary *ctr_drbg_refc = refc_binary_from_data(ctr_drbg_obj);
refc_binary_increment_refcount(ctr_drbg_refc);

mbedtls_ssl_conf_rng(&conf_obj->config, mbedtls_ctr_drbg_random, &ctr_drbg_obj->context);

return OK_ATOM;
Expand Down
9 changes: 4 additions & 5 deletions src/platforms/esp32/components/avm_builtins/gpio_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ static term nif_gpio_hold_dis(Context *ctx, int argc, term argv[])
return hold_dis(argv[0]);
}

#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 2) && SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 2) && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP)
static term nif_gpio_deep_sleep_hold_en(Context *ctx, int argc, term argv[])
{
UNUSED(ctx);
Expand Down Expand Up @@ -769,9 +769,8 @@ static const struct Nif gpio_hold_dis_nif =
.nif_ptr = nif_gpio_hold_dis
};

#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
static const struct Nif gpio_deep_sleep_hold_en_nif =
{
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 2) && SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 2) && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP)
static const struct Nif gpio_deep_sleep_hold_en_nif = {
.base.type = NIFFunctionType,
.nif_ptr = nif_gpio_deep_sleep_hold_en
};
Expand Down Expand Up @@ -829,7 +828,7 @@ const struct Nif *gpio_nif_get_nif(const char *nifname)
return &gpio_hold_dis_nif;
}

#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 2) && SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 2) && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP)
if (strcmp("gpio:deep_sleep_hold_en/0", nifname) == 0 || strcmp("Elixir.GPIO:deep_sleep_hold_en/0", nifname) == 0) {
TRACE("Resolved platform nif %s ...\n", nifname);
return &gpio_deep_sleep_hold_en_nif;
Expand Down
4 changes: 4 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ compile_erlang(test_utf8_atoms)
compile_erlang(twentyone_param_function)
compile_erlang(complex_list_match_xregs)
compile_erlang(twentyone_param_fun)
compile_erlang(gc_safe_x_reg_write)

compile_erlang(test_fun_to_list)

compile_erlang(maps_nifs)
Expand Down Expand Up @@ -988,6 +990,8 @@ add_custom_target(erlang_test_modules DEPENDS
twentyone_param_function.beam
complex_list_match_xregs.beam
twentyone_param_fun.beam
gc_safe_x_reg_write.beam

test_fun_to_list.beam

maps_nifs.beam
Expand Down
59 changes: 59 additions & 0 deletions tests/erlang_tests/gc_safe_x_reg_write.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
%
% This file is part of AtomVM.
%
% Copyright 2024 Jakub Gonet <[email protected]>
% Copyright 2024 Davide Bettio <[email protected]>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

% This test has been made after https://github.com/atomvm/AtomVM/issues/1379

-module(gc_safe_x_reg_write).
-export([start/0]).
-export([f/0, check/2]).

start() ->
?MODULE:check(?MODULE:f(), 0) - 21.

f() ->
[
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0},
{f, fun f/0}
].

check([], Count) ->
Count;
check([{f, F} | T], Count) when is_function(F) ->
check(T, Count + 1).
9 changes: 9 additions & 0 deletions tests/libs/alisp/test_alisp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ test() ->

test_snippet0() ->
?ASSERT_MATCH(alisp:run(snippet0()), 25),
?ASSERT_MATCH(alisp:run(snippet1()), 1),
ok.

snippet0() ->
Expand Down Expand Up @@ -57,3 +58,11 @@ snippet0() ->
" \n"
" (length (primes 100))\n"
" )\n".

snippet1() ->
"\n"
" (progn\n"
" (setq mylambda (lambda (a b) (- a b)))\n"
" (funcall mylambda 1 2)\n"
" (funcall (lambda ()\n"
" (funcall mylambda 4 3))))\n".
2 changes: 2 additions & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ struct Test tests[] = {
TEST_CASE(twentyone_param_function),
TEST_CASE(complex_list_match_xregs),
TEST_CASE(twentyone_param_fun),
TEST_CASE(gc_safe_x_reg_write),

TEST_CASE(test_fun_to_list),
TEST_CASE(maps_nifs),

Expand Down

0 comments on commit 29fe0fa

Please sign in to comment.