From a17e39e3a9c9781be6dd5275dca78592c4e610ff Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Sun, 29 Dec 2024 00:14:13 +0300 Subject: [PATCH] More strictly check stat index bounds. Don't allow referencing stats >= 32 without protocol extensions. --- src/client/ascii.c | 6 +++--- src/client/cgame.c | 8 +++++++- src/client/cgame_classic.c | 4 +++- src/client/cgame_classic.h | 3 +++ src/client/client.h | 1 + src/client/demo.c | 1 + src/client/main.c | 1 + src/client/parse.c | 2 ++ src/client/screen.c | 4 ++-- 9 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/client/ascii.c b/src/client/ascii.c index d0c943481..e73d5ea02 100644 --- a/src/client/ascii.c +++ b/src/client/ascii.c @@ -233,7 +233,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) width = Q_atoi(token); token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; @@ -244,7 +244,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) if (!strcmp(token, "stat_string")) { token = COM_Parse(&s); index = Q_atoi(token); - if (index < 0 || index >= MAX_STATS) { + if (index < 0 || index >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid string index", __func__); } index = cl.frame.ps.stats[index]; @@ -273,7 +273,7 @@ static void TH_DrawLayoutString(char *dst, const char *s) if (!strcmp(token, "if")) { token = COM_Parse(&s); value = Q_atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= cl.max_stats) { Com_Error(ERR_DROP, "%s: invalid stat index", __func__); } value = cl.frame.ps.stats[value]; diff --git a/src/client/cgame.c b/src/client/cgame.c index cd8d100e1..f1d99249f 100644 --- a/src/client/cgame.c +++ b/src/client/cgame.c @@ -34,6 +34,11 @@ static bool CGX_IsExtendedServer(void) return cl.csr.extended; } +static int CGX_GetMaxStats(void) +{ + return cl.max_stats; +} + static color_t apply_scr_alpha(color_t color) { color.a *= Cvar_ClampValue(scr_alpha, 0, 1); @@ -51,9 +56,10 @@ static const pmoveParams_t* CGX_GetPmoveParams(void) } static cgame_q2pro_extended_support_ext_t cgame_q2pro_extended_support = { - .api_version = 2, + .api_version = 3, .IsExtendedServer = CGX_IsExtendedServer, + .GetMaxStats = CGX_GetMaxStats, .DrawCharEx = CGX_DrawCharEx, .GetPmoveParams = CGX_GetPmoveParams, }; diff --git a/src/client/cgame_classic.c b/src/client/cgame_classic.c index af4530789..fc0542362 100644 --- a/src/client/cgame_classic.c +++ b/src/client/cgame_classic.c @@ -48,6 +48,7 @@ bool SCR_ParseColor(const char *s, color_t *color); static cgame_import_t cgi; static cgame_q2pro_extended_support_ext_t cgix; static const cs_remap_t *csr; +static int max_stats; static cvar_t *scr_centertime; static cvar_t *scr_draw2d; @@ -67,6 +68,7 @@ static void CGC_Init(void) /* We don't consider rerelease servers here and assume the appropriate * cgame is used in that case */ csr = cgix.IsExtendedServer() ? &cs_remap_q2pro_new : &cs_remap_old; + max_stats = cgix.GetMaxStats(); scr_centertime = cgi.cvar("scr_centertime", "2.5", 0); scr_draw2d = cgi.cvar("scr_draw2d", "2", 0); @@ -288,7 +290,7 @@ static void layout_pic(vrect_t hud_vrect, const char **s, const player_state_t * // draw a pic from a stat number char* token = COM_Parse(s); int value = atoi(token); - if (value < 0 || value >= MAX_STATS) { + if (value < 0 || value >= max_stats) { cgi.Com_Error(va("%s: invalid stat index", __func__)); } value = ps->stats[value]; diff --git a/src/client/cgame_classic.h b/src/client/cgame_classic.h index 74ca6fd28..4fb5865fd 100644 --- a/src/client/cgame_classic.h +++ b/src/client/cgame_classic.h @@ -26,6 +26,9 @@ typedef struct { // Whether server is using "extended" protocol bool (*IsExtendedServer)(void); + // Max allowed stats + int (*GetMaxStats)(void); + // Draw single character, colorized & w/ flags void (*DrawCharEx)(int x, int y, int flags, int ch, color_t color); diff --git a/src/client/client.h b/src/client/client.h index ee4daca3b..972e79c7b 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -338,6 +338,7 @@ typedef struct { char gamedir[MAX_QPATH]; int clientNum; // never changed during gameplay, set by serverdata packet int maxclients; + int max_stats; pmoveParams_t pmp; frametime_t frametime; diff --git a/src/client/demo.c b/src/client/demo.c index cb23f9c54..dfa2f0a17 100644 --- a/src/client/demo.c +++ b/src/client/demo.c @@ -892,6 +892,7 @@ static void CL_PlayDemo_f(void) Q_strlcpy(cls.servername, COM_SkipPath(name), sizeof(cls.servername)); cls.serverAddress.type = NA_LOOPBACK; cl.csr = cs_remap_old; + cl.max_stats = MAX_STATS_OLD; Con_Popup(true); SCR_UpdateScreen(); diff --git a/src/client/main.c b/src/client/main.c index 03e1b9998..b187b06bc 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -1405,6 +1405,7 @@ static void CL_ConnectionlessPacket(void) cls.connect_count = 0; Q_strlcpy(cl.mapname, mapname, sizeof(cl.mapname)); // for levelshot screen cl.csr = cs_remap_old; + cl.max_stats = MAX_STATS_OLD; return; } diff --git a/src/client/parse.c b/src/client/parse.c index a558109d5..47d9b9b87 100644 --- a/src/client/parse.c +++ b/src/client/parse.c @@ -796,6 +796,8 @@ static void CL_ParseServerData(const q2proto_svc_serverdata_t *serverdata) cls.demo.psFlags = cl.csr.extended ? CL_PS_EXTENDED_MASK_2 : 0; } + cl.max_stats = (cl.game_api >= Q2PROTO_GAME_Q2PRO_EXTENDED_V2) ? MAX_STATS_NEW : MAX_STATS_OLD; + // Load cgame (after we know all the timings) CG_Load(cl.gamedir, cl.game_api == Q2PROTO_GAME_RERELEASE); cgame->Init(); diff --git a/src/client/screen.c b/src/client/screen.c index c51b4fe7b..7a2240302 100644 --- a/src/client/screen.c +++ b/src/client/screen.c @@ -945,8 +945,8 @@ static void SCR_DrawDebugStats(void) if (j <= 0) return; - if (j > MAX_STATS) - j = MAX_STATS; + if (j > cl.max_stats) + j = cl.max_stats; x = CONCHAR_WIDTH; y = (scr.hud_height - j * CONCHAR_HEIGHT) / 2;