fix: prevent invalid variant visit dispatch

This commit is contained in:
Drew Rife 2026-10-11 18:11:32 +00:00
parent a8ab0bbeea
commit d5839115e2
3 changed files with 55 additions and 4 deletions

View File

@ -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<void>(prevent_optimisation);
#endif
#endif
}

View File

@ -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 <typename TRet, typename TCallable, typename TVariant, size_t tIndex>
constexpr TRet do_visit_single(TCallable&& f, TVariant&& v)
@ -2219,14 +2230,19 @@ namespace etl
template <typename TRet, typename TCallable, typename TVariant, size_t... tIndices, typename... TVarRest>
static ETL_CONSTEXPR14 TRet do_visit(TCallable&& f, TVariant&& v, index_sequence<tIndices...>, 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<TRet, TCallable, TVariant, TVarRest...>;
using func_ptr = typename helper_t::function_pointer;
constexpr func_ptr jmp_table[]{helper_t::template fptr<tIndices>()...};
return jmp_table[v.index()](static_cast<TCallable&&>(f), static_cast<TVariant&&>(v), static_cast<TVarRest&&>(variants)...);
return jmp_table[index](static_cast<TCallable&&>(f), static_cast<TVariant&&>(v), static_cast<TVarRest&&>(variants)...);
}
template <typename TRet, typename TCallable, typename TVariant, typename... TVs>

View File

@ -2548,6 +2548,31 @@ namespace
CHECK_EQUAL(70, type);
}
//*************************************************************************
TEST(test_variant_visit_dispatches_every_alternative)
{
etl::variant<int8_t, uint8_t, int16_t> 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<int8_t, uint8_t> 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<int8_t, uint8_t> variant1;
etl::variant<int8_t, uint16_t, uint8_t> variant2(etl::private_variant::valueless_t{});
CHECK_THROW(etl::visit<int16_t>(test_variant_multiple_visit_helper{}, variant1, variant2), etl::bad_variant_access);
}
#if ETL_USING_CPP14
//*************************************************************************
TEST(test_variant_multiple_visit_auto_return)