Skip to content

Fix global buffer overflow in SharedParse()#1165

Open
belomaxorka wants to merge 3 commits into
rehlds:masterfrom
belomaxorka:fix/sharedparse-bounds
Open

Fix global buffer overflow in SharedParse()#1165
belomaxorka wants to merge 3 commits into
rehlds:masterfrom
belomaxorka:fix/sharedparse-bounds

Conversation

@belomaxorka

@belomaxorka belomaxorka commented Jul 3, 2026

Copy link
Copy Markdown

What

SharedParse() copies each parsed token into the fixed-size global buffer s_shared_token[1500] without any bounds checking, so a single token longer than 1499 bytes overflows the buffer.

Why (Root cause)

The three token-writing sites in SharedParse() - quoted string, single special character, and regular word - increment len and write s_shared_token[len] with no check against the buffer size:

s_shared_token[len++] = c;   // quoted string / special char
...
s_shared_token[len] = c; len++;   // regular word

Any input that contains one token (quoted or unquoted) longer than the buffer overruns it and corrupts the globals that follow, which can crash the server or corrupt parser state.

The buffer is fed from data files parsed through LOAD_FILE_FOR_ME - botprofile.db, BotChatter.db, mapcycle.txt, tutor messages - and from the ct/t_default_weapons_* cvars, so a malformed or maliciously crafted file/config is enough to trigger it. The sibling parser CBasePlayer::ParseAutoBuyString() already bounds its copy correctly; SharedParse() simply misses the same guard.

How it works

Bound each of the three writes to sizeof(s_shared_token) - 1, mirroring the existing check in ParseAutoBuyString(). Oversized tokens are safely truncated and still null-terminated, and parsing resumes from the correct position.

Edge cases

  • Well-formed input is byte-for-byte unchanged; only a token longer than the buffer is truncated.
  • Truncation keeps the '\0' terminator and the returned parse position intact, so the surrounding parse loops behave as before.
  • Applies equally to quoted strings, single special characters, and regular words.

Tested

  • New unit tests (unittests/shared_util_tests.cpp): oversized unquoted word, oversized quoted string, and a normal-token regression (primaryWeapon / "hello world" / ,).
  • Release build (MSVC v145) compiles cleanly.
  • Standalone reproducer with a canary placed right after the buffer: a 1520-character token wrote 20 bytes past s_shared_token before the fix (canary clobbered with 0x4141414141414141), and is truncated to 1499 bytes after the fix.

Edit: also fixed a global buffer overflow of the same class in CBasePlayer::PrioritizeAutoBuyString(). priorityToken[32] is filled from the client-supplied autobuy string without bounds checking, so a single autobuy token longer than 31 characters overflowed it during career-mode auto-buy. Bounded to sizeof(priorityToken) - 1 matching ParseAutoBuyString(), and the rest of an oversized token is skipped so it is not re-split into a spurious priority token.

SharedParse() copies each parsed token into the fixed-size global buffer
s_shared_token[1500] without any bounds checking. A single token (a quoted
string or an unquoted word) longer than 1499 bytes overflows the buffer and
corrupts adjacent globals, which can crash the server or corrupt state.

The buffer is fed from data files parsed via LOAD_FILE_FOR_ME (botprofile.db,
BotChatter.db, mapcycle.txt, tutor messages) and from the default-weapon
cvars, so a malformed or malicious file/config triggers the overflow.

Bound all three write sites to sizeof(s_shared_token) - 1, matching the
existing pattern already used in CBasePlayer::ParseAutoBuyString(). Well-formed
input is unaffected; only pathologically long tokens are now truncated.

Add unit tests covering oversized unquoted/quoted tokens and normal token
parsing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@belomaxorka belomaxorka changed the title Fix global buffer overflow in SharedParse() Fix global buffer overflow in SharedParse() Jul 3, 2026
belomaxorka and others added 2 commits July 5, 2026 12:34
cppunitlite CHECK takes (message, condition); single-argument form
broke the Tests build on CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
priorityToken[32] is filled from the client-supplied autobuy string
(m_autoBuyString) without any bounds checking. A single autobuy token
longer than 31 characters overflows the buffer and corrupts adjacent
stack state during career-mode auto-buy.

Bound the token copy to sizeof(priorityToken) - 1, matching the pattern
in ParseAutoBuyString(), and skip the rest of an oversized token so it
is not re-split into a spurious priority token on the next iteration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant