Skip to content

Commit

Permalink
Add consistent input validation to dtNavMeshQuery
Browse files Browse the repository at this point in the history
Validate input values, including that points are finite. This would have
saved everyone some time in recastnavigation#343/recastnavigation#373.
  • Loading branch information
jakobbotsch committed Feb 10, 2019
1 parent 14b2631 commit ad71c30
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 46 deletions.
22 changes: 22 additions & 0 deletions Detour/Include/DetourCommon.h
Expand Up @@ -283,6 +283,28 @@ inline bool dtVequal(const float* p0, const float* p1)
return d < thr;
}

/// Checks that the specified vector's components are all finite.
/// @param[in] v A point. [(x, y, z)]
/// @return True if all of the point's components are finite, i.e. not NaN
/// or any of the infinities.
inline bool dtVisfinite(const float* v)
{
bool result =
dtMathIsfinite(v[0]) &&
dtMathIsfinite(v[1]) &&
dtMathIsfinite(v[2]);

return result;
}

/// Checks that the specified vector's 2D components are finite.
/// @param[in] v A point. [(x, y, z)]
inline bool dtVisfinite2D(const float* v)
{
bool result = dtMathIsfinite(v[0]) && dtMathIsfinite(v[2]);
return result;
}

/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
/// @param[in] u A vector [(x, y, z)]
/// @param[in] v A vector [(x, y, z)]
Expand Down
1 change: 1 addition & 0 deletions Detour/Include/DetourMath.h
Expand Up @@ -16,5 +16,6 @@ inline float dtMathCeilf(float x) { return ceilf(x); }
inline float dtMathCosf(float x) { return cosf(x); }
inline float dtMathSinf(float x) { return sinf(x); }
inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
inline bool dtMathIsfinite(float x) { return isfinite(x); }

#endif
156 changes: 110 additions & 46 deletions Detour/Source/DetourNavMeshQuery.cpp
Expand Up @@ -222,7 +222,10 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr
dtPolyRef* randomRef, float* randomPt) const
{
dtAssert(m_nav);


if (!filter || !frand || !randomRef || !randomPt)
return DT_FAILURE | DT_INVALID_PARAM;

// Randomly pick one tile. Assume that all tiles cover roughly the same area.
const dtMeshTile* tile = 0;
float tsum = 0.0f;
Expand Down Expand Up @@ -319,8 +322,13 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
dtAssert(m_openList);

// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
!filter || !frand || !randomRef || !randomPt)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

const dtMeshTile* startTile = 0;
const dtPoly* startPoly = 0;
Expand Down Expand Up @@ -512,6 +520,10 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo
return DT_FAILURE | DT_INVALID_PARAM;
if (!tile)
return DT_FAILURE | DT_INVALID_PARAM;

if (!pos || !dtVisfinite(pos) ||
!closest)
return DT_FAILURE | DT_INVALID_PARAM;

// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
Expand Down Expand Up @@ -607,6 +619,9 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;

if (!pos || !dtVisfinite(pos) || !closest)
return DT_FAILURE | DT_INVALID_PARAM;

// Collect vertices.
float verts[DT_VERTS_PER_POLYGON*3];
Expand Down Expand Up @@ -659,6 +674,9 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;

if (!pos || !dtVisfinite2D(pos))
return DT_FAILURE | DT_INVALID_PARAM;

if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
Expand Down Expand Up @@ -767,6 +785,8 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfE

if (!nearestRef)
return DT_FAILURE | DT_INVALID_PARAM;

// queryPolygons below will check rest of params

dtFindNearestPolyQuery query(this, center);

Expand Down Expand Up @@ -972,8 +992,12 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExt
{
dtAssert(m_nav);

if (!center || !halfExtents || !filter || !query)
if (!center || !dtVisfinite(center) ||
!halfExtents || !dtVisfinite(halfExtents) ||
!filter || !query)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

float bmin[3], bmax[3];
dtVsub(bmin, center, halfExtents);
Expand Down Expand Up @@ -1022,20 +1046,23 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
dtAssert(m_nodePool);
dtAssert(m_openList);

if (pathCount)
*pathCount = 0;

// Validate input
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
!startPos || !endPos || !filter || maxPath <= 0 || !path || !pathCount)
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter || !path || !pathCount || maxPath <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

if (startRef == endRef)
{
path[0] = startRef;
*pathCount = 1;
return DT_SUCCESS;
}

*pathCount = 0;

m_nodePool->clear();
m_openList->clear();
Expand Down Expand Up @@ -1257,6 +1284,14 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
dtAssert(m_nav);
dtAssert(m_nodePool);
dtAssert(m_openList);

// Validate input
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef)
|| !startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) || !filter)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

// Init path state.
memset(&m_query, 0, sizeof(dtQueryData));
Expand All @@ -1268,13 +1303,6 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
m_query.filter = filter;
m_query.options = options;
m_query.raycastLimitSqr = FLT_MAX;

if (!startRef || !endRef)
return DT_FAILURE | DT_INVALID_PARAM;

// Validate input
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
return DT_FAILURE | DT_INVALID_PARAM;

// trade quality with performance?
if (options & DT_FINDPATH_ANY_ANGLE)
Expand Down Expand Up @@ -1530,6 +1558,9 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)

dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
{
if (!path || !pathCount || maxPath <= 0)
return DT_FAILURE | DT_INVALID_PARAM;

*pathCount = 0;

if (dtStatusFailed(m_query.status))
Expand Down Expand Up @@ -1615,13 +1646,11 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
dtPolyRef* path, int* pathCount, const int maxPath)
{
if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0)
return DT_FAILURE | DT_INVALID_PARAM;

*pathCount = 0;

if (existingSize == 0)
{
return DT_FAILURE;
}

if (dtStatusFailed(m_query.status))
{
// Reset query.
Expand Down Expand Up @@ -1823,14 +1852,16 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
int* straightPathCount, const int maxStraightPath, const int options) const
{
dtAssert(m_nav);

*straightPathCount = 0;

if (!maxStraightPath)
return DT_FAILURE | DT_INVALID_PARAM;

if (!path[0])

if (!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!path || pathSize <= 0 || !path[0] ||
!straightPathCount || maxStraightPath <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

*straightPathCount = 0;

dtStatus stat = 0;

Expand Down Expand Up @@ -2070,13 +2101,17 @@ dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* start
dtAssert(m_nav);
dtAssert(m_tinyNodePool);

*visitedCount = 0;

// Validate input
if (!startRef)
return DT_FAILURE | DT_INVALID_PARAM;
if (!m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter || !resultPos || !visited || !visitedCount ||
maxVisitedSize <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

*visitedCount = 0;

dtStatus status = DT_SUCCESS;

Expand Down Expand Up @@ -2484,16 +2519,20 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
dtRaycastHit* hit, dtPolyRef prevRef) const
{
dtAssert(m_nav);

// Validate input
if (!m_nav->isValidPolyRef(startRef) ||
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter ||
(prevRef && !m_nav->isValidPolyRef(prevRef)))
{
return DT_FAILURE | DT_INVALID_PARAM;
}

hit->t = 0;
hit->pathCount = 0;
hit->pathCost = 0;

// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
return DT_FAILURE | DT_INVALID_PARAM;
if (prevRef && !m_nav->isValidPolyRef(prevRef))
return DT_FAILURE | DT_INVALID_PARAM;

float dir[3], curPos[3], lastPos[3];
float verts[DT_VERTS_PER_POLYGON*3+3];
Expand Down Expand Up @@ -2735,7 +2774,13 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
dtAssert(m_nodePool);
dtAssert(m_openList);

*resultCount = 0;
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
radius < 0 || !dtMathIsfinite(radius) ||
!filter || !resultCount || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
Expand Down Expand Up @@ -2901,7 +2946,14 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
dtAssert(m_nav);
dtAssert(m_nodePool);
dtAssert(m_openList);


if (!m_nav->isValidPolyRef(startRef) ||
!verts || nverts < 3 ||
!filter || !resultCount || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

*resultCount = 0;

// Validate input
Expand Down Expand Up @@ -3088,13 +3140,17 @@ dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float*
{
dtAssert(m_nav);
dtAssert(m_tinyNodePool);

*resultCount = 0;

// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
radius < 0 || !dtMathIsfinite(radius) ||
!filter || !resultCount || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

*resultCount = 0;

static const int MAX_STACK = 48;
dtNode* stack[MAX_STACK];
int nstack = 0;
Expand Down Expand Up @@ -3302,12 +3358,15 @@ dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter*
{
dtAssert(m_nav);

*segmentCount = 0;

const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;

if (!filter || !segmentVerts || !segmentCount || maxSegments < 0)
return DT_FAILURE | DT_INVALID_PARAM;

*segmentCount = 0;

int n = 0;
static const int MAX_INTERVAL = 16;
Expand Down Expand Up @@ -3455,8 +3514,13 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
dtAssert(m_openList);

// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
!filter || !hitDist || !hitPos || !hitNormal)
{
return DT_FAILURE | DT_INVALID_PARAM;
}

m_nodePool->clear();
m_openList->clear();
Expand Down

0 comments on commit ad71c30

Please sign in to comment.