beyond-all-reason / beyond-all-reason/RecoilEngine

EngineCrash [2026.06.08-RC] at [CBeamLaser::FireInternal] (bug report and gdb poke around assisted by LLM)

Open
#3,018 3 comments 0 reactions 0 assignees View on GitHub
bug enginecrash
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.