beyond-all-reason / beyond-all-reason/RecoilEngine
EngineCrash [2026.06.08-RC] at [CBeamLaser::FireInternal] (bug report and gdb poke around assisted by LLM)
- Dominant language
- C++
- Stars
- 683
- Forks
- 293
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 40
Description
### Description
The engine crashes with `SIGABRT` due to a libstdc++ hardening assertion failure (`__glibcxx_assert_fail`) inside `CQuadField::GetQuadsOnRay`. A laser beam's length calculation yields an inverted negative distance value, which violates the downstream `std::clamp` invariant (`lo <= hi`).
### Environment
- **OS:** Linux (Arch Linux / CachyOS package build)
- **Engine Build:** `recoil-engine-rc` (`systemwide_managed_by_pkgmngr-2026.06.08-RC.local`) (build from the AUR PKGBUILD (full build instructions used are below)
- **Game:** Beyond All Reason
- **Toolchain State:** Built with GCC runtime assertions enabled (`-D_GLIBCXX_ASSERTIONS`)
### Build Instructions used
```
cmake3 -S "${srcdir}/${pkgname%-git}" -G Ninja \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O3 -g -DNDEBUG -fdiagnostics-color=always" \
-DCMAKE_C_FLAGS_RELWITHDEBINFO="-O3 -g -DNDEBUG -fdiagnostics-color=always" \
-DCMAKE_BUILD_TYPE=RELWITHDEBINFO \
-DAI_TYPES=NATIVE \
-DINSTALL_PORTABLE=ON \
-DCMAKE_USE_RELATIVE_PATHS:BOOL=1 \
-DBINDIR:PATH=./ \
-DLIBDIR:PATH=./ \
-DDATADIR:PATH=./ \
-DCMAKE_INSTALL_PREFIX="$(pwd)/install"
### Disabled in favor of using the settings from makepkg.conf
# -DCMAKE_TOOLCHAIN_FILE="toolchain/clang_x86_64-pc-linux-gnu.cmake" \
# V=1 make VERBOSE=1 -j1
cd "${srcdir}"; ninja #--verbose
```
#### And here what makepkg.conf introduces
```
#-- Compiler and Linker Flags
#CPPFLAGS=""
CFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions \
-Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security \
-fstack-clash-protection -fcf-protection \
-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
CXXFLAGS="$CFLAGS -Wp,-D_GLIBCXX_ASSERTIONS"
LDFLAGS="-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now \
-Wl,-z,pack-relative-relocs"
LTOFLAGS="-flto=auto"
#-- Make Flags: change this for DistCC/SMP systems
#MAKEFLAGS="-j2"
#-- Debugging flags
DEBUG_CFLAGS="-g"
DEBUG_CXXFLAGS="$DEBUG_CFLAGS"
```
### Context & Diagnostic Data
Inspecting the core dump isolates the frame parameter conflict inside `CBeamLaser::FireInternal` where the negative distance value originates:
```text
(gdb) frame 7
#7 CBeamLaser::FireInternal (...) at rts/Sim/Weapons/BeamLaser.cpp:360
360 TraceRay::TraceRayShields(this, curPos, curDir, beamLength, hitShields);
(gdb) info locals
beamLength = -82.403389
maxLength = 215
hitColQuery = { b0 = 1, b1 = 1, t0 = 83.6559067, t1 = 108.723846, ... }
```
An intersection query successfully found a collision boundary at `hitColQuery.t0 = 83.6559067`. However, `beamLength` evaluates to `-82.403389`.
The difference between the query distance and the resulting length maps directly to the weapon's muzzle piece offset from the origin axis:
`83.6559067 - 82.403389 = 1.2525177`
The terms appear to be inverted during calculation (`muzzleOffset - hitColQuery.t0` instead of `hitColQuery.t0 - muzzleOffset`), generating a negative scalar value. This negative value is passed directly down into `CQuadField::GetQuadsOnRay`:
```text
(gdb) frame 5
#5 CQuadField::GetQuadsOnRay(QuadFieldQuery&, float3 const&, float3 const&, float) at rts/Sim/Misc/QuadField.cpp:264
264 t0 = std::clamp(t0, 0.0f, length);
(gdb) info args
length = -82.403389
```
Because `length` is negative, calling `std::clamp(t0, 0.0f, length)` passes a high bound that is lower than the low bound (`0.0f`). On hardened distributions, this immediately triggers a runtime abort via `__glibcxx_assert_fail`.
### Call Stack
```text
#0 0x00007f05f74adfbb pthread_kill () from /usr/lib/libc.so.6
#1 0x00007f05f7444c88 raise () from /usr/lib/libc.so.6
#2 0x00007f05f7425795 abort () from /usr/lib/libc.so.6
#3 0x00007f05f789e059 _ZSt21__glibcxx_assert_fail... () from /usr/lib/libstdc++.so.6
#4 0x00005595fe675be0 std::clamp(...)
#5 CQuadField::GetQuadsOnRay(...) at QuadField.cpp:264
#6 TraceRay::TraceRayShields(...) at TraceRay.cpp:343
#7 CBeamLaser::FireInternal(...) at BeamLaser.cpp:360
#8 CWeapon::Fire(bool) at Weapon.cpp:227
#9 CWeapon::UpdateSalvo() at Weapon.cpp:228
#10 CBeamLaser::Update() at BeamLaser.cpp:212
```
### full output of me poking around in gdb
https://gist.github.com/Wabuo/d26242b7724c6dca6e9f10ccd905dd8d
Contributor guide
Research direction
Start in rts/Sim/Weapons/BeamLaser.cpp around CBeamLaser::FireInternal at line 360, then trace the beam-length calculation into TraceRay.cpp and CQuadField::GetQuadsOnRay in rts/Sim/Misc/QuadField.cpp at line 264. Reproduce the crash with the reported hardened GCC build and inspect the muzzle-offset and hit-query values. Done means the reported laser case no longer passes an inverted range to std::clamp and does not abort.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, linux
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100