diff --git a/CHANGELOG.md b/CHANGELOG.md index 98f8793de..a837017cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,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 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/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index f660968bc..631ad8066 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(); \ } \ @@ -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) @@ -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; } 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; 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; diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index 383f6c793..d453167e2 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -506,6 +506,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) @@ -985,6 +987,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 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/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". diff --git a/tests/test.c b/tests/test.c index ffd92a3e8..5fa7d589b 100644 --- a/tests/test.c +++ b/tests/test.c @@ -565,6 +565,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),