[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