Skip to content

Commit

Permalink
Fix out of bounds access in dtMergeCorridorStartMoved and add tests (r…
Browse files Browse the repository at this point in the history
…ecastnavigation#635)

size can become negative if req > maxPath. This may happen when visited buffer is larger than path buffer.

Add tests to cover different use cases of the function including Should add visited points not present in path up to the path capacity to cover the fix.

List tests files explicitly. When new file is added CMake does not add it to the already generated list if GLOB is used.
  • Loading branch information
elsid authored Jan 28, 2024
1 parent b2d8fa9 commit 599fd0f
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 11 deletions.
10 changes: 5 additions & 5 deletions DetourCrowd/Source/DetourPathCorridor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPat
int size = dtMax(0, npath-orig);
if (req+size > maxPath)
size = maxPath-req;
if (size)
if (size > 0)
memmove(path+req, path+orig, size*sizeof(dtPolyRef));

// Store visited
for (int i = 0; i < req; ++i)
path[i] = visited[(nvisited-1)-i];
for (int i = 0, n = dtMin(req, maxPath); i < n; ++i)
path[i] = visited[(nvisited-1)-i];

return req+size;
}

Expand Down
3 changes: 2 additions & 1 deletion RecastDemo/premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,15 @@ project "Tests"
"../Tests/Recast/*.cpp",
"../Tests/Detour/*.h",
"../Tests/Detour/*.cpp",
"../Tests/DetourCrowd/*.cpp",
"../Tests/Contrib/catch2/*.cpp"
}

-- project dependencies
links {
"DebugUtils",
"Detour",
"DetourCrowd",
"Detour",
"DetourTileCache",
"Recast",
}
Expand Down
15 changes: 10 additions & 5 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
file(GLOB TESTS_SOURCES Detour/*.cpp Recast/*.cpp)

include_directories(../Detour/Include)
include_directories(../Recast/Include)

add_executable(Tests ${TESTS_SOURCES})
add_executable(Tests
Detour/Tests_Detour.cpp
Recast/Bench_rcVector.cpp
Recast/Tests_Alloc.cpp
Recast/Tests_Recast.cpp
Recast/Tests_RecastFilter.cpp
DetourCrowd/Tests_DetourPathCorridor.cpp
)

set_property(TARGET Tests PROPERTY CXX_STANDARD 17)

add_dependencies(Tests Recast Detour)
target_link_libraries(Tests Recast Detour)
add_dependencies(Tests Recast Detour DetourCrowd)
target_link_libraries(Tests Recast Detour DetourCrowd)

find_package(Catch2 QUIET)
if (Catch2_FOUND)
Expand Down
119 changes: 119 additions & 0 deletions Tests/DetourCrowd/Tests_DetourPathCorridor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include "catch2/catch_all.hpp"

#include "DetourPathCorridor.h"

TEST_CASE("dtMergeCorridorStartMoved")
{
SECTION("Should handle empty input")
{
dtPolyRef* const path = nullptr;
const int npath = 0;
const int maxPath = 0;
const dtPolyRef* const visited = nullptr;
const int nvisited = 0;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 0);
}

SECTION("Should handle empty visited")
{
dtPolyRef path[] = {1};
const int npath = 1;
const int maxPath = 1;
const dtPolyRef* const visited = nullptr;
const int nvisited = 0;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 1);
const dtPolyRef expectedPath[] = {1};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should handle empty path")
{
dtPolyRef* const path = nullptr;
const int npath = 0;
const int maxPath = 0;
const dtPolyRef visited[] = {1};
const int nvisited = 1;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 0);
}

SECTION("Should strip visited points from path except last")
{
dtPolyRef path[] = {1, 2};
const int npath = 2;
const int maxPath = 2;
const dtPolyRef visited[] = {1, 2};
const int nvisited = 2;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 1);
const dtPolyRef expectedPath[] = {2, 2};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should add visited points not present in path in reverse order")
{
dtPolyRef path[] = {1, 2, 0};
const int npath = 2;
const int maxPath = 3;
const dtPolyRef visited[] = {1, 2, 3, 4};
const int nvisited = 4;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 3);
const dtPolyRef expectedPath[] = {4, 3, 2};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should add visited points not present in path up to the path capacity")
{
dtPolyRef path[] = {1, 2, 0};
const int npath = 2;
const int maxPath = 3;
const dtPolyRef visited[] = {1, 2, 3, 4, 5};
const int nvisited = 5;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 3);
const dtPolyRef expectedPath[] = {5, 4, 3};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should not change path if there is no intersection with visited")
{
dtPolyRef path[] = {1, 2};
const int npath = 2;
const int maxPath = 2;
const dtPolyRef visited[] = {3, 4};
const int nvisited = 2;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 2);
const dtPolyRef expectedPath[] = {1, 2};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should save unvisited path points")
{
dtPolyRef path[] = {1, 2, 0};
const int npath = 2;
const int maxPath = 3;
const dtPolyRef visited[] = {1, 3};
const int nvisited = 2;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 3);
const dtPolyRef expectedPath[] = {3, 1, 2};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}

SECTION("Should save unvisited path points up to the path capacity")
{
dtPolyRef path[] = {1, 2};
const int npath = 2;
const int maxPath = 2;
const dtPolyRef visited[] = {1, 3};
const int nvisited = 2;
const int result = dtMergeCorridorStartMoved(path, npath, maxPath, visited, nvisited);
CHECK(result == 2);
const dtPolyRef expectedPath[] = {3, 1};
CHECK_THAT(path, Catch::Matchers::RangeEquals(expectedPath));
}
}

0 comments on commit 599fd0f

Please sign in to comment.