Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions regamedll/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ set(GAMEDLL_SRCS

set(UNITTESTS_SRCS
"unittests/animation_tests.cpp"
"unittests/shared_util_tests.cpp"
"unittests/struct_offsets_tests.cpp"
"unittests/TestRunner.cpp"
)
Expand Down
6 changes: 5 additions & 1 deletion regamedll/dlls/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9851,14 +9851,18 @@ void CBasePlayer::PrioritizeAutoBuyString(char (&autobuyString)[MAX_AUTOBUY_LENG
int i = 0;

// get the next token from the priority string.
while (*priorityChar != '\0' && *priorityChar != ' ')
while (*priorityChar != '\0' && *priorityChar != ' ' && i < (int)sizeof(priorityToken) - 1)
{
priorityToken[i++] = *priorityChar;
priorityChar++;
}

priorityToken[i] = '\0';

// skip the rest of an oversized token that did not fit
while (*priorityChar != '\0' && *priorityChar != ' ')
priorityChar++;

// skip spaces
while (*priorityChar == ' ')
priorityChar++;
Expand Down
11 changes: 7 additions & 4 deletions regamedll/game_shared/shared_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,27 @@ char *SharedParse(char *data)
return data;
}

s_shared_token[len++] = c;
// prevent overflow of the fixed-size token buffer
if (len < (int)sizeof(s_shared_token) - 1)
s_shared_token[len++] = c;
}
}

// parse single characters
if (c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ',')
{
s_shared_token[len++] = c;
if (len < (int)sizeof(s_shared_token) - 1)
s_shared_token[len++] = c;
s_shared_token[len] = '\0';
return data + 1;
}

// parse a regular word
do
{
s_shared_token[len] = c;
if (len < (int)sizeof(s_shared_token) - 1)
s_shared_token[len++] = c;
data++;
len++;
c = *data;

if (c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ',')
Expand Down
6 changes: 6 additions & 0 deletions regamedll/msvc/ReGameDLL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Play|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Play|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\unittests\shared_util_tests.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Play|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Play|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\unittests\struct_offsets_tests.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
Expand Down
47 changes: 47 additions & 0 deletions regamedll/unittests/shared_util_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "precompiled.h"
#include "cppunitlite/TestHarness.h"

// Regression test for the SharedParse() global-buffer overflow.
// s_shared_token is char[1500]; SharedParse() must never emit a token that
// does not fit, regardless of the size of a single unquoted/quoted token in
// the input. On the unpatched code the oversized-token cases below smash the
// global buffer (crash / memory corruption) before the CHECK is even reached.
const int SHARED_TOKEN_SIZE = 1500; // must match char s_shared_token[1500]

TEST(RegularWordOverflow, SharedParse, 10)
{
// One unquoted "word" of 5000 non-space characters (no quotes/specials).
static char input[5001];
Q_memset(input, 'A', sizeof(input) - 1);
input[sizeof(input) - 1] = '\0';

SharedParse(input);
CHECK("oversized word must be truncated", Q_strlen(SharedGetToken()) < SHARED_TOKEN_SIZE);
}

TEST(QuotedStringOverflow, SharedParse, 10)
{
// One quoted string of 5000 characters between the quotes.
static char input[5003];
input[0] = '\"';
Q_memset(input + 1, 'B', 5000);
input[5001] = '\"';
input[5002] = '\0';

SharedParse(input);
CHECK("oversized quoted string must be truncated", Q_strlen(SharedGetToken()) < SHARED_TOKEN_SIZE);
}

TEST(NormalTokensStillWork, SharedParse, 10)
{
// The fix must not change behavior for well-formed input.
char input[] = " primaryWeapon \"hello world\" , ";
char *p = SharedParse(input);
CHECK("plain word token", Q_strcmp(SharedGetToken(), "primaryWeapon") == 0);

p = SharedParse(p);
CHECK("quoted string token", Q_strcmp(SharedGetToken(), "hello world") == 0);

p = SharedParse(p);
CHECK("comma token", Q_strcmp(SharedGetToken(), ",") == 0);
}