Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dc89a8a
client: fix OOB read in DeathMsg
a1batross May 21, 2026
de9dcc1
client: demo: drop unused types, add stricter checks
a1batross May 21, 2026
7a4fda6
client: scoreboard: drop thisplayer hack from GetAllPlayersInfo. Zero…
a1batross May 21, 2026
e1c8d35
client: fix possible OOB reads by validating player and hostage indic…
a1batross May 21, 2026
f4e85f0
client: return NULL in gWR.GetWeapon to prevent OOB reads/writes
a1batross May 21, 2026
6c35791
client: fix OOB in weapon prediction, when vuser4.x carries ammo type…
a1batross May 21, 2026
f23a813
client: fix OOB in gWR.SetAmmo
a1batross May 21, 2026
40f89cf
client: port WeaponList checks from hlsdk
a1batross May 21, 2026
df9d3fa
client: saytext: validate client_index to prevent OOB
a1batross May 21, 2026
842ed5a
client: prevent OOB in GetClientColor
a1batross May 21, 2026
d621e0e
client: hud: radar: fix off by one error when drawing hostages on radar
a1batross May 21, 2026
38db9f7
client: add HUD_MessageBox from hlsdk-portable, use it to show mainui…
a1batross May 21, 2026
3752c7f
client: add compatible VGUI2 string formatter
a1batross May 21, 2026
e434e69
client: use Localize_Format in text_message
a1batross May 21, 2026
4264063
client: use Localize_Format in chat
a1batross May 21, 2026
52a0541
client: use Localize_Format in scoreboard
a1batross May 21, 2026
4d546ae
cmake: force -Wl,--no-undefined
a1batross May 21, 2026
0cf7c79
client: add strlcat/strlcpy
a1batross May 21, 2026
c1ef53d
client: use strlcpy/strlcat
a1batross May 21, 2026
7172b21
client: vgui_parser: fix localization file underread
a1batross May 21, 2026
fdf2300
client: in BufferReader, return string that's member of a class inste…
a1batross May 21, 2026
135e984
client: vsprintf -> vsnprintf
a1batross May 21, 2026
2e06260
client: fix ConvertString overflow
a1batross May 21, 2026
9647869
client: fix location order in fallback chat localization strings
a1batross May 22, 2026
bb6ac6a
client: drop outdated comment now that we have vgui2 compatible forma…
a1batross May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ if(VITA)
add_compile_options(-fno-use-cxa-atexit)
endif()

if(NOT WIN32 AND NOT APPLE)
add_link_options(-Wl,--no-undefined)
endif()

include(CheckSymbolExists)
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
check_symbol_exists(strlcat string.h HAVE_STRLCAT)

if(BUILD_CLIENT)
add_subdirectory(cl_dll)
endif()
Expand Down
12 changes: 12 additions & 0 deletions cl_dll/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ list(REMOVE_ITEM CS_WPN_SRC "${CMAKE_CURRENT_SOURCE_DIR}/cs_wpn/cs_baseentity.cp
list(APPEND CS_CLIENT_SRC ../common/interface.cpp)
list(APPEND CS_CLIENT_SRC ../public/utflib.cpp)

if(HAVE_STRLCPY)
add_definitions(-DHAVE_STRLCPY)
else()
list(APPEND CS_CLIENT_SRC ../public/strlcpy.c)
endif()

if(HAVE_STRLCAT)
add_definitions(-DHAVE_STRLCAT)
else()
list(APPEND CS_CLIENT_SRC ../public/strlcat.c)
endif()

list(APPEND CS_CLIENT_SRC ${CS_EV_SRC})
list(APPEND CS_CLIENT_SRC ${CS_WPN_SRC})
list(APPEND CS_CLIENT_SRC ${CS_WPNSH_SRC})
Expand Down
55 changes: 31 additions & 24 deletions cl_dll/ammo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <string.h>
#include <stdio.h>
#include "strl.h"

#include "ammohistory.h"
#include "eventscripts.h"
Expand Down Expand Up @@ -631,13 +632,13 @@ int CHudAmmo::MsgFunc_CurWeapon(const char *pszName, int iSize, void *pbuf )
int CHudAmmo::MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf )
{
BufferReader reader( pszName, pbuf, iSize );
WEAPON Weapon;

WEAPON Weapon = { 0 };

strncpy( Weapon.szName, reader.ReadString(), MAX_WEAPON_NAME );
Weapon.szName[MAX_WEAPON_NAME-1] = 0;
Weapon.iAmmoType = (int)reader.ReadChar();

Weapon.iMax1 = reader.ReadByte();
if (Weapon.iMax1 == 255)
Weapon.iMax1 = -1;
Expand All @@ -653,6 +654,21 @@ int CHudAmmo::MsgFunc_WeaponList(const char *pszName, int iSize, void *pbuf )
Weapon.iFlags = reader.ReadByte();
Weapon.iClip = 0;

if( Weapon.iId < 0 || Weapon.iId >= MAX_WEAPONS )
return 0;
if( Weapon.iSlot < 0 || Weapon.iSlot >= MAX_WEAPON_SLOTS + 1 )
return 0;
if( Weapon.iSlotPos < 0 || Weapon.iSlotPos >= MAX_WEAPON_POSITIONS + 1 )
return 0;
if( Weapon.iAmmoType < -1 || Weapon.iAmmoType >= MAX_AMMO_TYPES )
return 0;
if( Weapon.iAmmo2Type < -1 || Weapon.iAmmo2Type >= MAX_AMMO_TYPES )
return 0;
if( Weapon.iAmmoType >= 0 && Weapon.iMax1 == 0 )
return 0;
if( Weapon.iAmmo2Type >= 0 && Weapon.iMax2 == 0 )
return 0;

gWR.AddWeapon( &Weapon );

return 1;
Expand Down Expand Up @@ -947,24 +963,19 @@ void CHudAmmo::UserCmd_Autobuy()
char *pfile = afile;
char token[1024];
char szCmd[1024];
int remaining = 1023;

if( !pfile )
{
ConsolePrint( "Can't open autobuy.txt file.\n" );
return;
}

strcpy(szCmd, "cl_setautobuy");
remaining -= sizeof( "cl_setautobuy" );
strlcpy( szCmd, "cl_setautobuy", sizeof( szCmd ) );

while((pfile = gEngfuncs.COM_ParseFile( pfile, token )))
{
// append space first
strncat(szCmd, " ", remaining);
strncat(szCmd, token, remaining - 1);

remaining -= strlen( token ) - 1;
strlcat( szCmd, " ", sizeof( szCmd ) );
strlcat( szCmd, token, sizeof( szCmd ) );
}

gEngfuncs.pfnServerCmd( szCmd );
Expand All @@ -977,31 +988,27 @@ void CHudAmmo::UserCmd_Rebuy()
char *pfile = afile;
char token[1024];
char szCmd[1024];
int lastCh;
int remaining = 1023;
size_t lastCh;

if( !pfile )
{
ConsolePrint( "Can't open rebuy.txt file.\n" );
return;
}

// start with \"
strcpy(szCmd, "cl_setrebuy \"" );
remaining -= sizeof( "cl_setrebuy \"" );
strlcpy( szCmd, "cl_setrebuy \"", sizeof( szCmd ) );

while((pfile = gEngfuncs.COM_ParseFile( pfile, token )))
{
strncat(szCmd, token, remaining );
remaining -= strlen( token );

// append space after token
strncat(szCmd, " ", remaining );
remaining--;
strlcat( szCmd, token, sizeof( szCmd ) );
strlcat( szCmd, " ", sizeof( szCmd ) );
}
// replace last space with ", before terminator
lastCh = strlen(szCmd);
szCmd[lastCh] = '\"';
lastCh = strlen( szCmd );
if( lastCh > 0 && lastCh < sizeof( szCmd ) - 1 )
{
szCmd[lastCh - 1] = '\"';
}

gEngfuncs.pfnServerCmd( szCmd );
gEngfuncs.COM_FreeFile( afile );
Expand Down
16 changes: 12 additions & 4 deletions cl_dll/ammohistory.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ class WeaponsResource
///// WEAPON /////
int iOldWeaponBits;

WEAPON *GetWeapon( int iId ) { return &rgWeapons[iId]; }
WEAPON *GetWeapon( int iId )
{
if ( iId < 0 || iId >= MAX_WEAPONS )
return NULL;
return &rgWeapons[iId];
}
void AddWeapon( WEAPON *wp )
{
rgWeapons[ wp->iId ] = *wp;
Expand Down Expand Up @@ -89,9 +94,12 @@ class WeaponsResource
int HasAmmo( WEAPON *p );

///// AMMO /////
AMMO GetAmmo( int iId ) { return iId; }

void SetAmmo( int iId, int iCount ) { riAmmo[ iId ] = iCount; }
void SetAmmo( int iId, int iCount )
{
if ( iId < 0 || iId >= MAX_AMMO_TYPES )
return;
riAmmo[ iId ] = iCount;
}

int CountAmmo( int iId );

Expand Down
20 changes: 16 additions & 4 deletions cl_dll/cdll_int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,27 @@ static IGameMenuExports *GetNativeMenuExports( void )
return static_cast<IGameMenuExports *>( menuFactory( GAMEMENUEXPORTS_INTERFACE_VERSION, NULL ) );
}

static bool HUD_MessageBox( const char *msg )
{
gEngfuncs.Con_Printf( "%s", msg );

if( g_iMobileAPIVersion && gMobileAPI.pfnSys_Warn )
{
gMobileAPI.pfnSys_Warn( "%s", msg );
return true;
}

return false;
}

static void LoadMenuInterface( void )
{
if( g_pMenu )
return;

g_pMenu = GetNativeMenuExports();
if( !g_pMenu )
gEngfuncs.Con_Printf( "Error: native object \"MenuFactory\" is unavailable\n" );
HUD_MessageBox( "Error: native object \"MenuFactory\" is unavailable\n" );
}

void InitInput (void);
Expand Down Expand Up @@ -363,8 +376,7 @@ Called when a player starts or stops talking.
void DLLEXPORT HUD_VoiceStatus(int entindex, qboolean bTalking)
{
// gHUD.m_Radio.Voice( entindex, bTalking );

if ( entindex >= 0 && entindex < gEngfuncs.GetMaxClients() )
if ( entindex > 0 && entindex <= gEngfuncs.GetMaxClients() )
{
if ( bTalking )
{
Expand Down Expand Up @@ -489,7 +501,7 @@ extern "C" void DLLEXPORT HUD_ChatInputPosition( int *x, int *y )
extern "C" int DLLEXPORT HUD_GetPlayerTeam(int iplayer)
{
// original seems to return team_id, but I'm not sure it's even set somewhere
if ( iplayer >= 0 && iplayer < MAX_PLAYERS )
if ( iplayer >= 1 && iplayer <= MAX_PLAYERS )
return g_PlayerExtraInfo[iplayer].teamnumber;
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion cl_dll/com_weapons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void COM_Log( char *pszFile, char *fmt, ...)
}

va_start (argptr,fmt);
vsprintf (string, fmt,argptr);
vsnprintf( string, sizeof( string ), fmt, argptr );
va_end (argptr);

fp = fopen( pfilename, "a+t");
Expand Down
6 changes: 3 additions & 3 deletions cl_dll/cs_wpn/cs_weapons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ void AlertMessage( ALERT_TYPE atype, const char *szFmt, ... )
static char string[1024];

va_start (argptr, szFmt);
vsprintf (string, szFmt,argptr);
vsnprintf( string, sizeof( string ), szFmt, argptr );
va_end (argptr);

gEngfuncs.Con_Printf( "cl: " );
gEngfuncs.Con_Printf( string );
gEngfuncs.Con_Printf( "%s", string );
}

/*
Expand Down Expand Up @@ -1187,7 +1187,7 @@ void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cm
pCurrent->m_iShotsFired = pfrom->m_fInZoom;
}

if( from->client.vuser4.x < 0 || from->client.vuser4.x > MAX_AMMO_TYPES )
if( from->client.vuser4.x < 0 || from->client.vuser4.x >= MAX_AMMO_TYPES )
pWeapon->m_iPrimaryAmmoType = 0;
else
{
Expand Down
32 changes: 20 additions & 12 deletions cl_dll/death.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <string.h>
#include <stdio.h>
#include "draw_util.h"
#include "strl.h"

float color[3];

Expand Down Expand Up @@ -172,8 +173,8 @@ int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *p
int headshot = reader.ReadByte();

char killedwith[32];
strncpy( killedwith, "d_", sizeof(killedwith) );
strncat( killedwith, reader.ReadString(), sizeof( killedwith ) - 2 );
strlcpy( killedwith, "d_", sizeof( killedwith ) );
strlcat( killedwith, reader.ReadString(), sizeof( killedwith ) );

//if (gViewPort)
// gViewPort->DeathMsg( killer, victim );
Expand All @@ -197,7 +198,14 @@ int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *p
gHUD.m_Scoreboard.GetAllPlayersInfo();

// Get the Killer's name
const char *killer_name = g_PlayerInfoList[ killer ].name;
const char *killer_name = NULL;
bool killer_this_player = false;
if ( killer >= 1 && killer <= MAX_PLAYERS )
{
killer_name = g_PlayerInfoList[killer].name;
killer_this_player = g_PlayerInfoList[killer].thisplayer;
}

if ( !killer_name )
{
killer_name = "";
Expand All @@ -206,15 +214,15 @@ int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *p
else
{
rgDeathNoticeList[i].KillerColor = GetClientColor( killer );
strncpy( rgDeathNoticeList[i].szKiller, killer_name, MAX_PLAYER_NAME_LENGTH );
rgDeathNoticeList[i].szKiller[MAX_PLAYER_NAME_LENGTH-1] = 0;
strlcpy( rgDeathNoticeList[i].szKiller, killer_name, sizeof( rgDeathNoticeList[i].szKiller ) );
}

// Get the Victim's name
const char *victim_name = NULL;
// If victim is -1, the killer killed a specific, non-player object (like a sentrygun)
if ( ((char)victim) != -1 )

if ( victim >= 1 && victim <= MAX_PLAYERS )
victim_name = g_PlayerInfoList[ victim ].name;

if ( !victim_name )
{
victim_name = "";
Expand All @@ -223,17 +231,17 @@ int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *p
else
{
rgDeathNoticeList[i].VictimColor = GetClientColor( victim );
strncpy( rgDeathNoticeList[i].szVictim, victim_name, MAX_PLAYER_NAME_LENGTH );
rgDeathNoticeList[i].szVictim[MAX_PLAYER_NAME_LENGTH-1] = 0;
strlcpy( rgDeathNoticeList[i].szVictim, victim_name, sizeof( rgDeathNoticeList[i].szVictim ) );
}

// Is it a non-player object kill?
if ( ((char)victim) == -1 )
// If victim is 255, the killer killed a specific, non-player object (like a sentrygun)
if( victim == 255 )
{
rgDeathNoticeList[i].bNonPlayerKill = true;

// Store the object's name in the Victim slot (skip the d_ bit)
strncpy( rgDeathNoticeList[i].szVictim, killedwith+2, sizeof(killedwith) );
strlcpy( rgDeathNoticeList[i].szVictim, killedwith+2, sizeof( rgDeathNoticeList[i].szVictim ) );
}
else
{
Expand All @@ -254,7 +262,7 @@ int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *p
rgDeathNoticeList[i].flDisplayTime = gHUD.m_flTime + hud_deathnotice_time->value;

// Play kill sound
if ((g_PlayerInfoList[killer].thisplayer || g_iUser2 == killer) &&
if ((killer_this_player || g_iUser2 == killer) &&
!rgDeathNoticeList[i].bNonPlayerKill &&
!rgDeathNoticeList[i].bSuicide &&
cl_killsound->value > 0.0f)
Expand Down
Loading
Loading