Skip to content

fix: disable zero-size-array min-size trick by default to eliminate UBSan violations (#249)#688

Merged
kris-jusiak merged 1 commit into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-249-ubsan-min-size-default
May 27, 2026
Merged

fix: disable zero-size-array min-size trick by default to eliminate UBSan violations (#249)#688
kris-jusiak merged 1 commit into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-249-ubsan-min-size-default

Conversation

@PavelGuzenfeld
Copy link
Copy Markdown
Contributor

@PavelGuzenfeld PavelGuzenfeld commented May 27, 2026

Problem

Issue #249: Clang and GCC UBSan report 30+ "insufficient object size" runtime errors at -O2 (and higher) for any SM that uses lambda guards or actions. The root cause is the BOOST_SML_DETAIL_ZERO_SIZE_ARRAY zero-length array extension used inside pool and wrapper types.

At optimisation level -O2+, the compiler sees that every struct containing a zero-length array member has zero usable bytes and may legally place multiple such objects at the same or overlapping memory addresses. UBSan then catches every dereference as an insufficient-size access.

Reproducer (any TCP-like FSM with lambda guards/actions):

# 30+ runtime errors like:
test/ft/issue_249_ubsan.cpp:39:5: runtime error: member access within address 0x… which
  does not point to an object of type 'pool<…>'
SUMMARY: UBSan: undefined-behavior …

The existing workaround — -DBOOST_SML_CFG_DISABLE_MIN_SIZE — has been known since the issue was filed in 2020 but forces users to discover and apply it manually.

Fix

Flip the default for Clang and GCC: make the static_assert(true) branch (no zero-length array) the default, matching what MSVC and IAR already do.

Flag Behaviour
(neither) default — no zero-length arrays, no UBSan violations
BOOST_SML_CFG_ENABLE_MIN_SIZE new — opt-in to the zero-size-array trick for minimal object sizes
BOOST_SML_CFG_DISABLE_MIN_SIZE legacy — backward-compat alias; same as the new default; takes priority over ENABLE_MIN_SIZE

Changes

  • include/boost/sml.hpp: In the Clang and GCC blocks, change the condition on BOOST_SML_DETAIL_ZERO_SIZE_ARRAY from
    #if !defined(BOOST_SML_CFG_DISABLE_MIN_SIZE)#if defined(BOOST_SML_CFG_ENABLE_MIN_SIZE) && !defined(BOOST_SML_CFG_DISABLE_MIN_SIZE)
    with a brief comment explaining the rationale.

  • test/ft/sizeof.cpp: All size assertions assume objects are zero-sized, which requires the min-size trick. Guard the entire file with
    #if !defined(_MSC_VER) && defined(BOOST_SML_CFG_ENABLE_MIN_SIZE).

  • test/ft/issue_249_ubsan.cpp: New regression test — a 4-state TCP-like FSM with lambda guards and actions. Includes // cppcheck-suppress missingIncludeSystem to suppress the Codacy cppcheck false positive (the cloud runner invokes cppcheck per-file without include paths). Verified UBSan-clean with
    clang-18 -std=c++17 -O2 -fsanitize=undefined,address and
    clang-18 -std=c++20 -O2 -fsanitize=undefined,address.

  • example/hello_world.cpp: Remove static_assert(1 == sizeof(sm)). The assertion documented a size property that depended on the zero-size-array trick; it was never an API guarantee. The example demonstrates correctness of transitions, guards, and actions — not object size.

Verification

Clang UBSan (full ft suite, C++17 and C++20 at -O2)

PASS=29 FAIL=0  total UBSan runtime errors=0   # C++17
PASS=29 FAIL=0  total UBSan runtime errors=0   # C++20

GCC 14 (full suite including examples, C++20)

56/56 tests passed

MSVC 19.51.36244 (C++20)

=== Results: PASS=34 FAIL=0 ===

Relation

Companion CI config in PR #689 (can be merged independently in either order).

Closes #249.

@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-249-ubsan-min-size-default branch 2 times, most recently from 692ddb8 to da3cbf5 Compare May 27, 2026 10:48
Comment thread example/hello_world.cpp
using namespace sml;

sm<hello_world> sm;
static_assert(1 == sizeof(sm), "sizeof(sm) != 1b");
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed due to UB.

@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-249-ubsan-min-size-default branch 2 times, most recently from e8652d5 to d98b315 Compare May 27, 2026 11:14
…BSan violations (issue boost-ext#249)

The __BOOST_SML_ZERO_SIZE_ARRAY zero-length array extension placed
multiple SM objects at overlapping addresses at -O2+, triggering
30+ UBSan "insufficient object size" errors on every SM that uses
lambda guards or actions.

MSVC and IAR already used static_assert(true) (i.e. no zero-size
array) unconditionally.  Clang and GCC now do the same by default.

New opt-in flag: BOOST_SML_CFG_ENABLE_MIN_SIZE
  - Restores the old zero-size-array behaviour for users who need
    minimal SM object sizes and have verified UBSan clean-ness.

Old opt-out flag: BOOST_SML_CFG_DISABLE_MIN_SIZE
  - Continues to work for backward compatibility; it takes priority
    over BOOST_SML_CFG_ENABLE_MIN_SIZE.

test/ft/sizeof.cpp: all size assertions depend on the zero-size-array
trick, so the entire file is now guarded by
  #if !defined(_MSC_VER) && defined(BOOST_SML_CFG_ENABLE_MIN_SIZE)

test/ft/issue_249_ubsan.cpp: new regression test that exercises
lambda guards and actions through a small TCP-like FSM at -O2.
Verified clean with clang-18 -fsanitize=undefined,address on both
C++17 and C++20.

example/hello_world.cpp: remove static_assert(1 == sizeof(sm)).
That assertion documented a size property that relied on the
zero-size-array trick; it was never an API guarantee.
PavelGuzenfeld added a commit to PavelGuzenfeld/sml that referenced this pull request May 27, 2026
.codacy.yml: declares C++ language for Codacy's cppcheck engine.

cppcheck-project.xml: provides include search paths so cppcheck can
resolve <boost/sml.hpp> without missingInclude warnings during local
runs with --project=cppcheck-project.xml.

References fix/issue-249-ubsan-min-size-default (PR boost-ext#688).
@PavelGuzenfeld PavelGuzenfeld force-pushed the fix/issue-249-ubsan-min-size-default branch from 197742e to a66c30c Compare May 27, 2026 11:23
@kris-jusiak kris-jusiak merged commit 584b207 into boost-ext:master May 27, 2026
5 checks passed
kris-jusiak pushed a commit that referenced this pull request May 27, 2026
.codacy.yml: declares C++ language for Codacy's cppcheck engine.

cppcheck-project.xml: provides include search paths so cppcheck can
resolve <boost/sml.hpp> without missingInclude warnings during local
runs with --project=cppcheck-project.xml.

References fix/issue-249-ubsan-min-size-default (PR #688).
PavelGuzenfeld added a commit to PavelGuzenfeld/sml that referenced this pull request May 28, 2026
PR boost-ext#688 introduced BOOST_SML_DETAIL_ZERO_SIZE_ARRAY expanding to
static_assert(true) (no message) on Clang, GCC, MSVC, and IAR.
The two-argument form static_assert(expr, msg) is C++11; the
one-argument form static_assert(expr) is C++17.  With C++14 and
-Wc++17-extensions/-Werror this produces a build error.

Fix: add an empty message string in all four compiler branches.
PavelGuzenfeld added a commit to PavelGuzenfeld/sml that referenced this pull request May 28, 2026
PR boost-ext#688 introduced BOOST_SML_DETAIL_ZERO_SIZE_ARRAY expanding to
static_assert(true) (no message) on Clang, GCC, MSVC, and IAR.
The two-argument form static_assert(expr, msg) is C++11; the
one-argument form static_assert(expr) is C++17.  With C++14 and
-Wc++17-extensions/-Werror this produces a build error.

Fix: add an empty message string in all four compiler branches.
PavelGuzenfeld added a commit to PavelGuzenfeld/sml that referenced this pull request May 28, 2026
PR boost-ext#688 introduced BOOST_SML_DETAIL_ZERO_SIZE_ARRAY expanding to
static_assert(true) (no message) on Clang, GCC, MSVC, and IAR.
The two-argument form static_assert(expr, msg) is C++11; the
one-argument form static_assert(expr) is C++17.  With C++14 and
-Wc++17-extensions/-Werror this produces a build error.

Fix: add an empty message string in all four compiler branches.
kris-jusiak pushed a commit that referenced this pull request May 28, 2026
PR #688 introduced BOOST_SML_DETAIL_ZERO_SIZE_ARRAY expanding to
static_assert(true) (no message) on Clang, GCC, MSVC, and IAR.
The two-argument form static_assert(expr, msg) is C++11; the
one-argument form static_assert(expr) is C++17.  With C++14 and
-Wc++17-extensions/-Werror this produces a build error.

Fix: add an empty message string in all four compiler branches.
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.

Clang UBSan Violation (Insufficient object size) when compiled with -O2 or above

2 participants