From d5839115e2e72fa2d2be8499d7dccef6ff37992a Mon Sep 17 00:00:00 2001 From: Drew Rife Date: Sun, 11 Oct 2026 18:11:32 +0000 Subject: [PATCH] fix: prevent invalid variant visit dispatch --- include/etl/infinite_loop.h | 3 ++- include/etl/private/variant_variadic.h | 22 ++++++++++++++--- test/test_variant_variadic.cpp | 34 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/include/etl/infinite_loop.h b/include/etl/infinite_loop.h index 530e9b4c..4c91255d 100644 --- a/include/etl/infinite_loop.h +++ b/include/etl/infinite_loop.h @@ -55,7 +55,8 @@ namespace etl #elif defined(ETL_COMPILER_MICROSOFT) _ReadWriteBarrier(); #else - #error "Infinite loop not supported for this compiler and platform" + volatile bool prevent_optimisation = true; + static_cast(prevent_optimisation); #endif #endif } diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index 4161aa31..86c4a293 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -33,6 +33,7 @@ SOFTWARE. #include "../compare.h" #include "../error_handler.h" #include "../exception.h" +#include "../infinite_loop.h" #include "../initializer_list.h" #include "../integral_limits.h" #include "../largest.h" @@ -473,6 +474,17 @@ namespace etl namespace private_variant { + //*************************************************************************** + /// Reports an invalid visit and prevents execution from continuing if the + /// configured error policy returns or runtime checks are disabled. + //*************************************************************************** + ETL_NORETURN + inline void invalid_visit() + { + ETL_ASSERT_FAIL(ETL_ERROR(bad_variant_access)); + etl::infinite_loop(); + } + //*************************************************************************** /// variant_base for non-trivially destructible types. /// Uses uninitialized_buffer (char array) storage and switch-based @@ -2188,7 +2200,6 @@ namespace etl //*************************************************************************** /// Makes a call to TCallable using tIndex alternative to the variant. - /// Instantiated as function pointer in the `do_visit` function. //*************************************************************************** template constexpr TRet do_visit_single(TCallable&& f, TVariant&& v) @@ -2219,14 +2230,19 @@ namespace etl template static ETL_CONSTEXPR14 TRet do_visit(TCallable&& f, TVariant&& v, index_sequence, TVarRest&&... variants) { - ETL_ASSERT(!v.valueless_by_exception(), ETL_ERROR(bad_variant_access)); + const size_t index = v.index(); + + if (index >= sizeof...(tIndices)) ETL_UNLIKELY + { + invalid_visit(); + } using helper_t = do_visit_helper; using func_ptr = typename helper_t::function_pointer; constexpr func_ptr jmp_table[]{helper_t::template fptr()...}; - return jmp_table[v.index()](static_cast(f), static_cast(v), static_cast(variants)...); + return jmp_table[index](static_cast(f), static_cast(v), static_cast(variants)...); } template diff --git a/test/test_variant_variadic.cpp b/test/test_variant_variadic.cpp index 8e81a606..a5701fd0 100644 --- a/test/test_variant_variadic.cpp +++ b/test/test_variant_variadic.cpp @@ -2548,6 +2548,31 @@ namespace CHECK_EQUAL(70, type); } + //************************************************************************* + TEST(test_variant_visit_dispatches_every_alternative) + { + etl::variant variant; + variant_test_visit_dispatcher visitor; + + variant = int8_t{}; + CHECK_EQUAL(5, etl::visit(visitor, variant)); + + variant = uint8_t{}; + CHECK_EQUAL(6, etl::visit(visitor, variant)); + + variant = int16_t{}; + CHECK_EQUAL(7, etl::visit(visitor, variant)); + } + + //************************************************************************* + TEST(test_variant_visit_rejects_valueless_variant) + { + etl::variant variant(etl::private_variant::valueless_t{}); + variant_test_visit_dispatcher visitor; + + CHECK_THROW(etl::visit(visitor, variant), etl::bad_variant_access); + } + //************************************************************************* struct test_variant_multiple_visit_helper { @@ -2590,6 +2615,15 @@ namespace CHECK_EQUAL(32, res); } + //************************************************************************* + TEST(test_variant_multiple_visit_rejects_valueless_later_variant) + { + etl::variant variant1; + etl::variant variant2(etl::private_variant::valueless_t{}); + + CHECK_THROW(etl::visit(test_variant_multiple_visit_helper{}, variant1, variant2), etl::bad_variant_access); + } + #if ETL_USING_CPP14 //************************************************************************* TEST(test_variant_multiple_visit_auto_return)