Skip to content

CKnife::Swing crashes (SIGSEGV) when knife trace hits an edict whose CBaseEntity::Instance() is NULL #1178

Description

@Kkomia7

Description

CKnife::Swing can crash with a null-pointer dereference when the melee trace hits an edict for which CBaseEntity::Instance(tr.pHit) returns NULL.

In the REGAMEDLL_FIXES build the defensive if (pEntity) guard is compiled out, so pEntity is dereferenced unconditionally right after the trace:

CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
SetPlayerShieldAnim();
m_pPlayer->SetAnimation(PLAYER_ATTACK1);
ClearMultiDamage();

pEntity->TraceAttack( ... );   // <-- pEntity may be NULL -> SIGSEGV

The #ifndef REGAMEDLL_FIXES / if (pEntity) // -V595 block that used to protect this path is disabled in fixes builds, and current master still has no null-check before pEntity->TraceAttack.

Crash backtrace (observed)

Crash: signal 11 errno 0 with code 1 at 0x4 (nil)
 1: CKnife::Swing(int)+1289          (cs.so)
 2: CKnife::PrimaryAttack()+13       (cs.so)
 3: CBasePlayerWeapon::ItemPostFrame_OrigFunc()+873
 4: CBasePlayerWeapon::ItemPostFrame()+128
 5: CBasePlayer::PostThink_OrigFunc()+238
 ...
 9: SV_RunCmd
10: SV_ParseClientMove

Fault address 0x4 is consistent with dereferencing a NULL CBaseEntity* (accessing an early member/pev).

How it is triggered (logic, engine-dependent)

The trigger is a stale/orphan player edict that is still SOLID (so the knife trace can hit it) but no longer has a valid private-data / CBaseEntity instance, so CBaseEntity::Instance(tr.pHit) returns NULL.

In our case the engine keeps such an edict around after a client leaves without a proper disconnect notification (the gamedll logs client_disconnected and client_remove forwards have been disabled). The orphan edict stays SOLID_SLIDEBOX for a short window; if another player swings a knife through that spot during the window, Swing crashes.

It is timing-dependent and hard to reproduce on demand, but the code path is clearly unsafe regardless of how pHit ends up pointing at an instance-less edict.

Environment

  • Engine: Xash3D FWGS 0.21 (build 4142-18dddaf-master, linux-i386) — NOT ReHLDS
  • GameDLL: ReGameDLL_CS 5.30.0.814-dev (build May 18 2026, commit 781a68a), REGAMEDLL_FIXES enabled
  • Metamod: Metamod-FWGS 1.0.0.222+m (commit c0ad71a)
  • AMXX: 1.10.0.5479
  • OS: Ubuntu 24.04.4 LTS (x86_64 host, i386 binaries)

Suggested fix

Re-add a null guard in the REGAMEDLL_FIXES path before dereferencing pEntity:

CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
if (pEntity)   // trace can hit an edict without a valid CBaseEntity instance
{
    SetPlayerShieldAnim();
    m_pPlayer->SetAnimation(PLAYER_ATTACK1);
    ClearMultiDamage();
    pEntity->TraceAttack( ... );
    ApplyMultiDamage(...);
}

(We work around it plugin-side by forcing such orphan edicts to SOLID_NOT every frame so the knife trace can't hit them, but the DLL-side guard would fix the root cause.)


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions