From 8222f251bbffefbe4baa471999e4df156824112e Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Mon, 25 Nov 2024 23:04:25 +0100 Subject: [PATCH 1/7] Remove macos-12 from CI as it is deprecated Signed-off-by: Paul Guyot --- .github/workflows/build-and-test-macos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test-macos.yaml b/.github/workflows/build-and-test-macos.yaml index 38993a3b4..986e71de7 100644 --- a/.github/workflows/build-and-test-macos.yaml +++ b/.github/workflows/build-and-test-macos.yaml @@ -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: From d6fc7837e2624a0683b1db821b7e47380db0778e Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Thu, 28 Nov 2024 00:48:01 +0100 Subject: [PATCH 2/7] alisp: fix: allow variables with any kind of value There was an issue while making use of lambdas as variable value, the problem was likely with anything else than binaries and integers. Signed-off-by: Davide Bettio --- CHANGELOG.md | 1 + libs/alisp/src/alisp.erl | 8 +++----- tests/libs/alisp/test_alisp.erl | 9 +++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b588dda..9ceeb0cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ 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. ## [0.6.5] - 2024-10-15 diff --git a/libs/alisp/src/alisp.erl b/libs/alisp/src/alisp.erl index d20e6c285..307cce9db 100644 --- a/libs/alisp/src/alisp.erl +++ b/libs/alisp/src/alisp.erl @@ -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); @@ -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); diff --git a/tests/libs/alisp/test_alisp.erl b/tests/libs/alisp/test_alisp.erl index 02d90e060..a241bdbf1 100644 --- a/tests/libs/alisp/test_alisp.erl +++ b/tests/libs/alisp/test_alisp.erl @@ -30,6 +30,7 @@ test() -> test_snippet0() -> ?ASSERT_MATCH(alisp:run(snippet0()), 25), + ?ASSERT_MATCH(alisp:run(snippet1()), 1), ok. snippet0() -> @@ -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". From 3aa0a928004fe74fd41a6627f28bb341e0d318f9 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 30 Nov 2024 00:11:20 +0100 Subject: [PATCH 3/7] Fix corruption when using > 16 x regs with GC safe reg write WRITE_REGISTER_GC_SAFE was using a wrong heuristic when dealing with x registers having index >= 16. Signed-off-by: Davide Bettio --- CHANGELOG.md | 2 + src/libAtomVM/opcodesswitch.h | 36 ++++++++++--- tests/erlang_tests/CMakeLists.txt | 2 + tests/erlang_tests/gc_safe_x_reg_write.erl | 59 ++++++++++++++++++++++ tests/test.c | 1 + 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 tests/erlang_tests/gc_safe_x_reg_write.erl diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ceeb0cd2..74209000a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ might lead to a crash in certain situations. - 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. +- 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 diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index f660968bc..0c2351740 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -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; @@ -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) \ { \ @@ -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) \ { \ @@ -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; \ } \ @@ -800,6 +818,8 @@ 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)++; \ @@ -807,7 +827,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live) 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: \ @@ -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(); \ } \ diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index d7a0eacf9..9623736a2 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -505,6 +505,7 @@ 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) @@ -981,6 +982,7 @@ 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 diff --git a/tests/erlang_tests/gc_safe_x_reg_write.erl b/tests/erlang_tests/gc_safe_x_reg_write.erl new file mode 100644 index 000000000..f3a71d6e4 --- /dev/null +++ b/tests/erlang_tests/gc_safe_x_reg_write.erl @@ -0,0 +1,59 @@ +% +% This file is part of AtomVM. +% +% Copyright 2024 Jakub Gonet +% Copyright 2024 Davide Bettio +% +% 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). diff --git a/tests/test.c b/tests/test.c index 0722f8410..0a3f372d8 100644 --- a/tests/test.c +++ b/tests/test.c @@ -564,6 +564,7 @@ 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), From 519194e003f897ca3d6c47e652fce1fa1a5082cf Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 2 Dec 2024 00:42:42 +0100 Subject: [PATCH 4/7] opcodesswitch.h: minor optimization for OP_PUT_TUPLE2 No need to use GC safe write since no memory allocation is performed. Signed-off-by: Davide Bettio --- src/libAtomVM/opcodesswitch.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index 0c2351740..631ad8066 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -6230,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) @@ -6257,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; } From 549063f4209c3577f255b9348a017c67ce2fb857 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sat, 7 Dec 2024 23:14:36 +0100 Subject: [PATCH 5/7] Remove unnecessary argc check Signed-off-by: Paul Guyot --- src/libAtomVM/nifs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index a4fa6804f..f5c70ce24 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -3003,9 +3003,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)) { From a01dce5524b2dd589baa0b0a23a5c820f5545717 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Mon, 9 Dec 2024 22:38:39 +0100 Subject: [PATCH 6/7] Fix destruction of SSL-related resources Ensure that ctr_drbg resource is held by ssl context, fixing dependency of resources and destructor chain Signed-off-by: Paul Guyot --- CHANGELOG.md | 1 + src/libAtomVM/otp_ssl.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ceeb0cd2..ea7edaca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ might lead to a crash in certain situations. - 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 ## [0.6.5] - 2024-10-15 diff --git a/src/libAtomVM/otp_ssl.c b/src/libAtomVM/otp_ssl.c index c48f50b8a..4f247786c 100644 --- a/src/libAtomVM/otp_ssl.c +++ b/src/libAtomVM/otp_ssl.c @@ -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); @@ -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); @@ -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 = { @@ -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; From 771df7c523eead0130e3bc3e7a2ecd1ff56f664d Mon Sep 17 00:00:00 2001 From: Peter M Date: Sun, 8 Dec 2024 19:58:22 +0100 Subject: [PATCH 7/7] Fix esp-idf P4 >= 5.3.2 builds Fixes https://github.com/atomvm/AtomVM/issues/1387 Signed-off-by: Peter M --- .../esp32/components/avm_builtins/gpio_driver.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/platforms/esp32/components/avm_builtins/gpio_driver.c b/src/platforms/esp32/components/avm_builtins/gpio_driver.c index 17ec8f69c..d745535e6 100644 --- a/src/platforms/esp32/components/avm_builtins/gpio_driver.c +++ b/src/platforms/esp32/components/avm_builtins/gpio_driver.c @@ -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); @@ -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 }; @@ -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;