From 1122338b0ec6f6b81bd58b465710888fe814bc52 Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:03:46 +0300 Subject: [PATCH 1/4] Check path bounds before collision avoidance reads Evaluate the path index bound before reading blocker Path, preventing a one-past-end access when no sentinel is present. --- code/astar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/astar.cpp b/code/astar.cpp index fb40cd03..e4c5173f 100644 --- a/code/astar.cpp +++ b/code/astar.cpp @@ -900,7 +900,7 @@ void AStarClass::Apply_Path_Collision_Avoidance(FootClass * foot) } marked_path = true; - while (blocker->Path[index] != FACING_NONE && index < ARRAY_SIZE(blocker->Path)) { + while (index < ARRAY_SIZE(blocker->Path) && blocker->Path[index] != FACING_NONE) { blocker_cell = Next_Cell(blocker_cell, blocker->Path[index]); Map[blocker_cell].IsPredictedPath = !Map[blocker_cell].IsPredictedPath; index++; From 661e204113df95f0114306ce3ab9cc060d5118ed Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:03:46 +0300 Subject: [PATCH 2/4] Advance movement paths with overlap-safe copies Replace overlapping path shifts with centralized memmove-based advancement. Initialize and terminate path storage so completed steps cannot expose stale facings. --- code/drive.cpp | 13 ++++--------- code/foot.cpp | 19 ++++++++++++++++++- code/foot.h | 1 + code/hover.cpp | 6 ++---- code/mech.cpp | 7 ++----- code/walk.cpp | 6 ++---- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/code/drive.cpp b/code/drive.cpp index f70c01eb..538c71bc 100644 --- a/code/drive.cpp +++ b/code/drive.cpp @@ -1012,8 +1012,7 @@ bool DriveLocomotionClass::While_Moving(bool just_started) TubeClass * tube = Tubes[tubenum]; Cell c = tube->Exit; HeadToCoord = c.As_Coord(); - memcpy((char*)&LinkedTo->Path, (char*)&LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX-1)); - LinkedTo->Path[CONQUER_PATH_MAX-1] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->CurrentTube = tubenum; LinkedTo->CurrentTubeDir = FACING_FIRST; LinkedTo->LastTubeCoord = Map[Adjacent_Cell((Cell)tube->Enter, tube->Dirs[0])].Cell_Coord(); @@ -1196,8 +1195,7 @@ bool DriveLocomotionClass::While_Moving(bool just_started) if (LinkedTo == NULL || !LinkedTo->IsActive || LinkedTo->IsInLimbo || LinkedTo->IsFalling) return(false); if (Start_Driver(c)) { LinkedTo->Set_Speed(oldspeed); - memcpy((char*)&LinkedTo->Path, (char*)&LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX-1)); - LinkedTo->Path[CONQUER_PATH_MAX-1] = FACING_NONE; + LinkedTo->Advance_Path(1); } break; @@ -1992,15 +1990,12 @@ bool DriveLocomotionClass::Start_Of_Move(bool & stop_processing, bool retry, boo } } else { - memcpy((char*)&LinkedTo->Path[0], (char*)&LinkedTo->Path[2], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX-2)); - LinkedTo->Path[CONQUER_PATH_MAX-2] = FACING_NONE; + LinkedTo->Advance_Path(2); LinkedTo->IsPlanningToLook = true; } } else { - memcpy((char*)&LinkedTo->Path[0], (char*)&LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX-1)); + LinkedTo->Advance_Path(1); } - - LinkedTo->Path[CONQUER_PATH_MAX-1] = FACING_NONE; LinkedTo->LastPathingCell = dest.As_Cell(); LinkedTo->IsNewNavCom = false; TrackIndex = 0; diff --git a/code/foot.cpp b/code/foot.cpp index 3cc51c8d..a0294339 100644 --- a/code/foot.cpp +++ b/code/foot.cpp @@ -134,6 +134,8 @@ #include "tube.hh" #include +#include +#include DynamicVectorClass Feet; @@ -195,7 +197,7 @@ FootClass::FootClass(HouseClass * house) : CurrentTubeDir(0), NextWaypoint(0) { - Path[0] = FACING_NONE; + std::fill(std::begin(Path), std::end(Path), FACING_NONE); Feet.Add(this); TeamPtrTracker.Add(this); } @@ -581,6 +583,21 @@ bool FootClass::Basic_Path(Cell cell, int path_offset, int avoidance) return(false); } +/// +/// Removes completed steps from the front of the active movement path. +/// +void FootClass::Advance_Path(int count) +{ + if (count <= 0) { + return; + } + + int const advance = std::min(count, ARRAY_SIZE(Path)); + std::memmove(Path, Path + advance, (ARRAY_SIZE(Path) - advance) * sizeof(Path[0])); + std::fill(std::end(Path) - advance, std::end(Path), FACING_NONE); +} + + /*********************************************************************************************** * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * diff --git a/code/foot.h b/code/foot.h index a6990722..93b3ccff 100644 --- a/code/foot.h +++ b/code/foot.h @@ -385,6 +385,7 @@ class FootClass : public TechnoClass ** Member function prototypes. */ bool Basic_Path(Cell cell, int path_offset = 0, int avoidance = 0); + void Advance_Path(int count); virtual void Compute_CRC(CRCEngine &) const override; virtual Coord Destination_Coord(void) const override; diff --git a/code/hover.cpp b/code/hover.cpp index ff3c001a..b108e633 100644 --- a/code/hover.cpp +++ b/code/hover.cpp @@ -412,9 +412,8 @@ MoveType HoverLocomotionClass::While_Moving(bool first_pass) TubeClass * tube = Tubes[tubenum]; Cell exit = tube->Exit; HeadToCoord = exit.As_Coord(); - memcpy((char*)&LinkedTo->Path, (char*)&LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX-1)); + LinkedTo->Advance_Path(1); LinkedTo->LastPathingCell = HeadToCoord.As_Cell(); - LinkedTo->Path[CONQUER_PATH_MAX-1] = FACING_NONE; LinkedTo->CurrentTube = tubenum; LinkedTo->CurrentTubeDir = FACING_FIRST; LinkedTo->LastTubeCoord = Map[Adjacent_Cell((Cell)tube->Enter, tube->Dirs[0])].Cell_Coord() - Coord((Cell)tube->Enter) + LinkedTo->PositionCoord; @@ -515,8 +514,7 @@ MoveType HoverLocomotionClass::While_Moving(bool first_pass) if (LinkedTo->IsLocked) { LinkedTo->Set_Occupy_Bit(HeadToCoord); } - memcpy((char *)&LinkedTo->Path, (char *)&LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (CONQUER_PATH_MAX - 1)); - LinkedTo->Path[CONQUER_PATH_MAX - 1] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->LastPathingCell = HeadToCoord.As_Cell(); return(ok); diff --git a/code/mech.cpp b/code/mech.cpp index 8ec0a553..ee1d17cd 100644 --- a/code/mech.cpp +++ b/code/mech.cpp @@ -178,7 +178,6 @@ void MechLocomotionClass::Movement_AI(bool continue_moving) if (HeadToCoord == COORD_NONE) { if (DestinationCoord != COORD_NONE) { - /* * A destination exists. If there is no current path then try to build * one. If a path already exists, fall through to dispatch the next leg. @@ -261,8 +260,7 @@ void MechLocomotionClass::Movement_AI(bool continue_moving) Cell cell = tubeptr->Exit; HeadToCoord = Coord(cell, 0); - memcpy(&LinkedTo->Path[0], &LinkedTo->Path[1], 92); - LinkedTo->Path[23] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->CurrentTube = tube; LinkedTo->CurrentTubeDir = FACING_FIRST; @@ -491,8 +489,7 @@ void MechLocomotionClass::Movement_AI(bool continue_moving) LinkedTo->Path[1] = FACING_NONE; } - memcpy(&LinkedTo->Path[0], &LinkedTo->Path[1], 92); - LinkedTo->Path[23] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->Set_Coord(HeadToCoord); LinkedTo->LastPathingCell = HeadToCoord.As_Cell(); diff --git a/code/walk.cpp b/code/walk.cpp index 3eb8974e..d3996e10 100644 --- a/code/walk.cpp +++ b/code/walk.cpp @@ -287,8 +287,7 @@ void WalkLocomotionClass::Movement_AI(bool first_pass) Cell cell = tube_ptr->Exit; HeadToCoord = Coord(cell, 0); - memcpy(&LinkedTo->Path[0], &LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (ARRAY_SIZE(LinkedTo->Path) - 1)); - LinkedTo->Path[ARRAY_SIZE(LinkedTo->Path) - 1] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->CurrentTube = tube; LinkedTo->CurrentTubeDir = FACING_FIRST; @@ -459,8 +458,7 @@ void WalkLocomotionClass::Movement_AI(bool first_pass) LinkedTo->Path[1] = FACING_NONE; } - memcpy(&LinkedTo->Path[0], &LinkedTo->Path[1], sizeof(LinkedTo->Path[0]) * (ARRAY_SIZE(LinkedTo->Path) - 1)); - LinkedTo->Path[ARRAY_SIZE(LinkedTo->Path) - 1] = FACING_NONE; + LinkedTo->Advance_Path(1); LinkedTo->Set_Coord(HeadToCoord); LinkedTo->LastPathingCell = HeadToCoord.As_Cell(); From 6a8c67dd8273e002f217c1ba58f408d1783c3d54 Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:03:46 +0300 Subject: [PATCH 3/4] Canonicalize generated movement paths Validate path offsets and lengths before copying generated steps. Clear the regenerated suffix so paths remain terminated after success or failure. --- code/foot.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/code/foot.cpp b/code/foot.cpp index a0294339..788064eb 100644 --- a/code/foot.cpp +++ b/code/foot.cpp @@ -429,12 +429,15 @@ bool FootClass::Basic_Path(Cell cell, int path_offset, int avoidance) PathStruct * path; // Pointer to path control structure. int skip_path = false; - if (path_offset == 0) { - Path[0] = FACING_NONE; + if (path_offset < 0 || path_offset >= ARRAY_SIZE(Path)) { + std::fill(std::begin(Path), std::end(Path), FACING_NONE); + return(false); } + // Keep an existing path prefix, but always terminate the new suffix. + std::fill(std::begin(Path) + path_offset, std::end(Path), FACING_NONE); + if (!Is_In_Same_Zone(cell)) { - Path[0] = FACING_NONE; return(false); } @@ -485,9 +488,6 @@ bool FootClass::Basic_Path(Cell cell, int path_offset, int avoidance) if (!skip_path) { Mark(MARK_UP); - if (path_offset == 0) { - Path[0] = FACING_NONE; // Probably not necessary, but... - } /* ** Try to find a path to the destination. If a failure occurs, then keep trying @@ -512,7 +512,8 @@ bool FootClass::Basic_Path(Cell cell, int path_offset, int avoidance) */ if (found) { Fixup_Path(&path1); - memcpy(&Path[path_offset], &workpath[0], std::min(path->Length, ARRAY_SIZE(Path) - path_offset) * sizeof(Path[0])); + int length = std::clamp(path->Length, 0, ARRAY_SIZE(Path) - path_offset); + memcpy(&Path[path_offset], &workpath[0], length * sizeof(Path[0])); } Mark(MARK_DOWN); From b6a391e12817fa829263d6303df65e863d7da2eb Mon Sep 17 00:00:00 2001 From: Belonit <54427022+Belonit@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:03:46 +0300 Subject: [PATCH 4/4] Handle tunnel markers in movement cost traversal Stop stationary-occupier traversal at FACING_COUNT, which represents a tunnel marker rather than a direction suitable for Adjacent_Cell. --- code/astar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/astar.cpp b/code/astar.cpp index e4c5173f..732e5eec 100644 --- a/code/astar.cpp +++ b/code/astar.cpp @@ -181,6 +181,10 @@ double AStarClass::Get_Movement_Cost(CellClass **from, CellClass **to, bool brid clear = true; break; } + + if (facing == FACING_COUNT) { + break; + } } else { facing = occupier->PrimaryFacing.Current().As_Dir8(); }