[chocolate-doom] 01/02: Add patches from upstream to add bounds checking to the ACS implementation
Fabian Greffrath
fabian at moszumanska.debian.org
Tue Jan 9 19:51:42 UTC 2018
This is an automated email from the git hooks/post-receive script.
fabian pushed a commit to annotated tag debian/3.0.0-2
in repository chocolate-doom.
commit f280d6c93b3ba024e797dc916750f8828e3944b7
Author: Fabian Greffrath <fabian at debian.org>
Date: Tue Jan 9 20:45:53 2018 +0100
Add patches from upstream to add bounds checking to the ACS implementation
---
...exen-Add-assertion-checking-for-ACS-stack.patch | 95 ++++
...-Add-bounds-checking-for-script-variables.patch | 132 ++++++
...xen-Add-bounds-checking-for-map-variables.patch | 124 +++++
...n-Add-bounds-checking-for-world-variables.patch | 124 +++++
...xen-Add-bounds-checking-for-strings-table.patch | 124 +++++
...-hexen-Add-doc-comments-for-new-functions.patch | 99 ++++
...Add-bounds-checking-for-CmdPrintCharacter.patch | 36 ++
...n-Eliminate-most-direct-usage-of-PCodePtr.patch | 344 ++++++++++++++
...exen-Add-validation-of-lump-offset-values.patch | 123 +++++
.../0010-hexen-Remove-PCodePtr-entirely.patch | 510 +++++++++++++++++++++
...011-hexen-Use-ReadCodeInt-to-parse-header.patch | 78 ++++
.../0012-hexen-Validate-ACS-instructions.patch | 32 ++
...eplace-ValidateLumpOffset-with-ReadOffset.patch | 282 ++++++++++++
...en-Validate-strings-during-header-parsing.patch | 39 ++
...hexen-Change-comment-to-American-spelling.patch | 34 ++
...-Add-extra-context-for-assertion-failures.patch | 66 +++
debian/patches/series | 16 +
17 files changed, 2258 insertions(+)
diff --git a/debian/patches/0001-hexen-Add-assertion-checking-for-ACS-stack.patch b/debian/patches/0001-hexen-Add-assertion-checking-for-ACS-stack.patch
new file mode 100644
index 0000000..9905a0c
--- /dev/null
+++ b/debian/patches/0001-hexen-Add-assertion-checking-for-ACS-stack.patch
@@ -0,0 +1,95 @@
+From 1797341f9cefd83fb1ea2d01c8447023ed1a6e1c Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:09:08 +0100
+Subject: [PATCH 01/16] hexen: Add assertion checking for ACS stack.
+
+The vanilla ACS VM does not do any kind of bounds checking on the
+stack, which has a limited size. If an attempt is made to exceed the
+vanilla limits, exit with an error.
+---
+ src/hexen/p_acs.c | 34 +++++++++++++++++++++++++++++++---
+ 1 file changed, 31 insertions(+), 3 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 8a7bb313..6e549402 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -39,9 +39,6 @@
+ #define TEXTURE_TOP 0
+ #define TEXTURE_MIDDLE 1
+ #define TEXTURE_BOTTOM 2
+-#define S_DROP ACScript->stackPtr--
+-#define S_POP ACScript->stack[--ACScript->stackPtr]
+-#define S_PUSH(x) ACScript->stack[ACScript->stackPtr++] = x
+
+ // TYPES -------------------------------------------------------------------
+
+@@ -302,6 +299,31 @@ static int (*PCodeCmds[]) (void) =
+
+ // CODE --------------------------------------------------------------------
+
++//==========================================================================
++//
++// ACSAssert
++//
++// Check that the given condition evaluates to true. If it does not, exit
++// with an I_Error() printing the given message.
++//
++//==========================================================================
++
++static void ACSAssert(int condition, char *fmt, ...)
++{
++ char buf[128];
++ va_list args;
++
++ if (condition)
++ {
++ return;
++ }
++
++ va_start(args, fmt);
++ M_vsnprintf(buf, sizeof(buf), fmt, args);
++ va_end(args);
++ I_Error("ACS assertation failure: %s", buf);
++}
++
+ //==========================================================================
+ //
+ // P_LoadACScripts
+@@ -810,6 +832,9 @@ void CheckACSPresent(int number)
+
+ static void Push(int value)
+ {
++ ACSAssert(ACScript->stackPtr < ACS_STACK_DEPTH,
++ "maximum stack depth exceeded: %d >= %d",
++ ACScript->stackPtr, ACS_STACK_DEPTH);
+ ACScript->stack[ACScript->stackPtr++] = value;
+ }
+
+@@ -821,6 +846,7 @@ static void Push(int value)
+
+ static int Pop(void)
+ {
++ ACSAssert(ACScript->stackPtr > 0, "pop of empty stack");
+ return ACScript->stack[--ACScript->stackPtr];
+ }
+
+@@ -832,6 +858,7 @@ static int Pop(void)
+
+ static int Top(void)
+ {
++ ACSAssert(ACScript->stackPtr > 0, "read from top of empty stack");
+ return ACScript->stack[ACScript->stackPtr - 1];
+ }
+
+@@ -843,6 +870,7 @@ static int Top(void)
+
+ static void Drop(void)
+ {
++ ACSAssert(ACScript->stackPtr > 0, "drop on empty stack");
+ ACScript->stackPtr--;
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0002-hexen-Add-bounds-checking-for-script-variables.patch b/debian/patches/0002-hexen-Add-bounds-checking-for-script-variables.patch
new file mode 100644
index 0000000..aed4a4f
--- /dev/null
+++ b/debian/patches/0002-hexen-Add-bounds-checking-for-script-variables.patch
@@ -0,0 +1,132 @@
+From 48af05e08f0ebe20fab76b9f8ab54cab085630f8 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:21:25 +0100
+Subject: [PATCH 02/16] hexen: Add bounds checking for script variables.
+
+Scripts have a fixed number of variables and the limit should not
+be exceeded.
+---
+ src/hexen/p_acs.c | 44 ++++++++++++++++++++++++++------------------
+ 1 file changed, 26 insertions(+), 18 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 6e549402..2197e984 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -874,6 +874,23 @@ static void Drop(void)
+ ACScript->stackPtr--;
+ }
+
++static int ReadCodeImmediate(void)
++{
++ int result;
++ result = *PCodePtr;
++ ++PCodePtr;
++ return result;
++}
++
++static int ReadScriptVar(void)
++{
++ int var = ReadCodeImmediate();
++ ACSAssert(var >= 0, "negative script variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_SCRIPT_VARS,
++ "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
++ return var;
++}
++
+ //==========================================================================
+ //
+ // P-Code Commands
+@@ -1147,8 +1164,7 @@ static int CmdGE(void)
+
+ static int CmdAssignScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] = Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] = Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1168,8 +1184,7 @@ static int CmdAssignWorldVar(void)
+
+ static int CmdPushScriptVar(void)
+ {
+- Push(ACScript->vars[LONG(*PCodePtr)]);
+- ++PCodePtr;
++ Push(ACScript->vars[ReadScriptVar()]);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1189,8 +1204,7 @@ static int CmdPushWorldVar(void)
+
+ static int CmdAddScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] += Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] += Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1210,8 +1224,7 @@ static int CmdAddWorldVar(void)
+
+ static int CmdSubScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] -= Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] -= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1231,8 +1244,7 @@ static int CmdSubWorldVar(void)
+
+ static int CmdMulScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] *= Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] *= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1252,8 +1264,7 @@ static int CmdMulWorldVar(void)
+
+ static int CmdDivScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] /= Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] /= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1273,8 +1284,7 @@ static int CmdDivWorldVar(void)
+
+ static int CmdModScriptVar(void)
+ {
+- ACScript->vars[LONG(*PCodePtr)] %= Pop();
+- ++PCodePtr;
++ ACScript->vars[ReadScriptVar()] %= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1294,8 +1304,7 @@ static int CmdModWorldVar(void)
+
+ static int CmdIncScriptVar(void)
+ {
+- ++ACScript->vars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ ++ACScript->vars[ReadScriptVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1315,8 +1324,7 @@ static int CmdIncWorldVar(void)
+
+ static int CmdDecScriptVar(void)
+ {
+- --ACScript->vars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ --ACScript->vars[ReadScriptVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0003-hexen-Add-bounds-checking-for-map-variables.patch b/debian/patches/0003-hexen-Add-bounds-checking-for-map-variables.patch
new file mode 100644
index 0000000..830c6f5
--- /dev/null
+++ b/debian/patches/0003-hexen-Add-bounds-checking-for-map-variables.patch
@@ -0,0 +1,124 @@
+From 005a79c6528d7b98c37f24e548e805f6435f0e36 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:24:29 +0100
+Subject: [PATCH 03/16] hexen: Add bounds checking for map variables.
+
+There is a fixed number of map variables and the limit should not
+be exceeded.
+---
+ src/hexen/p_acs.c | 36 ++++++++++++++++++------------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 2197e984..8a491a7b 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -891,6 +891,15 @@ static int ReadScriptVar(void)
+ return var;
+ }
+
++static int ReadMapVar(void)
++{
++ int var = ReadCodeImmediate();
++ ACSAssert(var >= 0, "negative map variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_MAP_VARS,
++ "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS);
++ return var;
++}
++
+ //==========================================================================
+ //
+ // P-Code Commands
+@@ -1170,8 +1179,7 @@ static int CmdAssignScriptVar(void)
+
+ static int CmdAssignMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] = Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] = Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1190,8 +1198,7 @@ static int CmdPushScriptVar(void)
+
+ static int CmdPushMapVar(void)
+ {
+- Push(MapVars[LONG(*PCodePtr)]);
+- ++PCodePtr;
++ Push(MapVars[ReadMapVar()]);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1210,8 +1217,7 @@ static int CmdAddScriptVar(void)
+
+ static int CmdAddMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] += Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] += Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1230,8 +1236,7 @@ static int CmdSubScriptVar(void)
+
+ static int CmdSubMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] -= Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] -= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1250,8 +1255,7 @@ static int CmdMulScriptVar(void)
+
+ static int CmdMulMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] *= Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] *= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1270,8 +1274,7 @@ static int CmdDivScriptVar(void)
+
+ static int CmdDivMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] /= Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] /= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1290,8 +1293,7 @@ static int CmdModScriptVar(void)
+
+ static int CmdModMapVar(void)
+ {
+- MapVars[LONG(*PCodePtr)] %= Pop();
+- ++PCodePtr;
++ MapVars[ReadMapVar()] %= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1310,8 +1312,7 @@ static int CmdIncScriptVar(void)
+
+ static int CmdIncMapVar(void)
+ {
+- ++MapVars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ ++MapVars[ReadMapVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1330,8 +1331,7 @@ static int CmdDecScriptVar(void)
+
+ static int CmdDecMapVar(void)
+ {
+- --MapVars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ --MapVars[ReadMapVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0004-hexen-Add-bounds-checking-for-world-variables.patch b/debian/patches/0004-hexen-Add-bounds-checking-for-world-variables.patch
new file mode 100644
index 0000000..38dfe79
--- /dev/null
+++ b/debian/patches/0004-hexen-Add-bounds-checking-for-world-variables.patch
@@ -0,0 +1,124 @@
+From 118d869f5b104563284ac033e156a6bb85a4d642 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:27:11 +0100
+Subject: [PATCH 04/16] hexen: Add bounds checking for world variables.
+
+There is a fixed number of world variables and the limit should not
+be exceeded.
+---
+ src/hexen/p_acs.c | 36 ++++++++++++++++++------------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 8a491a7b..c33c1f17 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -900,6 +900,15 @@ static int ReadMapVar(void)
+ return var;
+ }
+
++static int ReadWorldVar(void)
++{
++ int var = ReadCodeImmediate();
++ ACSAssert(var >= 0, "negative world variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_WORLD_VARS,
++ "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS);
++ return var;
++}
++
+ //==========================================================================
+ //
+ // P-Code Commands
+@@ -1185,8 +1194,7 @@ static int CmdAssignMapVar(void)
+
+ static int CmdAssignWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] = Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] = Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1204,8 +1212,7 @@ static int CmdPushMapVar(void)
+
+ static int CmdPushWorldVar(void)
+ {
+- Push(WorldVars[LONG(*PCodePtr)]);
+- ++PCodePtr;
++ Push(WorldVars[ReadWorldVar()]);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1223,8 +1230,7 @@ static int CmdAddMapVar(void)
+
+ static int CmdAddWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] += Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] += Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1242,8 +1248,7 @@ static int CmdSubMapVar(void)
+
+ static int CmdSubWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] -= Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] -= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1261,8 +1266,7 @@ static int CmdMulMapVar(void)
+
+ static int CmdMulWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] *= Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] *= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1280,8 +1284,7 @@ static int CmdDivMapVar(void)
+
+ static int CmdDivWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] /= Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] /= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1299,8 +1302,7 @@ static int CmdModMapVar(void)
+
+ static int CmdModWorldVar(void)
+ {
+- WorldVars[LONG(*PCodePtr)] %= Pop();
+- ++PCodePtr;
++ WorldVars[ReadWorldVar()] %= Pop();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1318,8 +1320,7 @@ static int CmdIncMapVar(void)
+
+ static int CmdIncWorldVar(void)
+ {
+- ++WorldVars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ ++WorldVars[ReadWorldVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1337,8 +1338,7 @@ static int CmdDecMapVar(void)
+
+ static int CmdDecWorldVar(void)
+ {
+- --WorldVars[LONG(*PCodePtr)];
+- ++PCodePtr;
++ --WorldVars[ReadWorldVar()];
+ return SCRIPT_CONTINUE;
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0005-hexen-Add-bounds-checking-for-strings-table.patch b/debian/patches/0005-hexen-Add-bounds-checking-for-strings-table.patch
new file mode 100644
index 0000000..b0c0409
--- /dev/null
+++ b/debian/patches/0005-hexen-Add-bounds-checking-for-strings-table.patch
@@ -0,0 +1,124 @@
+From 07109cb24fab1ecde3f2abc8220201f3da99f4e8 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:36:31 +0100
+Subject: [PATCH 05/16] hexen: Add bounds checking for strings table.
+
+ACS lumps provide a strings table but we must enforce that all lookups
+into this table are properly bounds checked.
+---
+ src/hexen/p_acs.c | 29 +++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index c33c1f17..d52d8a74 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -909,6 +909,15 @@ static int ReadWorldVar(void)
+ return var;
+ }
+
++static char *StringLookup(int string_index)
++{
++ ACSAssert(string_index >= 0,
++ "negative string index: %d < 0", string_index);
++ ACSAssert(string_index < ACStringCount,
++ "invalid string index: %d >= %d", string_index, ACStringCount);
++ return ACStrings[string_index];
++}
++
+ //==========================================================================
+ //
+ // P-Code Commands
+@@ -1517,7 +1526,7 @@ static int CmdChangeFloor(void)
+ int flat;
+ int sectorIndex;
+
+- flat = R_FlatNumForName(ACStrings[Pop()]);
++ flat = R_FlatNumForName(StringLookup(Pop()));
+ tag = Pop();
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+@@ -1535,7 +1544,7 @@ static int CmdChangeFloorDirect(void)
+
+ tag = LONG(*PCodePtr);
+ ++PCodePtr;
+- flat = R_FlatNumForName(ACStrings[LONG(*PCodePtr)]);
++ flat = R_FlatNumForName(StringLookup(LONG(*PCodePtr)));
+ ++PCodePtr;
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+@@ -1551,7 +1560,7 @@ static int CmdChangeCeiling(void)
+ int flat;
+ int sectorIndex;
+
+- flat = R_FlatNumForName(ACStrings[Pop()]);
++ flat = R_FlatNumForName(StringLookup(Pop()));
+ tag = Pop();
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+@@ -1569,7 +1578,7 @@ static int CmdChangeCeilingDirect(void)
+
+ tag = LONG(*PCodePtr);
+ ++PCodePtr;
+- flat = R_FlatNumForName(ACStrings[LONG(*PCodePtr)]);
++ flat = R_FlatNumForName(StringLookup(LONG(*PCodePtr)));
+ ++PCodePtr;
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+@@ -1746,7 +1755,7 @@ static int CmdEndPrintBold(void)
+
+ static int CmdPrintString(void)
+ {
+- M_StringConcat(PrintBuffer, ACStrings[Pop()], sizeof(PrintBuffer));
++ M_StringConcat(PrintBuffer, StringLookup(Pop()), sizeof(PrintBuffer));
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1826,7 +1835,7 @@ static int CmdSectorSound(void)
+ mobj = (mobj_t *) & ACScript->line->frontsector->soundorg;
+ }
+ volume = Pop();
+- S_StartSoundAtVolume(mobj, S_GetSoundID(ACStrings[Pop()]), volume);
++ S_StartSoundAtVolume(mobj, S_GetSoundID(StringLookup(Pop())), volume);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1839,7 +1848,7 @@ static int CmdThingSound(void)
+ int searcher;
+
+ volume = Pop();
+- sound = S_GetSoundID(ACStrings[Pop()]);
++ sound = S_GetSoundID(StringLookup(Pop()));
+ tid = Pop();
+ searcher = -1;
+ while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL)
+@@ -1854,7 +1863,7 @@ static int CmdAmbientSound(void)
+ int volume;
+
+ volume = Pop();
+- S_StartSoundAtVolume(NULL, S_GetSoundID(ACStrings[Pop()]), volume);
++ S_StartSoundAtVolume(NULL, S_GetSoundID(StringLookup(Pop())), volume);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1867,7 +1876,7 @@ static int CmdSoundSequence(void)
+ {
+ mobj = (mobj_t *) & ACScript->line->frontsector->soundorg;
+ }
+- SN_StartSequenceName(mobj, ACStrings[Pop()]);
++ SN_StartSequenceName(mobj, StringLookup(Pop()));
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1880,7 +1889,7 @@ static int CmdSetLineTexture(void)
+ int texture;
+ int searcher;
+
+- texture = R_TextureNumForName(ACStrings[Pop()]);
++ texture = R_TextureNumForName(StringLookup(Pop()));
+ position = Pop();
+ side = Pop();
+ lineTag = Pop();
+--
+2.15.1
+
diff --git a/debian/patches/0006-hexen-Add-doc-comments-for-new-functions.patch b/debian/patches/0006-hexen-Add-doc-comments-for-new-functions.patch
new file mode 100644
index 0000000..9988c1e
--- /dev/null
+++ b/debian/patches/0006-hexen-Add-doc-comments-for-new-functions.patch
@@ -0,0 +1,99 @@
+From 714b700b1cf7f9cb0f6dca3c9bc7b0653a74f270 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 01:47:19 +0100
+Subject: [PATCH 06/16] hexen: Add doc comments for new functions.
+
+---
+ src/hexen/p_acs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index d52d8a74..f0cf190b 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -874,14 +874,34 @@ static void Drop(void)
+ ACScript->stackPtr--;
+ }
+
++//==========================================================================
++//
++// ReadCodeImmediate
++//
++// Some instructions take "immediate" parameters which are stored in the
++// bytecode immediately following the instruction. This function should be
++// used to read them.
++//
++//==========================================================================
++
+ static int ReadCodeImmediate(void)
+ {
+ int result;
++ // TODO: Add bounds checking
+ result = *PCodePtr;
+ ++PCodePtr;
+ return result;
+ }
+
++//==========================================================================
++//
++// ReadScriptVar
++//
++// Read a script variable index as an immediate value, validating the
++// result is a valid script variable number.
++//
++//==========================================================================
++
+ static int ReadScriptVar(void)
+ {
+ int var = ReadCodeImmediate();
+@@ -891,6 +911,15 @@ static int ReadScriptVar(void)
+ return var;
+ }
+
++//==========================================================================
++//
++// ReadMapVar
++//
++// Read a map variable index as an immediate value, validating the
++// result is a valid map variable number.
++//
++//==========================================================================
++
+ static int ReadMapVar(void)
+ {
+ int var = ReadCodeImmediate();
+@@ -900,6 +929,15 @@ static int ReadMapVar(void)
+ return var;
+ }
+
++//==========================================================================
++//
++// ReadWorldVar
++//
++// Read a world variable index as an immediate value, validating the
++// result is a valid world variable number.
++//
++//==========================================================================
++
+ static int ReadWorldVar(void)
+ {
+ int var = ReadCodeImmediate();
+@@ -909,6 +947,15 @@ static int ReadWorldVar(void)
+ return var;
+ }
+
++//==========================================================================
++//
++// StringLookup
++//
++// Look up the given string in the strings table by index, validating that
++// it is a valid string index.
++//
++//==========================================================================
++
+ static char *StringLookup(int string_index)
+ {
+ ACSAssert(string_index >= 0,
+--
+2.15.1
+
diff --git a/debian/patches/0007-hexen-Add-bounds-checking-for-CmdPrintCharacter.patch b/debian/patches/0007-hexen-Add-bounds-checking-for-CmdPrintCharacter.patch
new file mode 100644
index 0000000..cb9759b
--- /dev/null
+++ b/debian/patches/0007-hexen-Add-bounds-checking-for-CmdPrintCharacter.patch
@@ -0,0 +1,36 @@
+From 5e51c2032749df58fadc8182da345acb18b19885 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 12:41:32 +0100
+Subject: [PATCH 07/16] hexen: Add bounds checking for CmdPrintCharacter.
+
+If adding another character to the print buffer would cause a buffer
+overflow, don't exceed the limits of the buffer. Similar protection
+is already in place for CmdPrintString and CmdPrintNumber.
+---
+ src/hexen/p_acs.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index f0cf190b..16725b74 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -1817,11 +1817,12 @@ static int CmdPrintNumber(void)
+
+ static int CmdPrintCharacter(void)
+ {
+- char *bufferEnd;
++ char tempStr[2];
++
++ tempStr[0] = Pop();
++ tempStr[1] = '\0';
++ M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer));
+
+- bufferEnd = PrintBuffer + strlen(PrintBuffer);
+- *bufferEnd++ = Pop();
+- *bufferEnd = 0;
+ return SCRIPT_CONTINUE;
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0008-hexen-Eliminate-most-direct-usage-of-PCodePtr.patch b/debian/patches/0008-hexen-Eliminate-most-direct-usage-of-PCodePtr.patch
new file mode 100644
index 0000000..46dccf5
--- /dev/null
+++ b/debian/patches/0008-hexen-Eliminate-most-direct-usage-of-PCodePtr.patch
@@ -0,0 +1,344 @@
+From 4ec7962877652b2ea56a9ae50ee8ff1921e06290 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 19:44:38 +0100
+Subject: [PATCH 08/16] hexen: Eliminate most direct usage of PCodePtr.
+
+ReadCodeImmediate() will be a safer and simpler way to read additional
+arguments from the code buffer.
+---
+ src/hexen/p_acs.c | 151 ++++++++++++++++++++----------------------------------
+ 1 file changed, 55 insertions(+), 96 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 16725b74..4c9d153d 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -888,7 +888,7 @@ static int ReadCodeImmediate(void)
+ {
+ int result;
+ // TODO: Add bounds checking
+- result = *PCodePtr;
++ result = LONG(*PCodePtr);
+ ++PCodePtr;
+ return result;
+ }
+@@ -989,8 +989,7 @@ static int CmdSuspend(void)
+
+ static int CmdPushNumber(void)
+ {
+- Push(LONG(*PCodePtr));
+- ++PCodePtr;
++ Push(ReadCodeImmediate());
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -998,8 +997,7 @@ static int CmdLSpec1(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
+ SpecArgs[0] = Pop();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+@@ -1010,8 +1008,7 @@ static int CmdLSpec2(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
+ SpecArgs[1] = Pop();
+ SpecArgs[0] = Pop();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+@@ -1023,8 +1020,7 @@ static int CmdLSpec3(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
+ SpecArgs[2] = Pop();
+ SpecArgs[1] = Pop();
+ SpecArgs[0] = Pop();
+@@ -1037,8 +1033,7 @@ static int CmdLSpec4(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
+ SpecArgs[3] = Pop();
+ SpecArgs[2] = Pop();
+ SpecArgs[1] = Pop();
+@@ -1052,8 +1047,7 @@ static int CmdLSpec5(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
+ SpecArgs[4] = Pop();
+ SpecArgs[3] = Pop();
+ SpecArgs[2] = Pop();
+@@ -1068,10 +1062,8 @@ static int CmdLSpec1Direct(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[0] = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
++ SpecArgs[0] = ReadCodeImmediate();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1081,12 +1073,9 @@ static int CmdLSpec2Direct(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[0] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[1] = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
++ SpecArgs[0] = ReadCodeImmediate();
++ SpecArgs[1] = ReadCodeImmediate();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1096,14 +1085,10 @@ static int CmdLSpec3Direct(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[0] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[1] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[2] = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
++ SpecArgs[0] = ReadCodeImmediate();
++ SpecArgs[1] = ReadCodeImmediate();
++ SpecArgs[2] = ReadCodeImmediate();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1113,16 +1098,11 @@ static int CmdLSpec4Direct(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[0] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[1] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[2] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[3] = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
++ SpecArgs[0] = ReadCodeImmediate();
++ SpecArgs[1] = ReadCodeImmediate();
++ SpecArgs[2] = ReadCodeImmediate();
++ SpecArgs[3] = ReadCodeImmediate();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1132,18 +1112,12 @@ static int CmdLSpec5Direct(void)
+ {
+ int special;
+
+- special = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[0] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[1] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[2] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[3] = LONG(*PCodePtr);
+- ++PCodePtr;
+- SpecArgs[4] = LONG(*PCodePtr);
+- ++PCodePtr;
++ special = ReadCodeImmediate();
++ SpecArgs[0] = ReadCodeImmediate();
++ SpecArgs[1] = ReadCodeImmediate();
++ SpecArgs[2] = ReadCodeImmediate();
++ SpecArgs[3] = ReadCodeImmediate();
++ SpecArgs[4] = ReadCodeImmediate();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1400,19 +1374,19 @@ static int CmdDecWorldVar(void)
+
+ static int CmdGoto(void)
+ {
+- PCodePtr = (int *) (ActionCodeBase + LONG(*PCodePtr));
++ PCodePtr = (int *) (ActionCodeBase + ReadCodeImmediate());
+ return SCRIPT_CONTINUE;
+ }
+
+ static int CmdIfGoto(void)
+ {
++ int offset;
++
++ offset = ReadCodeImmediate();
++
+ if (Pop() != 0)
+ {
+- PCodePtr = (int *) (ActionCodeBase + LONG(*PCodePtr));
+- }
+- else
+- {
+- ++PCodePtr;
++ PCodePtr = (int *) (ActionCodeBase + offset);
+ }
+ return SCRIPT_CONTINUE;
+ }
+@@ -1431,8 +1405,7 @@ static int CmdDelay(void)
+
+ static int CmdDelayDirect(void)
+ {
+- ACScript->delayCount = LONG(*PCodePtr);
+- ++PCodePtr;
++ ACScript->delayCount = ReadCodeImmediate();
+ return SCRIPT_STOP;
+ }
+
+@@ -1452,10 +1425,8 @@ static int CmdRandomDirect(void)
+ int low;
+ int high;
+
+- low = LONG(*PCodePtr);
+- ++PCodePtr;
+- high = LONG(*PCodePtr);
+- ++PCodePtr;
++ low = ReadCodeImmediate();
++ high = ReadCodeImmediate();
+ Push(low + (P_Random() % (high - low + 1)));
+ return SCRIPT_CONTINUE;
+ }
+@@ -1473,10 +1444,8 @@ static int CmdThingCountDirect(void)
+ {
+ int type;
+
+- type = LONG(*PCodePtr);
+- ++PCodePtr;
+- ThingCount(type, LONG(*PCodePtr));
+- ++PCodePtr;
++ type = ReadCodeImmediate();
++ ThingCount(type, ReadCodeImmediate());
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1546,8 +1515,7 @@ static int CmdTagWait(void)
+
+ static int CmdTagWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = LONG(*PCodePtr);
+- ++PCodePtr;
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG;
+ return SCRIPT_STOP;
+ }
+@@ -1561,8 +1529,7 @@ static int CmdPolyWait(void)
+
+ static int CmdPolyWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = LONG(*PCodePtr);
+- ++PCodePtr;
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY;
+ return SCRIPT_STOP;
+ }
+@@ -1589,10 +1556,8 @@ static int CmdChangeFloorDirect(void)
+ int flat;
+ int sectorIndex;
+
+- tag = LONG(*PCodePtr);
+- ++PCodePtr;
+- flat = R_FlatNumForName(StringLookup(LONG(*PCodePtr)));
+- ++PCodePtr;
++ tag = ReadCodeImmediate();
++ flat = R_FlatNumForName(StringLookup(ReadCodeImmediate()));
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+ {
+@@ -1623,10 +1588,8 @@ static int CmdChangeCeilingDirect(void)
+ int flat;
+ int sectorIndex;
+
+- tag = LONG(*PCodePtr);
+- ++PCodePtr;
+- flat = R_FlatNumForName(StringLookup(LONG(*PCodePtr)));
+- ++PCodePtr;
++ tag = ReadCodeImmediate();
++ flat = R_FlatNumForName(StringLookup(ReadCodeImmediate()));
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+ {
+@@ -1703,13 +1666,13 @@ static int CmdUnaryMinus(void)
+
+ static int CmdIfNotGoto(void)
+ {
+- if (Pop() != 0)
+- {
+- ++PCodePtr;
+- }
+- else
++ int offset;
++
++ offset = ReadCodeImmediate();
++
++ if (Pop() == 0)
+ {
+- PCodePtr = (int *) (ActionCodeBase + LONG(*PCodePtr));
++ PCodePtr = (int *) (ActionCodeBase + offset);
+ }
+ return SCRIPT_CONTINUE;
+ }
+@@ -1729,8 +1692,7 @@ static int CmdScriptWait(void)
+
+ static int CmdScriptWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = LONG(*PCodePtr);
+- ++PCodePtr;
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT;
+ return SCRIPT_STOP;
+ }
+@@ -1747,19 +1709,16 @@ static int CmdClearLineSpecial(void)
+ static int CmdCaseGoto(void)
+ {
+ int value;
++ int offset;
+
+- value = LONG(*PCodePtr);
+- ++PCodePtr;
++ value = ReadCodeImmediate();
++ offset = ReadCodeImmediate();
+
+ if (Top() == value)
+ {
+- PCodePtr = (int *) (ActionCodeBase + LONG(*PCodePtr));
++ PCodePtr = (int *) (ActionCodeBase + offset);
+ Drop();
+ }
+- else
+- {
+- ++PCodePtr;
+- }
+
+ return SCRIPT_CONTINUE;
+ }
+--
+2.15.1
+
diff --git a/debian/patches/0009-hexen-Add-validation-of-lump-offset-values.patch b/debian/patches/0009-hexen-Add-validation-of-lump-offset-values.patch
new file mode 100644
index 0000000..ef11af1
--- /dev/null
+++ b/debian/patches/0009-hexen-Add-validation-of-lump-offset-values.patch
@@ -0,0 +1,123 @@
+From fe9adcbd0b0e9b8e6ac744967bbf0ce3fcf40546 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:02:06 +0100
+Subject: [PATCH 09/16] hexen: Add validation of lump offset values.
+
+Offsets like these (used for eg. CmdGoto instructions) should point to
+a location inside the lump; otherwise it is an error and we should fail
+an assertion.
+---
+ src/hexen/p_acs.c | 39 ++++++++++++++++++++++++++++++++-------
+ 1 file changed, 32 insertions(+), 7 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 4c9d153d..1636d774 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -176,6 +176,7 @@ static void ThingCount(int type, int tid);
+
+ int ACScriptCount;
+ byte *ActionCodeBase;
++static int ActionCodeSize;
+ acsInfo_t *ACSInfo;
+ int MapVars[MAX_ACS_MAP_VARS];
+ int WorldVars[MAX_ACS_WORLD_VARS];
+@@ -324,6 +325,23 @@ static void ACSAssert(int condition, char *fmt, ...)
+ I_Error("ACS assertation failure: %s", buf);
+ }
+
++//==========================================================================
++//
++// ValidateLumpOffset
++//
++// Check that the given offset is a valid location inside the loaded ACS
++// lump and does not point outside it.
++//
++//==========================================================================
++
++static int ValidateLumpOffset(int offset, char *where)
++{
++ ACSAssert(offset >= 0, "negative lump offset in %s", where);
++ ACSAssert(offset < ActionCodeSize, "invalid lump offset in %s: %d >= %d",
++ where, offset, ActionCodeSize);
++ return offset;
++}
++
+ //==========================================================================
+ //
+ // P_LoadACScripts
+@@ -339,9 +357,10 @@ void P_LoadACScripts(int lump)
+
+ header = W_CacheLumpNum(lump, PU_LEVEL);
+ ActionCodeBase = (byte *) header;
++ ActionCodeSize = W_LumpLength(lump);
+ buffer = (int *) ((byte *) header + LONG(header->infoOffset));
+
+- ACScriptCount = LONG(*buffer);
++ ACScriptCount = LONG(*buffer);
+ ++buffer;
+
+ if (ACScriptCount == 0)
+@@ -356,7 +375,9 @@ void P_LoadACScripts(int lump)
+ info->number = LONG(*buffer);
+ ++buffer;
+
+- info->address = (int *) ((byte *) ActionCodeBase + LONG(*buffer));
++ info->address = (int *) (
++ (byte *) ActionCodeBase +
++ ValidateLumpOffset(LONG(*buffer), "script header"));
+ ++buffer;
+
+ info->argCount = LONG(*buffer);
+@@ -390,7 +411,8 @@ void P_LoadACScripts(int lump)
+
+ for (i=0; i<ACStringCount; ++i)
+ {
+- ACStrings[i] = (char *) ActionCodeBase + LONG(buffer[i]);
++ ACStrings[i] = (char *) ActionCodeBase +
++ ValidateLumpOffset(LONG(buffer[i]), "string header");
+ }
+
+ memset(MapVars, 0, sizeof(MapVars));
+@@ -1374,7 +1396,10 @@ static int CmdDecWorldVar(void)
+
+ static int CmdGoto(void)
+ {
+- PCodePtr = (int *) (ActionCodeBase + ReadCodeImmediate());
++ int offset;
++
++ offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdGoto parameter");
++ PCodePtr = (int *) (ActionCodeBase + offset);
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1382,7 +1407,7 @@ static int CmdIfGoto(void)
+ {
+ int offset;
+
+- offset = ReadCodeImmediate();
++ offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdIfGoto parameter");
+
+ if (Pop() != 0)
+ {
+@@ -1668,7 +1693,7 @@ static int CmdIfNotGoto(void)
+ {
+ int offset;
+
+- offset = ReadCodeImmediate();
++ offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdIfNotGoto parameter");
+
+ if (Pop() == 0)
+ {
+@@ -1712,7 +1737,7 @@ static int CmdCaseGoto(void)
+ int offset;
+
+ value = ReadCodeImmediate();
+- offset = ReadCodeImmediate();
++ offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdCaseGoto parameter");
+
+ if (Top() == value)
+ {
+--
+2.15.1
+
diff --git a/debian/patches/0010-hexen-Remove-PCodePtr-entirely.patch b/debian/patches/0010-hexen-Remove-PCodePtr-entirely.patch
new file mode 100644
index 0000000..554b833
--- /dev/null
+++ b/debian/patches/0010-hexen-Remove-PCodePtr-entirely.patch
@@ -0,0 +1,510 @@
+From 53e98bc707cc0a37773985cfdce5419a7491da26 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:19:26 +0100
+Subject: [PATCH 10/16] hexen: Remove PCodePtr entirely.
+
+Stop using pointers to represent code location; instead use an offset
+from the start of the lump. This can be continually validated for
+correctness and we exit with an error if we pass the end of the lump.
+---
+ src/hexen/p_acs.c | 171 ++++++++++++++++++++++++++--------------------------
+ src/hexen/p_spec.h | 4 +-
+ src/hexen/sv_save.c | 5 +-
+ 3 files changed, 88 insertions(+), 92 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 1636d774..f3631faa 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -55,7 +55,7 @@ typedef PACKED_STRUCT (
+
+ // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+
+-static void StartOpenACS(int number, int infoIndex, int *address);
++static void StartOpenACS(int number, int infoIndex, int offset);
+ static void ScriptFinished(int number);
+ static boolean TagBusy(int tag);
+ static boolean AddToACSStore(int map, int number, byte * args);
+@@ -185,7 +185,7 @@ acsstore_t ACSStore[MAX_ACS_STORE + 1]; // +1 for termination marker
+ // PRIVATE DATA DEFINITIONS ------------------------------------------------
+
+ static acs_t *ACScript;
+-static int *PCodePtr;
++static unsigned int PCodeOffset;
+ static byte SpecArgs[8];
+ static int ACStringCount;
+ static char **ACStrings;
+@@ -342,6 +342,29 @@ static int ValidateLumpOffset(int offset, char *where)
+ return offset;
+ }
+
++//==========================================================================
++//
++// ReadCodeInt
++//
++// Read a 32-bit value from the loaded ACS lump at the location pointed to
++// by PCodeOffset, advancing PCodeOffset to the next value in the process.
++//
++//==========================================================================
++
++static int ReadCodeInt(void)
++{
++ int result;
++ int *ptr;
++
++ ValidateLumpOffset(PCodeOffset + 3, "ReadCodeInt");
++
++ ptr = (int *) (ActionCodeBase + PCodeOffset);
++ result = LONG(*ptr);
++ PCodeOffset += 4;
++
++ return result;
++}
++
+ //==========================================================================
+ //
+ // P_LoadACScripts
+@@ -375,9 +398,7 @@ void P_LoadACScripts(int lump)
+ info->number = LONG(*buffer);
+ ++buffer;
+
+- info->address = (int *) (
+- (byte *) ActionCodeBase +
+- ValidateLumpOffset(LONG(*buffer), "script header"));
++ info->offset = ValidateLumpOffset(LONG(*buffer), "script header");
+ ++buffer;
+
+ info->argCount = LONG(*buffer);
+@@ -396,7 +417,7 @@ void P_LoadACScripts(int lump)
+ if (info->number >= OPEN_SCRIPTS_BASE)
+ { // Auto-activate
+ info->number -= OPEN_SCRIPTS_BASE;
+- StartOpenACS(info->number, i, info->address);
++ StartOpenACS(info->number, i, info->offset);
+ info->state = ASTE_RUNNING;
+ }
+ else
+@@ -424,7 +445,7 @@ void P_LoadACScripts(int lump)
+ //
+ //==========================================================================
+
+-static void StartOpenACS(int number, int infoIndex, int *address)
++static void StartOpenACS(int number, int infoIndex, int offset)
+ {
+ acs_t *script;
+
+@@ -436,7 +457,7 @@ static void StartOpenACS(int number, int infoIndex, int *address)
+ script->delayCount = 35;
+
+ script->infoIndex = infoIndex;
+- script->ip = address;
++ script->ip = offset;
+ script->thinker.function = T_InterpretACS;
+ P_AddThinker(&script->thinker);
+ }
+@@ -517,7 +538,7 @@ boolean P_StartACS(int number, int map, byte * args, mobj_t * activator,
+ script->activator = activator;
+ script->line = line;
+ script->side = side;
+- script->ip = ACSInfo[infoIndex].address;
++ script->ip = ACSInfo[infoIndex].offset;
+ script->thinker.function = T_InterpretACS;
+ for (i = 0; i < MAX_SCRIPT_ARGS && i < ACSInfo[infoIndex].argCount; i++)
+ {
+@@ -697,17 +718,15 @@ void T_InterpretACS(acs_t * script)
+ return;
+ }
+ ACScript = script;
+- PCodePtr = ACScript->ip;
++ PCodeOffset = ACScript->ip;
+
+ do
+ {
+- cmd = LONG(*PCodePtr);
+- ++PCodePtr;
+-
+- action = PCodeCmds[cmd] ();
++ cmd = ReadCodeInt();
++ action = PCodeCmds[cmd]();
+ } while (action == SCRIPT_CONTINUE);
+
+- ACScript->ip = PCodePtr;
++ ACScript->ip = PCodeOffset;
+
+ if (action == SCRIPT_TERMINATE)
+ {
+@@ -896,25 +915,6 @@ static void Drop(void)
+ ACScript->stackPtr--;
+ }
+
+-//==========================================================================
+-//
+-// ReadCodeImmediate
+-//
+-// Some instructions take "immediate" parameters which are stored in the
+-// bytecode immediately following the instruction. This function should be
+-// used to read them.
+-//
+-//==========================================================================
+-
+-static int ReadCodeImmediate(void)
+-{
+- int result;
+- // TODO: Add bounds checking
+- result = LONG(*PCodePtr);
+- ++PCodePtr;
+- return result;
+-}
+-
+ //==========================================================================
+ //
+ // ReadScriptVar
+@@ -926,7 +926,7 @@ static int ReadCodeImmediate(void)
+
+ static int ReadScriptVar(void)
+ {
+- int var = ReadCodeImmediate();
++ int var = ReadCodeInt();
+ ACSAssert(var >= 0, "negative script variable: %d < 0", var);
+ ACSAssert(var < MAX_ACS_SCRIPT_VARS,
+ "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
+@@ -944,7 +944,7 @@ static int ReadScriptVar(void)
+
+ static int ReadMapVar(void)
+ {
+- int var = ReadCodeImmediate();
++ int var = ReadCodeInt();
+ ACSAssert(var >= 0, "negative map variable: %d < 0", var);
+ ACSAssert(var < MAX_ACS_MAP_VARS,
+ "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS);
+@@ -962,7 +962,7 @@ static int ReadMapVar(void)
+
+ static int ReadWorldVar(void)
+ {
+- int var = ReadCodeImmediate();
++ int var = ReadCodeInt();
+ ACSAssert(var >= 0, "negative world variable: %d < 0", var);
+ ACSAssert(var < MAX_ACS_WORLD_VARS,
+ "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS);
+@@ -1011,7 +1011,7 @@ static int CmdSuspend(void)
+
+ static int CmdPushNumber(void)
+ {
+- Push(ReadCodeImmediate());
++ Push(ReadCodeInt());
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1019,7 +1019,7 @@ static int CmdLSpec1(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
++ special = ReadCodeInt();
+ SpecArgs[0] = Pop();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+@@ -1030,7 +1030,7 @@ static int CmdLSpec2(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
++ special = ReadCodeInt();
+ SpecArgs[1] = Pop();
+ SpecArgs[0] = Pop();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+@@ -1042,7 +1042,7 @@ static int CmdLSpec3(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
++ special = ReadCodeInt();
+ SpecArgs[2] = Pop();
+ SpecArgs[1] = Pop();
+ SpecArgs[0] = Pop();
+@@ -1055,7 +1055,7 @@ static int CmdLSpec4(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
++ special = ReadCodeInt();
+ SpecArgs[3] = Pop();
+ SpecArgs[2] = Pop();
+ SpecArgs[1] = Pop();
+@@ -1069,7 +1069,7 @@ static int CmdLSpec5(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
++ special = ReadCodeInt();
+ SpecArgs[4] = Pop();
+ SpecArgs[3] = Pop();
+ SpecArgs[2] = Pop();
+@@ -1084,8 +1084,8 @@ static int CmdLSpec1Direct(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
+- SpecArgs[0] = ReadCodeImmediate();
++ special = ReadCodeInt();
++ SpecArgs[0] = ReadCodeInt();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1095,9 +1095,9 @@ static int CmdLSpec2Direct(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
+- SpecArgs[0] = ReadCodeImmediate();
+- SpecArgs[1] = ReadCodeImmediate();
++ special = ReadCodeInt();
++ SpecArgs[0] = ReadCodeInt();
++ SpecArgs[1] = ReadCodeInt();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1107,10 +1107,10 @@ static int CmdLSpec3Direct(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
+- SpecArgs[0] = ReadCodeImmediate();
+- SpecArgs[1] = ReadCodeImmediate();
+- SpecArgs[2] = ReadCodeImmediate();
++ special = ReadCodeInt();
++ SpecArgs[0] = ReadCodeInt();
++ SpecArgs[1] = ReadCodeInt();
++ SpecArgs[2] = ReadCodeInt();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1120,11 +1120,11 @@ static int CmdLSpec4Direct(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
+- SpecArgs[0] = ReadCodeImmediate();
+- SpecArgs[1] = ReadCodeImmediate();
+- SpecArgs[2] = ReadCodeImmediate();
+- SpecArgs[3] = ReadCodeImmediate();
++ special = ReadCodeInt();
++ SpecArgs[0] = ReadCodeInt();
++ SpecArgs[1] = ReadCodeInt();
++ SpecArgs[2] = ReadCodeInt();
++ SpecArgs[3] = ReadCodeInt();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1134,12 +1134,12 @@ static int CmdLSpec5Direct(void)
+ {
+ int special;
+
+- special = ReadCodeImmediate();
+- SpecArgs[0] = ReadCodeImmediate();
+- SpecArgs[1] = ReadCodeImmediate();
+- SpecArgs[2] = ReadCodeImmediate();
+- SpecArgs[3] = ReadCodeImmediate();
+- SpecArgs[4] = ReadCodeImmediate();
++ special = ReadCodeInt();
++ SpecArgs[0] = ReadCodeInt();
++ SpecArgs[1] = ReadCodeInt();
++ SpecArgs[2] = ReadCodeInt();
++ SpecArgs[3] = ReadCodeInt();
++ SpecArgs[4] = ReadCodeInt();
+ P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
+ ACScript->side, ACScript->activator);
+ return SCRIPT_CONTINUE;
+@@ -1396,10 +1396,7 @@ static int CmdDecWorldVar(void)
+
+ static int CmdGoto(void)
+ {
+- int offset;
+-
+- offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdGoto parameter");
+- PCodePtr = (int *) (ActionCodeBase + offset);
++ PCodeOffset = ValidateLumpOffset(ReadCodeInt(), "CmdGoto parameter");
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1407,11 +1404,11 @@ static int CmdIfGoto(void)
+ {
+ int offset;
+
+- offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdIfGoto parameter");
++ offset = ValidateLumpOffset(ReadCodeInt(), "CmdIfGoto parameter");
+
+ if (Pop() != 0)
+ {
+- PCodePtr = (int *) (ActionCodeBase + offset);
++ PCodeOffset = offset;
+ }
+ return SCRIPT_CONTINUE;
+ }
+@@ -1430,7 +1427,7 @@ static int CmdDelay(void)
+
+ static int CmdDelayDirect(void)
+ {
+- ACScript->delayCount = ReadCodeImmediate();
++ ACScript->delayCount = ReadCodeInt();
+ return SCRIPT_STOP;
+ }
+
+@@ -1450,8 +1447,8 @@ static int CmdRandomDirect(void)
+ int low;
+ int high;
+
+- low = ReadCodeImmediate();
+- high = ReadCodeImmediate();
++ low = ReadCodeInt();
++ high = ReadCodeInt();
+ Push(low + (P_Random() % (high - low + 1)));
+ return SCRIPT_CONTINUE;
+ }
+@@ -1469,8 +1466,8 @@ static int CmdThingCountDirect(void)
+ {
+ int type;
+
+- type = ReadCodeImmediate();
+- ThingCount(type, ReadCodeImmediate());
++ type = ReadCodeInt();
++ ThingCount(type, ReadCodeInt());
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1540,7 +1537,7 @@ static int CmdTagWait(void)
+
+ static int CmdTagWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG;
+ return SCRIPT_STOP;
+ }
+@@ -1554,7 +1551,7 @@ static int CmdPolyWait(void)
+
+ static int CmdPolyWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY;
+ return SCRIPT_STOP;
+ }
+@@ -1581,8 +1578,8 @@ static int CmdChangeFloorDirect(void)
+ int flat;
+ int sectorIndex;
+
+- tag = ReadCodeImmediate();
+- flat = R_FlatNumForName(StringLookup(ReadCodeImmediate()));
++ tag = ReadCodeInt();
++ flat = R_FlatNumForName(StringLookup(ReadCodeInt()));
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+ {
+@@ -1613,8 +1610,8 @@ static int CmdChangeCeilingDirect(void)
+ int flat;
+ int sectorIndex;
+
+- tag = ReadCodeImmediate();
+- flat = R_FlatNumForName(StringLookup(ReadCodeImmediate()));
++ tag = ReadCodeInt();
++ flat = R_FlatNumForName(StringLookup(ReadCodeInt()));
+ sectorIndex = -1;
+ while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
+ {
+@@ -1625,7 +1622,7 @@ static int CmdChangeCeilingDirect(void)
+
+ static int CmdRestart(void)
+ {
+- PCodePtr = ACSInfo[ACScript->infoIndex].address;
++ PCodeOffset = ACSInfo[ACScript->infoIndex].offset;
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1693,11 +1690,11 @@ static int CmdIfNotGoto(void)
+ {
+ int offset;
+
+- offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdIfNotGoto parameter");
++ offset = ValidateLumpOffset(ReadCodeInt(), "CmdIfNotGoto parameter");
+
+ if (Pop() == 0)
+ {
+- PCodePtr = (int *) (ActionCodeBase + offset);
++ PCodeOffset = offset;
+ }
+ return SCRIPT_CONTINUE;
+ }
+@@ -1717,7 +1714,7 @@ static int CmdScriptWait(void)
+
+ static int CmdScriptWaitDirect(void)
+ {
+- ACSInfo[ACScript->infoIndex].waitValue = ReadCodeImmediate();
++ ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
+ ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT;
+ return SCRIPT_STOP;
+ }
+@@ -1736,12 +1733,12 @@ static int CmdCaseGoto(void)
+ int value;
+ int offset;
+
+- value = ReadCodeImmediate();
+- offset = ValidateLumpOffset(ReadCodeImmediate(), "CmdCaseGoto parameter");
++ value = ReadCodeInt();
++ offset = ValidateLumpOffset(ReadCodeInt(), "CmdCaseGoto parameter");
+
+ if (Top() == value)
+ {
+- PCodePtr = (int *) (ActionCodeBase + offset);
++ PCodeOffset = offset;
+ Drop();
+ }
+
+diff --git a/src/hexen/p_spec.h b/src/hexen/p_spec.h
+index 750e9187..030ea202 100644
+--- a/src/hexen/p_spec.h
++++ b/src/hexen/p_spec.h
+@@ -507,7 +507,7 @@ typedef struct acsInfo_s acsInfo_t;
+ struct acsInfo_s
+ {
+ int number;
+- int *address;
++ int offset;
+ int argCount;
+ aste_t state;
+ int waitValue;
+@@ -525,7 +525,7 @@ struct acs_s
+ int stack[ACS_STACK_DEPTH];
+ int stackPtr;
+ int vars[MAX_ACS_SCRIPT_VARS];
+- int *ip;
++ int ip;
+ };
+
+ typedef struct
+diff --git a/src/hexen/sv_save.c b/src/hexen/sv_save.c
+index d3cea158..b139c158 100644
+--- a/src/hexen/sv_save.c
++++ b/src/hexen/sv_save.c
+@@ -1593,8 +1593,7 @@ static void StreamIn_acs_t(acs_t *str)
+ }
+
+ // int *ip;
+- i = SV_ReadLong();
+- str->ip = (int *) (ActionCodeBase + i);
++ str->ip = SV_ReadLong();
+ }
+
+ static void StreamOut_acs_t(acs_t *str)
+@@ -1645,7 +1644,7 @@ static void StreamOut_acs_t(acs_t *str)
+ }
+
+ // int *ip;
+- SV_WriteLong((byte *) str->ip - ActionCodeBase);
++ SV_WriteLong(str->ip);
+ }
+
+
+--
+2.15.1
+
diff --git a/debian/patches/0011-hexen-Use-ReadCodeInt-to-parse-header.patch b/debian/patches/0011-hexen-Use-ReadCodeInt-to-parse-header.patch
new file mode 100644
index 0000000..e5ab768
--- /dev/null
+++ b/debian/patches/0011-hexen-Use-ReadCodeInt-to-parse-header.patch
@@ -0,0 +1,78 @@
+From f4f2242c5534a64ab4c208479a6a722cf7f6a877 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:26:13 +0100
+Subject: [PATCH 11/16] hexen: Use ReadCodeInt() to parse header.
+
+This needs to be validated too, and it's simplest to just reuse the
+same mechanism used for executing code.
+---
+ src/hexen/p_acs.c | 28 +++++++++++-----------------
+ 1 file changed, 11 insertions(+), 17 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index f3631faa..e6bc5e99 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -374,17 +374,16 @@ static int ReadCodeInt(void)
+ void P_LoadACScripts(int lump)
+ {
+ int i;
+- int *buffer;
+ acsHeader_t *header;
+ acsInfo_t *info;
+
+- header = W_CacheLumpNum(lump, PU_LEVEL);
+- ActionCodeBase = (byte *) header;
++ ActionCodeBase = W_CacheLumpNum(lump, PU_LEVEL);
+ ActionCodeSize = W_LumpLength(lump);
+- buffer = (int *) ((byte *) header + LONG(header->infoOffset));
+
+- ACScriptCount = LONG(*buffer);
+- ++buffer;
++ header = (acsHeader_t *) ActionCodeBase;
++ PCodeOffset = LONG(header->infoOffset);
++
++ ACScriptCount = ReadCodeInt();
+
+ if (ACScriptCount == 0)
+ { // Empty behavior lump
+@@ -395,14 +394,9 @@ void P_LoadACScripts(int lump)
+ memset(ACSInfo, 0, ACScriptCount * sizeof(acsInfo_t));
+ for (i = 0, info = ACSInfo; i < ACScriptCount; i++, info++)
+ {
+- info->number = LONG(*buffer);
+- ++buffer;
+-
+- info->offset = ValidateLumpOffset(LONG(*buffer), "script header");
+- ++buffer;
+-
+- info->argCount = LONG(*buffer);
+- ++buffer;
++ info->number = ReadCodeInt();
++ info->offset = ValidateLumpOffset(ReadCodeInt(), "script header");
++ info->argCount = ReadCodeInt();
+
+ if (info->argCount > MAX_SCRIPT_ARGS)
+ {
+@@ -425,15 +419,15 @@ void P_LoadACScripts(int lump)
+ info->state = ASTE_INACTIVE;
+ }
+ }
+- ACStringCount = LONG(*buffer);
+- ++buffer;
+
++ ACStringCount = ReadCodeInt();
++ ACSAssert(ACStringCount >= 0, "negative string count %d", ACStringCount);
+ ACStrings = Z_Malloc(ACStringCount * sizeof(char *), PU_LEVEL, NULL);
+
+ for (i=0; i<ACStringCount; ++i)
+ {
+ ACStrings[i] = (char *) ActionCodeBase +
+- ValidateLumpOffset(LONG(buffer[i]), "string header");
++ ValidateLumpOffset(ReadCodeInt(), "string header");
+ }
+
+ memset(MapVars, 0, sizeof(MapVars));
+--
+2.15.1
+
diff --git a/debian/patches/0012-hexen-Validate-ACS-instructions.patch b/debian/patches/0012-hexen-Validate-ACS-instructions.patch
new file mode 100644
index 0000000..c32c54c
--- /dev/null
+++ b/debian/patches/0012-hexen-Validate-ACS-instructions.patch
@@ -0,0 +1,32 @@
+From 29994d4dbd93a67cf8e39d72230c963e14974b42 Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:28:53 +0100
+Subject: [PATCH 12/16] hexen: Validate ACS instructions.
+
+We do an array lookup based on the instruction we read, so it is
+important that the instruction is within the bounds of the array. In
+particular the instruction may an extension supported by an advanced
+source port like ZDoom, that we don't support.
+---
+ src/hexen/p_acs.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index e6bc5e99..163aaaa6 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -717,6 +717,11 @@ void T_InterpretACS(acs_t * script)
+ do
+ {
+ cmd = ReadCodeInt();
++ ACSAssert(cmd >= 0, "negative ACS instruction %d", cmd);
++ ACSAssert(cmd < arrlen(PCodeCmds),
++ "invalid ACS instruction %d (maybe this WAD is designed "
++ "for an advanced source port and is not vanilla "
++ "compatible)", cmd);
+ action = PCodeCmds[cmd]();
+ } while (action == SCRIPT_CONTINUE);
+
+--
+2.15.1
+
diff --git a/debian/patches/0013-hexen-Replace-ValidateLumpOffset-with-ReadOffset.patch b/debian/patches/0013-hexen-Replace-ValidateLumpOffset-with-ReadOffset.patch
new file mode 100644
index 0000000..732ad8b
--- /dev/null
+++ b/debian/patches/0013-hexen-Replace-ValidateLumpOffset-with-ReadOffset.patch
@@ -0,0 +1,282 @@
+From 55fb41791d467a8c6b1609770c9cc3348079b5cb Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:36:38 +0100
+Subject: [PATCH 13/16] hexen: Replace ValidateLumpOffset with ReadOffset().
+
+All offsets to be validated are now read as immediate values anyway.
+---
+ src/hexen/p_acs.c | 195 +++++++++++++++++++++++++++---------------------------
+ 1 file changed, 98 insertions(+), 97 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 163aaaa6..ce0d8db9 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -325,23 +325,6 @@ static void ACSAssert(int condition, char *fmt, ...)
+ I_Error("ACS assertation failure: %s", buf);
+ }
+
+-//==========================================================================
+-//
+-// ValidateLumpOffset
+-//
+-// Check that the given offset is a valid location inside the loaded ACS
+-// lump and does not point outside it.
+-//
+-//==========================================================================
+-
+-static int ValidateLumpOffset(int offset, char *where)
+-{
+- ACSAssert(offset >= 0, "negative lump offset in %s", where);
+- ACSAssert(offset < ActionCodeSize, "invalid lump offset in %s: %d >= %d",
+- where, offset, ActionCodeSize);
+- return offset;
+-}
+-
+ //==========================================================================
+ //
+ // ReadCodeInt
+@@ -356,7 +339,8 @@ static int ReadCodeInt(void)
+ int result;
+ int *ptr;
+
+- ValidateLumpOffset(PCodeOffset + 3, "ReadCodeInt");
++ ACSAssert(PCodeOffset + 3 < ActionCodeSize,
++ "unexpectedly reached end of ACS lump");
+
+ ptr = (int *) (ActionCodeBase + PCodeOffset);
+ result = LONG(*ptr);
+@@ -365,6 +349,96 @@ static int ReadCodeInt(void)
+ return result;
+ }
+
++//==========================================================================
++//
++// ReadScriptVar
++//
++// Read a script variable index as an immediate value, validating the
++// result is a valid script variable number.
++//
++//==========================================================================
++
++static int ReadScriptVar(void)
++{
++ int var = ReadCodeInt();
++ ACSAssert(var >= 0, "negative script variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_SCRIPT_VARS,
++ "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
++ return var;
++}
++
++//==========================================================================
++//
++// ReadMapVar
++//
++// Read a map variable index as an immediate value, validating the
++// result is a valid map variable number.
++//
++//==========================================================================
++
++static int ReadMapVar(void)
++{
++ int var = ReadCodeInt();
++ ACSAssert(var >= 0, "negative map variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_MAP_VARS,
++ "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS);
++ return var;
++}
++
++//==========================================================================
++//
++// ReadWorldVar
++//
++// Read a world variable index as an immediate value, validating the
++// result is a valid world variable number.
++//
++//==========================================================================
++
++static int ReadWorldVar(void)
++{
++ int var = ReadCodeInt();
++ ACSAssert(var >= 0, "negative world variable: %d < 0", var);
++ ACSAssert(var < MAX_ACS_WORLD_VARS,
++ "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS);
++ return var;
++}
++
++//==========================================================================
++//
++// StringLookup
++//
++// Look up the given string in the strings table by index, validating that
++// it is a valid string index.
++//
++//==========================================================================
++
++static char *StringLookup(int string_index)
++{
++ ACSAssert(string_index >= 0,
++ "negative string index: %d < 0", string_index);
++ ACSAssert(string_index < ACStringCount,
++ "invalid string index: %d >= %d", string_index, ACStringCount);
++ return ACStrings[string_index];
++}
++
++//==========================================================================
++//
++// ReadOffset
++//
++// Read a lump offset value, validating that it is an offset within the
++// range of the lump.
++//
++//==========================================================================
++
++static int ReadOffset(void)
++{
++ int offset = ReadCodeInt();
++ ACSAssert(offset >= 0, "negative lump offset %d", offset);
++ ACSAssert(offset < ActionCodeSize, "invalid lump offset: %d >= %d",
++ offset, ActionCodeSize);
++ return offset;
++}
++
+ //==========================================================================
+ //
+ // P_LoadACScripts
+@@ -395,7 +469,7 @@ void P_LoadACScripts(int lump)
+ for (i = 0, info = ACSInfo; i < ACScriptCount; i++, info++)
+ {
+ info->number = ReadCodeInt();
+- info->offset = ValidateLumpOffset(ReadCodeInt(), "script header");
++ info->offset = ReadOffset();
+ info->argCount = ReadCodeInt();
+
+ if (info->argCount > MAX_SCRIPT_ARGS)
+@@ -426,8 +500,7 @@ void P_LoadACScripts(int lump)
+
+ for (i=0; i<ACStringCount; ++i)
+ {
+- ACStrings[i] = (char *) ActionCodeBase +
+- ValidateLumpOffset(ReadCodeInt(), "string header");
++ ACStrings[i] = (char *) ActionCodeBase + ReadOffset();
+ }
+
+ memset(MapVars, 0, sizeof(MapVars));
+@@ -914,78 +987,6 @@ static void Drop(void)
+ ACScript->stackPtr--;
+ }
+
+-//==========================================================================
+-//
+-// ReadScriptVar
+-//
+-// Read a script variable index as an immediate value, validating the
+-// result is a valid script variable number.
+-//
+-//==========================================================================
+-
+-static int ReadScriptVar(void)
+-{
+- int var = ReadCodeInt();
+- ACSAssert(var >= 0, "negative script variable: %d < 0", var);
+- ACSAssert(var < MAX_ACS_SCRIPT_VARS,
+- "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
+- return var;
+-}
+-
+-//==========================================================================
+-//
+-// ReadMapVar
+-//
+-// Read a map variable index as an immediate value, validating the
+-// result is a valid map variable number.
+-//
+-//==========================================================================
+-
+-static int ReadMapVar(void)
+-{
+- int var = ReadCodeInt();
+- ACSAssert(var >= 0, "negative map variable: %d < 0", var);
+- ACSAssert(var < MAX_ACS_MAP_VARS,
+- "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS);
+- return var;
+-}
+-
+-//==========================================================================
+-//
+-// ReadWorldVar
+-//
+-// Read a world variable index as an immediate value, validating the
+-// result is a valid world variable number.
+-//
+-//==========================================================================
+-
+-static int ReadWorldVar(void)
+-{
+- int var = ReadCodeInt();
+- ACSAssert(var >= 0, "negative world variable: %d < 0", var);
+- ACSAssert(var < MAX_ACS_WORLD_VARS,
+- "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS);
+- return var;
+-}
+-
+-//==========================================================================
+-//
+-// StringLookup
+-//
+-// Look up the given string in the strings table by index, validating that
+-// it is a valid string index.
+-//
+-//==========================================================================
+-
+-static char *StringLookup(int string_index)
+-{
+- ACSAssert(string_index >= 0,
+- "negative string index: %d < 0", string_index);
+- ACSAssert(string_index < ACStringCount,
+- "invalid string index: %d >= %d", string_index, ACStringCount);
+- return ACStrings[string_index];
+-}
+-
+ //==========================================================================
+ //
+ // P-Code Commands
+@@ -1395,7 +1396,7 @@ static int CmdDecWorldVar(void)
+
+ static int CmdGoto(void)
+ {
+- PCodeOffset = ValidateLumpOffset(ReadCodeInt(), "CmdGoto parameter");
++ PCodeOffset = ReadOffset();
+ return SCRIPT_CONTINUE;
+ }
+
+@@ -1403,7 +1404,7 @@ static int CmdIfGoto(void)
+ {
+ int offset;
+
+- offset = ValidateLumpOffset(ReadCodeInt(), "CmdIfGoto parameter");
++ offset = ReadOffset();
+
+ if (Pop() != 0)
+ {
+@@ -1689,7 +1690,7 @@ static int CmdIfNotGoto(void)
+ {
+ int offset;
+
+- offset = ValidateLumpOffset(ReadCodeInt(), "CmdIfNotGoto parameter");
++ offset = ReadOffset();
+
+ if (Pop() == 0)
+ {
+@@ -1733,7 +1734,7 @@ static int CmdCaseGoto(void)
+ int offset;
+
+ value = ReadCodeInt();
+- offset = ValidateLumpOffset(ReadCodeInt(), "CmdCaseGoto parameter");
++ offset = ReadOffset();
+
+ if (Top() == value)
+ {
+--
+2.15.1
+
diff --git a/debian/patches/0014-hexen-Validate-strings-during-header-parsing.patch b/debian/patches/0014-hexen-Validate-strings-during-header-parsing.patch
new file mode 100644
index 0000000..9d9cfd1
--- /dev/null
+++ b/debian/patches/0014-hexen-Validate-strings-during-header-parsing.patch
@@ -0,0 +1,39 @@
+From ffa290d23c4a9b90341306866eb53f5a1b3f19aa Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:44:51 +0100
+Subject: [PATCH 14/16] hexen: Validate strings during header parsing.
+
+Strings must be terminated with a NUL before the end of the lump is
+reached; if not they are invalid. Check that this really is the case.
+---
+ src/hexen/p_acs.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index ce0d8db9..50e09ed8 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -447,7 +447,7 @@ static int ReadOffset(void)
+
+ void P_LoadACScripts(int lump)
+ {
+- int i;
++ int i, offset;
+ acsHeader_t *header;
+ acsInfo_t *info;
+
+@@ -500,7 +500,10 @@ void P_LoadACScripts(int lump)
+
+ for (i=0; i<ACStringCount; ++i)
+ {
+- ACStrings[i] = (char *) ActionCodeBase + ReadOffset();
++ offset = ReadOffset();
++ ACStrings[i] = (char *) ActionCodeBase + offset;
++ ACSAssert(memchr(ACStrings[i], '\0', ActionCodeSize - offset) != NULL,
++ "string %d missing terminating NUL", i);
+ }
+
+ memset(MapVars, 0, sizeof(MapVars));
+--
+2.15.1
+
diff --git a/debian/patches/0015-hexen-Change-comment-to-American-spelling.patch b/debian/patches/0015-hexen-Change-comment-to-American-spelling.patch
new file mode 100644
index 0000000..cba804e
--- /dev/null
+++ b/debian/patches/0015-hexen-Change-comment-to-American-spelling.patch
@@ -0,0 +1,34 @@
+From f5446def0c1df4cc00899f65331b643a75391cda Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 20:52:04 +0100
+Subject: [PATCH 15/16] hexen: Change comment to American spelling.
+
+---
+ src/hexen/p_acs.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index 50e09ed8..e0bde2f3 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -928,7 +928,7 @@ static int GetACSIndex(int number)
+ // CheckACSPresent
+ //
+ // Placing Korax in a PWAD without extra steps will result in a crash in
+-// Vanilla because the relevant ACS scripts are not initialised
++// Vanilla because the relevant ACS scripts are not initialized
+ //
+ //==========================================================================
+
+@@ -936,7 +936,7 @@ void CheckACSPresent(int number)
+ {
+ if (GetACSIndex(number) == -1)
+ {
+- I_Error("Required ACS script %d not initialised", number);
++ I_Error("Required ACS script %d not initialized", number);
+ }
+ }
+
+--
+2.15.1
+
diff --git a/debian/patches/0016-hexen-Add-extra-context-for-assertion-failures.patch b/debian/patches/0016-hexen-Add-extra-context-for-assertion-failures.patch
new file mode 100644
index 0000000..fa50845
--- /dev/null
+++ b/debian/patches/0016-hexen-Add-extra-context-for-assertion-failures.patch
@@ -0,0 +1,66 @@
+From 666e8da5fdc204a0406139f660a6a674210f156c Mon Sep 17 00:00:00 2001
+From: Simon Howard <fraggle at soulsphere.org>
+Date: Fri, 5 Jan 2018 21:07:03 +0100
+Subject: [PATCH 16/16] hexen: Add extra context for assertion failures.
+
+If bugs are encountered, it may be helpful to get some extra detail
+about the context in which the assertation failed, including the
+offset inside the BEHAVIOR lump.
+---
+ src/hexen/p_acs.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/hexen/p_acs.c b/src/hexen/p_acs.c
+index e0bde2f3..3d370326 100644
+--- a/src/hexen/p_acs.c
++++ b/src/hexen/p_acs.c
+@@ -184,6 +184,7 @@ acsstore_t ACSStore[MAX_ACS_STORE + 1]; // +1 for termination marker
+
+ // PRIVATE DATA DEFINITIONS ------------------------------------------------
+
++static char EvalContext[64];
+ static acs_t *ACScript;
+ static unsigned int PCodeOffset;
+ static byte SpecArgs[8];
+@@ -322,7 +323,7 @@ static void ACSAssert(int condition, char *fmt, ...)
+ va_start(args, fmt);
+ M_vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+- I_Error("ACS assertation failure: %s", buf);
++ I_Error("ACS assertation failure: in %s: %s", EvalContext, buf);
+ }
+
+ //==========================================================================
+@@ -361,6 +362,7 @@ static int ReadCodeInt(void)
+ static int ReadScriptVar(void)
+ {
+ int var = ReadCodeInt();
++ ACSAssert(0, "testing error messages");
+ ACSAssert(var >= 0, "negative script variable: %d < 0", var);
+ ACSAssert(var < MAX_ACS_SCRIPT_VARS,
+ "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
+@@ -454,6 +456,9 @@ void P_LoadACScripts(int lump)
+ ActionCodeBase = W_CacheLumpNum(lump, PU_LEVEL);
+ ActionCodeSize = W_LumpLength(lump);
+
++ M_snprintf(EvalContext, sizeof(EvalContext),
++ "header parsing of lump #%d", lump);
++
+ header = (acsHeader_t *) ActionCodeBase;
+ PCodeOffset = LONG(header->infoOffset);
+
+@@ -792,7 +797,11 @@ void T_InterpretACS(acs_t * script)
+
+ do
+ {
++ M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x",
++ ACSInfo[script->infoIndex].number, PCodeOffset);
+ cmd = ReadCodeInt();
++ M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x, cmd=%d",
++ ACSInfo[script->infoIndex].number, PCodeOffset, cmd);
+ ACSAssert(cmd >= 0, "negative ACS instruction %d", cmd);
+ ACSAssert(cmd < arrlen(PCodeCmds),
+ "invalid ACS instruction %d (maybe this WAD is designed "
+--
+2.15.1
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..42ddec3
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,16 @@
+0001-hexen-Add-assertion-checking-for-ACS-stack.patch
+0002-hexen-Add-bounds-checking-for-script-variables.patch
+0003-hexen-Add-bounds-checking-for-map-variables.patch
+0004-hexen-Add-bounds-checking-for-world-variables.patch
+0005-hexen-Add-bounds-checking-for-strings-table.patch
+0006-hexen-Add-doc-comments-for-new-functions.patch
+0007-hexen-Add-bounds-checking-for-CmdPrintCharacter.patch
+0008-hexen-Eliminate-most-direct-usage-of-PCodePtr.patch
+0009-hexen-Add-validation-of-lump-offset-values.patch
+0010-hexen-Remove-PCodePtr-entirely.patch
+0011-hexen-Use-ReadCodeInt-to-parse-header.patch
+0012-hexen-Validate-ACS-instructions.patch
+0013-hexen-Replace-ValidateLumpOffset-with-ReadOffset.patch
+0014-hexen-Validate-strings-during-header-parsing.patch
+0015-hexen-Change-comment-to-American-spelling.patch
+0016-hexen-Add-extra-context-for-assertion-failures.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/chocolate-doom.git
More information about the Pkg-games-commits
mailing list