Commit 382e9fb5 authored by Dino Bollinger's avatar Dino Bollinger Committed by Richard Gobeille

Duke3D: improve how labels are loaded into memory and introduce MAXLABELS limit

Previous to this commit, the game would hijack the sprite, wall and sector
arrays to store the label data temporarily, which would get copied into
freshly allocated arrays once the scripts were fully parsed.

This lead to the maximum number of labels depending on MAXSPRITES.
This coupling is now removed, and an independent limit MAXLABELS
has been introduced, which is currently set to 16384.

Additionally, the limit is now checked before character data is loaded
into memory, rather than afterwards, preventing out of bounds accesses.
parent 48026d0f
......@@ -5880,8 +5880,8 @@ static void G_Cleanup(void)
for (i=MAXSOUNDS-1; i>=0; i--)
Xfree(g_sounds[i].filename);
if (label != (char *)&sprite[0]) Xfree(label);
if (labelcode != (int32_t *)&sector[0]) Xfree(labelcode);
Xfree(label);
Xfree(labelcode);
Xfree(apScript);
Xfree(bitptr);
......@@ -5935,14 +5935,13 @@ static void G_CompileScripts(void)
{
int32_t psm = pathsearchmode;
label = (char *)&sprite[0]; // V8: 16384*44/64 = 11264 V7: 4096*44/64 = 2816
labelcode = (int32_t *)&sector[0]; // V8: 4096*40/4 = 40960 V7: 1024*40/4 = 10240
labeltype = (uint8_t *)&wall[0]; // V8: 16384*32 = 524288 V7: 8192*32/4 = 262144
label = (char *) Xmalloc(MAXLABELS << 6);
labelcode = (int32_t *) Xmalloc(MAXLABELS * sizeof(int32_t));
labeltype = (uint8_t *) Xmalloc(MAXLABELS * sizeof(uint8_t));
if (g_scriptNamePtr != NULL)
Bcorrectfilename(g_scriptNamePtr,0);
// if we compile for a V7 engine wall[] should be used for label names since it's bigger
pathsearchmode = 1;
C_Compile(G_ConFile());
......@@ -5950,24 +5949,14 @@ static void G_CompileScripts(void)
if (g_loadFromGroupOnly) // g_loadFromGroupOnly is true only when compiling fails and internal defaults are utilized
C_Compile(G_ConFile());
if ((uint32_t)g_labelCnt > MAXSPRITES*sizeof(spritetype)/64) // see the arithmetic above for why
// for safety
if ((uint32_t)g_labelCnt >= MAXLABELS)
G_GameExit("Error: too many labels defined!");
auto newlabel = (char *)Xmalloc(g_labelCnt << 6);
auto newlabelcode = (int32_t *)Xmalloc(g_labelCnt * sizeof(int32_t));
auto newlabeltype = (uint8_t *)Xmalloc(g_labelCnt * sizeof(uint8_t));
Bmemcpy(newlabel, label, g_labelCnt * 64);
Bmemcpy(newlabelcode, labelcode, g_labelCnt * sizeof(int32_t));
Bmemcpy(newlabeltype, labeltype, g_labelCnt * sizeof(uint8_t));
label = newlabel;
labelcode = newlabelcode;
labeltype = newlabeltype;
Bmemset(sprite, 0, MAXSPRITES*sizeof(spritetype));
Bmemset(sector, 0, MAXSECTORS*sizeof(sectortype));
Bmemset(wall, 0, MAXWALLS*sizeof(walltype));
label = (char *) Xrealloc(label, g_labelCnt << 6);
labelcode = (int32_t *) Xrealloc(labelcode, g_labelCnt * sizeof(int32_t));
labeltype = (uint8_t *) Xrealloc(labeltype, g_labelCnt * sizeof(uint8_t));
VM_OnEvent(EVENT_INIT);
pathsearchmode = psm;
......
......@@ -1038,7 +1038,7 @@ uint8_t *bitptr; // pointer to bitmap of which bytecode positions contain pointe
hashtable_t h_arrays = { MAXGAMEARRAYS >> 1, NULL };
hashtable_t h_gamevars = { MAXGAMEVARS >> 1, NULL };
hashtable_t h_labels = { 11264 >> 1, NULL };
hashtable_t h_labels = { MAXLABELS >> 1, NULL };
static void C_SetScriptSize(int32_t newsize)
{
......@@ -1214,6 +1214,14 @@ static void C_GetNextLabelName(void)
{
int32_t i = 0;
if (EDUKE32_PREDICT_FALSE(g_labelCnt >= MAXLABELS))
{
g_errorCnt++;
C_ReportError(ERROR_TOOMANYLABELS);
G_GameExit("Error: too many labels defined!");
return;
}
C_SkipComments();
// while (ispecial(*textptr) == 0 && *textptr!='['&& *textptr!=']' && *textptr!='\t' && *textptr!='\n' && *textptr!='\r')
......@@ -6263,9 +6271,7 @@ static char const * C_ScriptVersionString(int32_t version)
void C_PrintStats(void)
{
initprintf("%d/%d labels, %d/%d variables, %d/%d arrays\n", g_labelCnt,
(int32_t) min((MAXSECTORS * sizeof(sectortype)/sizeof(int32_t)),
MAXSPRITES * sizeof(spritetype)/(1<<6)),
initprintf("%d/%d labels, %d/%d variables, %d/%d arrays\n", g_labelCnt, MAXLABELS,
g_gameVarCount, MAXGAMEVARS, g_gameArrayCount, MAXGAMEARRAYS);
int cnt = g_numXStrings;
......@@ -6578,6 +6584,9 @@ void C_ReportError(int error)
case ERROR_VARTYPEMISMATCH:
initprintf("%s:%d: error: variable `%s' is of the wrong type.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
break;
case ERROR_TOOMANYLABELS:
initprintf("%s:%d: error: too many labels defined! Maximum is %d\n.",g_scriptFileName,g_lineNumber, MAXLABELS);
break;
case WARNING_BADGAMEVAR:
initprintf("%s:%d: warning: variable `%s' should be either per-player OR per-actor, not both.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
break;
......
......@@ -48,6 +48,8 @@ enum
LABEL_ANY = -1,
};
#define MAXLABELS 16384
#define LABEL_CHAR sizeof(uint8_t)
#define LABEL_SHORT sizeof(uint16_t)
#define LABEL_INT sizeof(uint32_t)
......@@ -229,6 +231,7 @@ enum ScriptError_t
ERROR_NOTTOPLEVEL,
ERROR_PARAMUNDEFINED,
ERROR_SYNTAXERROR,
ERROR_TOOMANYLABELS,
ERROR_VARREADONLY,
ERROR_VARTYPEMISMATCH,
WARNING_ARRAYMASKSKEYWORD,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment