Skip to content

fix null deref when pointer dependency passed as lvalue to sm ctor (#485)#673

Merged
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-485-const-ptr-dep-null-deref
May 24, 2026
Merged

fix null deref when pointer dependency passed as lvalue to sm ctor (#485)#673
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-485-const-ptr-dep-null-deref

Conversation

@PavelGuzenfeld
Copy link
Copy Markdown
Contributor

Problem

When a T* dependency is passed as an lvalue to sm<M>{ptr}, C++ forwarding reference deduction binds TDeps as T*& (reference-to-pointer). The pool init constructor then builds an input pool of type pool<T*&>.

The stripping logic in the pool init constructor:

try_get<remove_const_t<remove_reference_t<remove_pointer_t<Ts>>>>(&p)

strips T*T, then calls try_get<T> on pool<T*&>. The existing try_get overloads only match pool_type<T*> and pool_type<const T*> — there is no overload for pool_type<T*&>, so the call falls through to the variadic catch-all and returns missing_ctor_parameter<T>{}. That silently converts to a null pointer. Every action or guard that takes a pointer dependency then receives nullptr and dereferences it → crash / assertion failure.

Reproduction

struct Dep { int val = 42; };
struct M {
  auto operator()() noexcept {
    using namespace sml;
    // action takes const Dep* — was receiving nullptr
    auto action = [](const Dep* d) { assert(d != nullptr); };
    return make_transition_table(*"s1"_s + event<e1>{} / action = X);
  }
};
Dep dep;
Dep* ptr = &dep;          // lvalue pointer
sml::sm<M> sm{ptr};       // TDeps deduced as Dep*& — bug triggers here
sm.process_event(e1{});   // assertion fires

The same bug affects Dep* (non-const) action parameters.

Fix

Add two try_get overloads that match reference-to-pointer pool slots:

template <class T>
constexpr T *try_get(const pool_type<T *&> *object) {
  return object->value;
}
template <class T>
constexpr const T *try_get(const pool_type<const T *&> *object) {
  return object->value;
}

These extract the stored pointer from the reference-wrapping slot. The T& (reference dependency) case already worked correctly and is unaffected.

Tests

Regression tests added in test/ft/dependencies.cpp:

  • pointer_dep_lvalue_not_null — action takes Dep*, SM constructed with Dep* lvalue
  • const_pointer_dep_lvalue_not_null — action takes const Dep*, SM constructed with Dep* lvalue

Existing test suite passes without modification.

Fixes #485.

@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-485-const-ptr-dep-null-deref branch 2 times, most recently from 9f0d2e3 to 7c996d3 Compare May 23, 2026 22:16
…ssue boost-ext#485)

When a T* dependency is passed as an lvalue to sm<M>{ptr}, forwarding
reference deduction binds TDeps as T*& (reference-to-pointer).  The pool
init constructor then creates pool<T*&> as the input pool.  The stripping
logic:

  remove_const_t<remove_reference_t<remove_pointer_t<T*>>>

strips T* to T and calls try_get<T> on pool<T*&>.  The existing try_get
overloads only cover pool_type<T*> and pool_type<const T*>; there is no
overload for pool_type<T*&>, so the call falls through to the variadic
catch-all which returns missing_ctor_parameter<T>{}.  That converts to a
null T* (or null const T*), so every action/guard taking a pointer
dependency receives nullptr and dereferences it — crash.

Fix: add two try_get overloads that match reference-to-pointer types:

  template <class T> T*       try_get(const pool_type<T*&>*  object)
  template <class T> const T* try_get(const pool_type<const T*&>* object)

These extract the stored pointer value from the reference-wrapping pool slot.
The T& (reference) case was already handled correctly and is unaffected.

Regression tests added in test/ft/dependencies.cpp:
  pointer_dep_lvalue_not_null     — action takes Dep*,       sm{Dep* lvalue}
  const_pointer_dep_lvalue_not_null — action takes const Dep*, sm{Dep* lvalue}

Fixes boost-ext#485.
@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-485-const-ptr-dep-null-deref branch from 7c996d3 to c990fc4 Compare May 24, 2026 15:47
@kris-jusiak kris-jusiak merged commit 7207ac2 into boost-ext:master May 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash in guard when dependency referenced by const vs non-const

2 participants