Skip to content

Commit

Permalink
Fix potential buffer overflow in COM_Parse
Browse files Browse the repository at this point in the history
e.g. `wad` field longer than 1023 characters
  • Loading branch information
andrei-drexler committed Sep 24, 2023
1 parent 9497d57 commit 764ef9d
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 14 deletions.
43 changes: 34 additions & 9 deletions Quake/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1253,12 +1253,16 @@ char *COM_TintString (const char *in, char *out, size_t outsize)

/*
==============
COM_Parse
COM_ParseEx
Parse a token out of a string
The mode argument controls how overflow is handled:
- CPE_NOTRUNC: return NULL (abort parsing)
- CPE_ALLOWTRUNC: truncate com_token (ignore the extra characters in this token)
==============
*/
const char *COM_Parse (const char *data)
const char *COM_ParseEx (const char *data, cpe_mode mode)
{
int c;
int len;
Expand Down Expand Up @@ -1310,26 +1314,32 @@ const char *COM_Parse (const char *data)
com_token[len] = 0;
return data;
}
com_token[len] = c;
len++;
if (len < countof (com_token) - 1)
com_token[len++] = c;
else if (mode == CPE_NOTRUNC)
return NULL;
}
}

// parse single characters
if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\'' || c == ':')
{
com_token[len] = c;
len++;
if (len < countof (com_token) - 1)
com_token[len++] = c;
else if (mode == CPE_NOTRUNC)
return NULL;
com_token[len] = 0;
return data+1;
}

// parse a regular word
do
{
com_token[len] = c;
if (len < countof (com_token) - 1)
com_token[len++] = c;
else if (mode == CPE_NOTRUNC)
return NULL;
data++;
len++;
c = *data;
/* commented out the check for ':' so that ip:port works */
if (c == '{' || c == '}'|| c == '('|| c == ')' || c == '\''/* || c == ':' */)
Expand All @@ -1341,6 +1351,21 @@ const char *COM_Parse (const char *data)
}


/*
==============
COM_Parse
Parse a token out of a string
Return NULL in case of overflow
==============
*/
const char *COM_Parse (const char *data)
{
return COM_ParseEx (data, CPE_NOTRUNC);
}


/*
================
COM_CheckParm
Expand Down Expand Up @@ -2082,7 +2107,7 @@ const char *COM_ParseFloatNewline(const char *buffer, float *value)
const char *COM_ParseStringNewline(const char *buffer)
{
int i;
for (i = 0; i < 1023; i++)
for (i = 0; i < countof (com_token) - 1; i++)
if (!buffer[i] || q_isspace (buffer[i]))
break;
memcpy (com_token, buffer, i);
Expand Down
7 changes: 7 additions & 0 deletions Quake/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,14 @@ extern int q_vsnprintf(char *str, size_t size, const char *format, va_list args)
extern THREAD_LOCAL char com_token[1024];
extern qboolean com_eof;

typedef enum
{
CPE_NOTRUNC, // return parse error in case of overflow
CPE_ALLOWTRUNC, // truncate com_token in case of overflow
} cpe_mode;

const char *COM_Parse (const char *data);
const char *COM_ParseEx (const char *data, cpe_mode mode);


extern int com_argc;
Expand Down
2 changes: 1 addition & 1 deletion Quake/gl_fog.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ void Fog_ParseWorldspawn (void)
q_strlcpy(key, com_token, sizeof(key));
while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
data = COM_ParseEx(data, CPE_ALLOWTRUNC);
if (!data)
return; // error
q_strlcpy(value, com_token, sizeof(value));
Expand Down
2 changes: 1 addition & 1 deletion Quake/gl_model.c
Original file line number Diff line number Diff line change
Expand Up @@ -2539,7 +2539,7 @@ qboolean Mod_LoadMapDescription (char *desc, size_t maxchars, const char *map)
is_classname = i != 0 && !strcmp (com_token, "classname");

// parse value
data = COM_Parse (data);
data = COM_ParseEx (data, CPE_ALLOWTRUNC);
if (!data)
return ret;

Expand Down
2 changes: 1 addition & 1 deletion Quake/gl_rmisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ static void R_ParseWorldspawn (void)
q_strlcpy(key, com_token, sizeof(key));
while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
data = COM_ParseEx(data, CPE_ALLOWTRUNC);
if (!data)
return; // error
q_strlcpy(value, com_token, sizeof(value));
Expand Down
2 changes: 1 addition & 1 deletion Quake/gl_sky.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ void Sky_NewMap (void)
q_strlcpy(key, com_token, sizeof(key));
while (key[0] && key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
data = COM_ParseEx(data, CPE_ALLOWTRUNC);
if (!data)
return; // error
q_strlcpy(value, com_token, sizeof(value));
Expand Down
5 changes: 4 additions & 1 deletion Quake/pr_edict.c
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,10 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
}

// parse value
data = COM_Parse (data);
// HACK: we allow truncation when reading the wad field,
// otherwise maps using lots of wads with absolute paths
// could cause a parse error
data = COM_ParseEx (data, !strcmp (keyname, "wad") ? CPE_ALLOWTRUNC : CPE_NOTRUNC);
if (!data)
Host_Error ("ED_ParseEntity: EOF without closing brace");

Expand Down

7 comments on commit 764ef9d

@andrei-drexler
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sezero @j0zzz This issue is also present in QS and JoeQuake. It looks like newer versions of ericw-tools now allow fields longer than 1024 characters, which can overflow com_token used in COM_Parse.

Small test BSP that can trigger this: parsecrash.zip.

@sezero
Copy link

@sezero sezero commented on 764ef9d Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make it to crash on i686-linux as is, but valgrind screamed
only after I changed com_token from an array to a malloc'ed pointer
Thanks. And to think that the issue was actually implied in another
bug report: sezero#25 - sigh..

Will push this shortly after adapting.

P.S.: Change to COM_ParseStringNewline is irrelevant as far as I can
see (and it is different in QS, using sscanf.)

@sezero
Copy link

@sezero sezero commented on 764ef9d Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch applied to QS as sezero@584d3c6

@andrei-drexler
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make it to crash on i686-linux as is

The version of JoeQuake that I tested with didn't crash, either, but looking at the code, the issue is there as well. FWIW, the Win64 binary for QS 0.96.0 did crash.

P.S.: Change to COM_ParseStringNewline is irrelevant as far as I can see (and it is different in QS, using sscanf.)

Yup, not strictly relevant for this fix, but I wanted to remove the hardcoded 1023 in case the size of com_token is ever increased.

@j0zzz
Copy link

@j0zzz j0zzz commented on 764ef9d Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version of JoeQuake that I tested with didn't crash, either, but looking at the code, the issue is there as well.

It does in debug. Not sure why it doesn't in release, maybe due to compilation options...?
Anyways, after applying the changes, it does properly load the map, but saving game still makes it crash. Looking the issue right now.
@andrei-drexler Thank you very much for the info about this patch.

@j0zzz
Copy link

@j0zzz j0zzz commented on 764ef9d Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, found the issue with savegame, JoeQuake was missing this fix from QuakeSpasm:
sezero@15a41d2

@sezero
Copy link

@sezero sezero commented on 764ef9d Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, found the issue with savegame, JoeQuake was missing this fix from QuakeSpasm: sezero@15a41d2

Simplified that one: sezero@22e57e0

Please sign in to comment.