Detect all overflows in rcBuildHeightfieldLayers (#214)

The function now fails if it fails to insert all layers because the
array overflowed. Previously this would silently corrupt the heap.
This commit is contained in:
Jakob Botsch Nielsen 2016-07-08 01:51:23 +03:00 committed by GitHub
parent ab0a679d8f
commit 9763478a9a

View File

@ -27,7 +27,9 @@
#include "RecastAssert.h" #include "RecastAssert.h"
static const int RC_MAX_LAYERS = RC_NOT_CONNECTED; // Must be 255 or smaller (not 256) because layer IDs are stored as
// a byte where 255 is a special value.
static const int RC_MAX_LAYERS = 63;
static const int RC_MAX_NEIS = 16; static const int RC_MAX_NEIS = 16;
struct rcLayerRegion struct rcLayerRegion
@ -42,25 +44,31 @@ struct rcLayerRegion
}; };
static void addUnique(unsigned char* a, unsigned char& an, unsigned char v)
{
const int n = (int)an;
for (int i = 0; i < n; ++i)
if (a[i] == v)
return;
a[an] = v;
an++;
}
static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v) static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v)
{ {
const int n = (int)an; const int n = (int)an;
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
{
if (a[i] == v) if (a[i] == v)
return true; return true;
}
return false; return false;
} }
static bool addUnique(unsigned char* a, unsigned char& an, int anMax, unsigned char v)
{
if (contains(a, an, v))
return true;
if ((int)an >= anMax)
return false;
a[an] = v;
an++;
return true;
}
inline bool overlapRange(const unsigned short amin, const unsigned short amax, inline bool overlapRange(const unsigned short amin, const unsigned short amax,
const unsigned short bmin, const unsigned short bmax) const unsigned short bmin, const unsigned short bmax)
{ {
@ -258,8 +266,13 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
const int ay = y + rcGetDirOffsetY(dir); const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
const unsigned char rai = srcReg[ai]; const unsigned char rai = srcReg[ai];
if (rai != 0xff && rai != ri && regs[ri].nneis < RC_MAX_NEIS) if (rai != 0xff && rai != ri)
addUnique(regs[ri].neis, regs[ri].nneis, rai); {
// Don't check return value -- if we cannot add the neighbor
// it will just cause a few more regions to be created, which
// is fine.
addUnique(regs[ri].neis, regs[ri].nneis, RC_MAX_NEIS, rai);
}
} }
} }
@ -274,8 +287,13 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
{ {
rcLayerRegion& ri = regs[lregs[i]]; rcLayerRegion& ri = regs[lregs[i]];
rcLayerRegion& rj = regs[lregs[j]]; rcLayerRegion& rj = regs[lregs[j]];
addUnique(ri.layers, ri.nlayers, lregs[j]);
addUnique(rj.layers, rj.nlayers, lregs[i]); if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, lregs[j]) ||
!addUnique(rj.layers, rj.nlayers, RC_MAX_LAYERS, lregs[i]))
{
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
return false;
}
} }
} }
} }
@ -338,7 +356,13 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
regn.layerId = layerId; regn.layerId = layerId;
// Merge current layers to root. // Merge current layers to root.
for (int k = 0; k < regn.nlayers; ++k) for (int k = 0; k < regn.nlayers; ++k)
addUnique(root.layers, root.nlayers, regn.layers[k]); {
if (!addUnique(root.layers, root.nlayers, RC_MAX_LAYERS, regn.layers[k]))
{
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
return false;
}
}
root.ymin = rcMin(root.ymin, regn.ymin); root.ymin = rcMin(root.ymin, regn.ymin);
root.ymax = rcMax(root.ymax, regn.ymax); root.ymax = rcMax(root.ymax, regn.ymax);
} }
@ -416,7 +440,14 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
rj.layerId = newId; rj.layerId = newId;
// Add overlaid layers from 'rj' to 'ri'. // Add overlaid layers from 'rj' to 'ri'.
for (int k = 0; k < rj.nlayers; ++k) for (int k = 0; k < rj.nlayers; ++k)
addUnique(ri.layers, ri.nlayers, rj.layers[k]); {
if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, rj.layers[k]))
{
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
return false;
}
}
// Update height bounds. // Update height bounds.
ri.ymin = rcMin(ri.ymin, rj.ymin); ri.ymin = rcMin(ri.ymin, rj.ymin);
ri.ymax = rcMax(ri.ymax, rj.ymax); ri.ymax = rcMax(ri.ymax, rj.ymax);