From f053a06920b294de3072a83faa464a1aa418aa92 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 9 Jul 2026 07:24:14 +0200 Subject: [PATCH] Fix core RNG collapsing to ~0-1 on Linux due to integer overflow CWorldServer::RandNumber (and three inlined copies of the same pattern in worldpackets.cpp's item-refine code) computed "range*rand()" in integer arithmetic before dividing by (RAND_MAX + 1.0). On Windows RAND_MAX is 32767 (15-bit), so this never overflowed. On Linux/glibc RAND_MAX is 2147483647 (2^31-1), so "range*rand()" overflows a 32-bit UINT for the vast majority of rand() outputs, wrapping to a small value -- the division by ~2^31 then collapses the result to ~0 or 1 almost always, regardless of the requested range. This made every percentage-based roll in the game (item drop selection, refine durability/stats, and anything else using RandNumber) behave as if called with range 1-2 instead of the real range, which is why item drops effectively never triggered post-port. Cast to double before the multiply so it can't overflow. Co-Authored-By: Claude Sonnet 5 --- Src/World Server/extrafunctions.cpp | 12 ++++++++---- Src/World Server/worldpackets.cpp | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Src/World Server/extrafunctions.cpp b/Src/World Server/extrafunctions.cpp index f7ea349..057ef90 100644 --- a/Src/World Server/extrafunctions.cpp +++ b/Src/World Server/extrafunctions.cpp @@ -1048,15 +1048,19 @@ UINT CWorldServer::RandNumber( UINT init, UINT range, UINT seed ) if(range>RAND_MAX) { UINT max = (UINT)GServer->round(sqrt((double)range)); - UINT num1 = init+int(max*rand()/(RAND_MAX + 1.0)); - UINT num2 = init+int(max*rand()/(RAND_MAX + 1.0)); - UINT num3 = init+int(max*rand()/(RAND_MAX + 1.0)); + // NOTE: multiply in double first -- on Linux RAND_MAX is 2^31-1 (vs. + // Windows' 15-bit 32767), so "max*rand()" done in integer arithmetic + // before dividing overflows UINT for nearly every rand() result, + // collapsing the output to ~0-1 regardless of range/max. + UINT num1 = init+int((double)max*rand()/(RAND_MAX + 1.0)); + UINT num2 = init+int((double)max*rand()/(RAND_MAX + 1.0)); + UINT num3 = init+int((double)max*rand()/(RAND_MAX + 1.0)); UINT res = (num1 * num2)+num3; return res>range?range:res; } else { - UINT ranum = init+int(range*rand()/(RAND_MAX + 1.0)); + UINT ranum = init+int((double)range*rand()/(RAND_MAX + 1.0)); return ranum; } diff --git a/Src/World Server/worldpackets.cpp b/Src/World Server/worldpackets.cpp index a4d37aa..db0cdd6 100644 --- a/Src/World Server/worldpackets.cpp +++ b/Src/World Server/worldpackets.cpp @@ -3162,18 +3162,18 @@ bool CWorldServer::pakCraft( CPlayer* thisclient, CPacket* P ) int lowest = thisclient->Attr->Con/ 10 + 17; int highest = thisclient->Attr->Con / 10 + 70; int range = (highest - lowest) + 1; - item.durability = lowest+int(range*rand()/(RAND_MAX + 1.0)); + item.durability = lowest+int((double)range*rand()/(RAND_MAX + 1.0)); // stats randomizer int changeofstatslow = thisclient->Attr->Sen / 13 + 10; int changeofstatshigh = thisclient->Attr->Sen / 13 + 50; int changeofstatsrange = (changeofstatshigh-changeofstatslow)+1; - if (changeofstatslow+int(changeofstatsrange*rand()/(RAND_MAX + 1.0)) > 50) + if (changeofstatslow+int((double)changeofstatsrange*rand()/(RAND_MAX + 1.0)) > 50) { int statslowget = 1; int statshighget = 256; int setstatrange=(statshighget-statslowget)+1; - item.stats = statslowget + int(setstatrange*rand()/(RAND_MAX + 1.0)); + item.stats = statslowget + int((double)setstatrange*rand()/(RAND_MAX + 1.0)); item.appraised = 1; } else