From d93d81653d946b9db1ce3c735aa27be8594efec1 Mon Sep 17 00:00:00 2001 From: Kr3m Date: Sat, 11 Apr 2026 10:46:19 -0400 Subject: [PATCH] fix: COM_StripExtension overflow and CG_CompileText heap churn/OOB write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - COM_StripExtension: add destsize parameter and reimplement using Q_strncpyz to avoid unbounded copy and undefined in‑place use. Update all cg_weapons.c call sites to pass sizeof(path). - CG_CompileText: replace per‑frame Z_Malloc scratch buffers with static arrays; cap input length at MAX_STRING_CHARS; add bounds check (i < OSP_TEXT_CMD_MAX - 1) to prevent command array overrun. CG_CompiledTextDestroy becomes a no‑op. --- code/cgame/cg_drawtools.c | 30 ++++++++---------------------- code/cgame/cg_weapons.c | 6 +++--- code/qcommon/q_shared.c | 20 ++++++++++++++------ code/qcommon/q_shared.h | 2 +- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c index 0043a392..f2f5a58d 100644 --- a/code/cgame/cg_drawtools.c +++ b/code/cgame/cg_drawtools.c @@ -429,10 +429,9 @@ void CG_OSPDrawStringPrepare(const char* from, char* to, int size) text_command_t* CG_CompileText(const char* in, int flags) { int b; - text_command_t* commands; - text_command_t* result = NULL; + static text_command_t commands[OSP_TEXT_CMD_MAX]; char* text; - char* dmem; + static char dmem[MAX_STRING_CHARS]; int i = 0; int len; vec4_t back_color; @@ -448,17 +447,14 @@ text_command_t* CG_CompileText(const char* in, int flags) Vector4Copy(colorWhite, back_color); - commands = Z_Malloc(sizeof(*commands) * OSP_TEXT_CMD_MAX) ; - OSP_MEMORY_CHECK(commands); - len = strlen(in) + 1; - dmem = Z_Malloc(len); - OSP_MEMORY_CHECK(dmem); + if (len > MAX_STRING_CHARS) + len = MAX_STRING_CHARS; text = dmem; CG_OSPDrawStringPrepare(in, text, len); - while (*text) + while (*text && i < OSP_TEXT_CMD_MAX - 1) { if (text[0] == '^' && text[1]) { @@ -603,22 +599,12 @@ text_command_t* CG_CompileText(const char* in, int flags) } commands[i++].type = OSP_TEXT_CMD_STOP; - result = Z_Malloc(sizeof(text_command_t) * i); - OSP_MEMORY_CHECK(result); - - memcpy(result, commands, sizeof(text_command_t) * i); - - Z_Free(dmem); - Z_Free(commands); - return result; + return commands; } void CG_CompiledTextDestroy(text_command_t* root) { - if (root) - { - Z_Free(root); - } + (void)root; // static buffer, nothing to free } /* @@ -1579,7 +1565,7 @@ void CG_OSPDrawPoly(float x, float y, float w, float h, vec4_t color) // bk001205 - code below duplicated in q3_ui/ui-atoms.c // bk001205 - FIXME: does this belong in ui_shared.c? // bk001205 - FIXME: HARD_LINKED flags not visible here -#ifndef Q3_STATIC // bk001205 - q_shared defines not visible here +#ifndef Q3_STATIC // bk001205 - q_shared defines not visible here /* ================= UI_DrawProportionalString2 diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 8bad9755..63e7e2e1 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -751,17 +751,17 @@ void CG_RegisterWeapon(int weaponNum) weaponInfo->ammoModel = trap_R_RegisterModel(ammo->world_model[0]); } strcpy(path, item->world_model[0]); - COM_StripExtension(path, path); + COM_StripExtension(path, path, sizeof(path)); strcat(path, "_flash.md3"); weaponInfo->flashModel = trap_R_RegisterModel(path); strcpy(path, item->world_model[0]); - COM_StripExtension(path, path); + COM_StripExtension(path, path, sizeof(path)); strcat(path, "_barrel.md3"); weaponInfo->barrelModel = trap_R_RegisterModel(path); strcpy(path, item->world_model[0]); - COM_StripExtension(path, path); + COM_StripExtension(path, path, sizeof(path)); strcat(path, "_hand.md3"); weaponInfo->handsModel = trap_R_RegisterModel(path); diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index 2a9029c2..15b438db 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -123,13 +123,21 @@ char* COM_SkipPath(char* pathname) COM_StripExtension ============ */ -void COM_StripExtension(const char* in, char* out) +void COM_StripExtension(const char* in, char* out, int destsize) { - while (*in && *in != '.') - { - *out++ = *in++; - } - *out = 0; + int length; + + Q_strncpyz(out, in, destsize); + + length = strlen(out) - 1; + while (length > 0 && out[length] != '.') + { + length--; + if (out[length] == '/') + return; // no extension + } + if (length) + out[length] = '\0'; } diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index e3bc9e6a..6b9bedcd 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -865,7 +865,7 @@ void PerpendicularVector(vec3_t dst, const vec3_t src); float Com_Clamp(float min, float max, float value); char* COM_SkipPath(char* pathname); -void COM_StripExtension(const char* in, char* out); +void COM_StripExtension(const char* in, char* out, int destsize); void COM_DefaultExtension(char* path, int maxSize, const char* extension); void COM_BeginParseSession(const char* name);