fix: sanitizer sweep across all generators; bound signed shifts/division/overflow at coordinate limits (L61); R31

New verify.py sanitize tier (Clang address,undefined,float-cast-overflow; GCC address,undefined;
-fno-sanitize-recover) runs 19 cases per compiler at LOD0-8 across every generator, passages and the
crossing: 0 runtime reports; a planted signed-overflow control is caught by both. A static scan fixed
INT_MIN negation in floor division, unbounded signed LOD shifts and coordinate-times-extent overflow in
VoxelClipmapDesiredTiles.h and VoxelTypes.h (plugin and standalone), and VoxelWorld.cpp, VoxelContentManager.cpp
and VoxelDensityVolume.cpp (plugin only, uncompiled here). Supported-input output unchanged (version 211).
fast/medium/full PASS. Research R31 (self-checked): far-LOD 4-8 cut plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-26 05:19:18 +01:00
1 parent c0c204ba50
commit 135957831b
13 files changed
+760 -132

No files matched your search

@@ -559,9 +559,13 @@ int32 UVoxelContentManager::RegionSize() const
// truncation, a negative-coord cell is queued under one region but routed back to another → its march
// result is dropped → blank chunk. Truncation == floor for A>=0 and for R==1, which is exactly why only
// NEGATIVE coordinates at R>=2 were affected. R (region size) is always > 0 here.
static FORCEINLINE int32 FloorDivPos(int32 A, int32 R)
static FORCEINLINE int32 FloorDivPos(int64 A, int32 R)
{
return (A >= 0) ? (A / R) : -(((-A) + R - 1) / R);
if (R <= 0) { return 0; }
const int64 Quotient = A / R;
const int64 Remainder = A % R;
return static_cast<int32>(FMath::Clamp<int64>(
Remainder < 0 ? Quotient - 1 : Quotient, MIN_int32, MAX_int32));
}
static FORCEINLINE FIntPoint CellToRegion(const FIntPoint& Cell, int32 R)
@@ -573,10 +577,10 @@ static FORCEINLINE FIntPoint CellToRegion(const FIntPoint& Cell, int32 R)
static FORCEINLINE bool IsRegionDesired(const FIntPoint& Region, const FIntPoint& PlayerCell,
int32 FarR, int32 R)
{
const int32 MinX = FloorDivPos(PlayerCell.X - FarR, R);
const int32 MaxX = FloorDivPos(PlayerCell.X + FarR, R);
const int32 MinY = FloorDivPos(PlayerCell.Y - FarR, R);
const int32 MaxY = FloorDivPos(PlayerCell.Y + FarR, R);
const int32 MinX = FloorDivPos(static_cast<int64>(PlayerCell.X) - FarR, R);
const int32 MaxX = FloorDivPos(static_cast<int64>(PlayerCell.X) + FarR, R);
const int32 MinY = FloorDivPos(static_cast<int64>(PlayerCell.Y) - FarR, R);
const int32 MaxY = FloorDivPos(static_cast<int64>(PlayerCell.Y) + FarR, R);
return Region.X >= MinX && Region.X <= MaxX && Region.Y >= MinY && Region.Y <= MaxY;
}
@@ -1102,17 +1106,22 @@ void UVoxelContentManager::RebuildDesiredCells(FDecoGrid& G, const FIntPoint& Pl
const int32 R = RegionSize();
// Desired regions = every region whose footprint touches the radius-FarR cell box around the player.
const FIntPoint RMin(FloorDivPos(PlayerCell.X - FarR, R),
FloorDivPos(PlayerCell.Y - FarR, R));
const FIntPoint RMax(FloorDivPos(PlayerCell.X + FarR, R),
FloorDivPos(PlayerCell.Y + FarR, R));
const FIntPoint RMin(FloorDivPos(static_cast<int64>(PlayerCell.X) - FarR, R),
FloorDivPos(static_cast<int64>(PlayerCell.Y) - FarR, R));
const FIntPoint RMax(FloorDivPos(static_cast<int64>(PlayerCell.X) + FarR, R),
FloorDivPos(static_cast<int64>(PlayerCell.Y) + FarR, R));
TSet<FIntPoint> DesiredRegions;
DesiredRegions.Reserve((RMax.X - RMin.X + 1) * (RMax.Y - RMin.Y + 1));
for (int32 ry = RMin.Y; ry <= RMax.Y; ++ry)
for (int32 rx = RMin.X; rx <= RMax.X; ++rx)
constexpr int64 MaxRegionReserve = 4096;
const int64 WidthX = static_cast<int64>(RMax.X) - RMin.X + 1;
const int64 WidthY = static_cast<int64>(RMax.Y) - RMin.Y + 1;
const int64 ReserveCount = WidthY > 0 && WidthX > MaxRegionReserve / WidthY
? MaxRegionReserve : FMath::Max<int64>(0, WidthX * WidthY);
DesiredRegions.Reserve(static_cast<int32>(ReserveCount));
for (int64 ry = RMin.Y; ry <= RMax.Y; ++ry)
for (int64 rx = RMin.X; rx <= RMax.X; ++rx)
{
DesiredRegions.Add(FIntPoint(rx, ry));
DesiredRegions.Add(FIntPoint(static_cast<int32>(rx), static_cast<int32>(ry)));
}
// Unload loaded regions no longer desired (plain DestroyComponent — no per-instance removal).
@@ -1464,8 +1473,15 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
const uint32 CH = VoxelHash::Mix(H ^ (0x00C0FFEEu + (uint32)ci * 0x9E3779B1u));
if (VoxelHash::ToFloat01(CH) > Comp.Probability) continue;
const int32 Span = FMath::Max(0, Comp.CountMax - Comp.CountMin);
const int32 Count = Comp.CountMin + (int32)(VoxelHash::ToFloat01(VoxelHash::Mix(CH ^ 0x1234u)) * (float)(Span + 1));
const int64 Span = FMath::Max<int64>(0,
static_cast<int64>(Comp.CountMax) - Comp.CountMin);
const uint64 CountRange = static_cast<uint64>(Span) + 1u;
const float CountOffset = VoxelHash::ToFloat01(
VoxelHash::Mix(CH ^ 0x1234u)) * static_cast<float>(CountRange);
const int64 CountWide = static_cast<int64>(Comp.CountMin)
+ static_cast<int64>(CountOffset);
const int32 Count = static_cast<int32>(FMath::Clamp<int64>(
CountWide, MIN_int32, MAX_int32));
for (int32 ii = 0; ii < Count; ++ii)
{
if (CompBudget <= 0) break;
@@ -1514,8 +1530,15 @@ void UVoxelContentManager::BuildCellSpawns(const UVoxelGenerator* Gen, const FTr
const uint32 SH = VoxelHash::Mix(IH ^ (0x0000544Bu + (uint32)si * 0x27D4EB2Fu));
if (VoxelHash::ToFloat01(SH) > Sub.Probability) continue;
const int32 SubSpan = FMath::Max(0, Sub.CountMax - Sub.CountMin);
const int32 SubCount = Sub.CountMin + (int32)(VoxelHash::ToFloat01(VoxelHash::Mix(SH ^ 0x1234u)) * (float)(SubSpan + 1));
const int64 SubSpan = FMath::Max<int64>(0,
static_cast<int64>(Sub.CountMax) - Sub.CountMin);
const uint64 SubCountRange = static_cast<uint64>(SubSpan) + 1u;
const float SubCountOffset = VoxelHash::ToFloat01(
VoxelHash::Mix(SH ^ 0x1234u)) * static_cast<float>(SubCountRange);
const int64 SubCountWide = static_cast<int64>(Sub.CountMin)
+ static_cast<int64>(SubCountOffset);
const int32 SubCount = static_cast<int32>(FMath::Clamp<int64>(
SubCountWide, MIN_int32, MAX_int32));
for (int32 sj = 0; sj < SubCount; ++sj)
{
if (CompBudget <= 0) break;