mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
Harden GuildRosterParser against unbounded memory allocation
Cap numMembers to 1000 and rankCount to 20 to prevent OOM attacks. Add per-field bounds checking for member data with graceful truncation.
This commit is contained in:
parent
2c67331bc3
commit
3849ad75ce
1 changed files with 92 additions and 11 deletions
|
|
@ -2109,16 +2109,49 @@ bool GuildRosterParser::parse(network::Packet& packet, GuildRosterData& data) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t numMembers = packet.readUInt32();
|
uint32_t numMembers = packet.readUInt32();
|
||||||
|
|
||||||
|
// Cap members and ranks to prevent unbounded memory allocation
|
||||||
|
const uint32_t MAX_GUILD_MEMBERS = 1000;
|
||||||
|
if (numMembers > MAX_GUILD_MEMBERS) {
|
||||||
|
LOG_WARNING("GuildRosterParser: numMembers capped (requested=", numMembers, ")");
|
||||||
|
numMembers = MAX_GUILD_MEMBERS;
|
||||||
|
}
|
||||||
|
|
||||||
data.motd = packet.readString();
|
data.motd = packet.readString();
|
||||||
data.guildInfo = packet.readString();
|
data.guildInfo = packet.readString();
|
||||||
|
|
||||||
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
|
LOG_WARNING("GuildRosterParser: truncated before rankCount");
|
||||||
|
data.ranks.clear();
|
||||||
|
data.members.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t rankCount = packet.readUInt32();
|
uint32_t rankCount = packet.readUInt32();
|
||||||
|
|
||||||
|
// Cap rank count to prevent unbounded allocation
|
||||||
|
const uint32_t MAX_GUILD_RANKS = 20;
|
||||||
|
if (rankCount > MAX_GUILD_RANKS) {
|
||||||
|
LOG_WARNING("GuildRosterParser: rankCount capped (requested=", rankCount, ")");
|
||||||
|
rankCount = MAX_GUILD_RANKS;
|
||||||
|
}
|
||||||
|
|
||||||
data.ranks.resize(rankCount);
|
data.ranks.resize(rankCount);
|
||||||
for (uint32_t i = 0; i < rankCount; ++i) {
|
for (uint32_t i = 0; i < rankCount; ++i) {
|
||||||
|
// Validate 4 bytes before each rank rights read
|
||||||
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
|
LOG_WARNING("GuildRosterParser: truncated rank at index ", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
data.ranks[i].rights = packet.readUInt32();
|
data.ranks[i].rights = packet.readUInt32();
|
||||||
data.ranks[i].goldLimit = packet.readUInt32();
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
|
data.ranks[i].goldLimit = 0;
|
||||||
|
} else {
|
||||||
|
data.ranks[i].goldLimit = packet.readUInt32();
|
||||||
|
}
|
||||||
// 6 bank tab flags + 6 bank tab items per day
|
// 6 bank tab flags + 6 bank tab items per day
|
||||||
for (int t = 0; t < 6; ++t) {
|
for (int t = 0; t < 6; ++t) {
|
||||||
|
if (packet.getReadPos() + 8 > packet.getSize()) break;
|
||||||
packet.readUInt32(); // tabFlags
|
packet.readUInt32(); // tabFlags
|
||||||
packet.readUInt32(); // tabItemsPerDay
|
packet.readUInt32(); // tabItemsPerDay
|
||||||
}
|
}
|
||||||
|
|
@ -2126,20 +2159,68 @@ bool GuildRosterParser::parse(network::Packet& packet, GuildRosterData& data) {
|
||||||
|
|
||||||
data.members.resize(numMembers);
|
data.members.resize(numMembers);
|
||||||
for (uint32_t i = 0; i < numMembers; ++i) {
|
for (uint32_t i = 0; i < numMembers; ++i) {
|
||||||
|
// Validate minimum bytes before reading member (guid+online+name at minimum is 9+ bytes)
|
||||||
|
if (packet.getReadPos() + 9 > packet.getSize()) {
|
||||||
|
LOG_WARNING("GuildRosterParser: truncated member at index ", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
auto& m = data.members[i];
|
auto& m = data.members[i];
|
||||||
m.guid = packet.readUInt64();
|
m.guid = packet.readUInt64();
|
||||||
m.online = (packet.readUInt8() != 0);
|
m.online = (packet.readUInt8() != 0);
|
||||||
m.name = packet.readString();
|
|
||||||
m.rankIndex = packet.readUInt32();
|
// Validate before reading name string
|
||||||
m.level = packet.readUInt8();
|
if (packet.getReadPos() >= packet.getSize()) {
|
||||||
m.classId = packet.readUInt8();
|
m.name.clear();
|
||||||
m.gender = packet.readUInt8();
|
} else {
|
||||||
m.zoneId = packet.readUInt32();
|
m.name = packet.readString();
|
||||||
if (!m.online) {
|
}
|
||||||
m.lastOnline = packet.readFloat();
|
|
||||||
|
// Validate before reading rank/level/class/gender/zone
|
||||||
|
if (packet.getReadPos() + 1 > packet.getSize()) {
|
||||||
|
m.rankIndex = 0;
|
||||||
|
m.level = 1;
|
||||||
|
m.classId = 0;
|
||||||
|
m.gender = 0;
|
||||||
|
m.zoneId = 0;
|
||||||
|
} else {
|
||||||
|
m.rankIndex = packet.readUInt32();
|
||||||
|
if (packet.getReadPos() + 3 > packet.getSize()) {
|
||||||
|
m.level = 1;
|
||||||
|
m.classId = 0;
|
||||||
|
m.gender = 0;
|
||||||
|
} else {
|
||||||
|
m.level = packet.readUInt8();
|
||||||
|
m.classId = packet.readUInt8();
|
||||||
|
m.gender = packet.readUInt8();
|
||||||
|
}
|
||||||
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
|
m.zoneId = 0;
|
||||||
|
} else {
|
||||||
|
m.zoneId = packet.readUInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Online status affects next fields
|
||||||
|
if (!m.online) {
|
||||||
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
|
m.lastOnline = 0.0f;
|
||||||
|
} else {
|
||||||
|
m.lastOnline = packet.readFloat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read notes
|
||||||
|
if (packet.getReadPos() >= packet.getSize()) {
|
||||||
|
m.publicNote.clear();
|
||||||
|
m.officerNote.clear();
|
||||||
|
} else {
|
||||||
|
m.publicNote = packet.readString();
|
||||||
|
if (packet.getReadPos() >= packet.getSize()) {
|
||||||
|
m.officerNote.clear();
|
||||||
|
} else {
|
||||||
|
m.officerNote = packet.readString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.publicNote = packet.readString();
|
|
||||||
m.officerNote = packet.readString();
|
|
||||||
}
|
}
|
||||||
LOG_INFO("Parsed SMSG_GUILD_ROSTER: ", numMembers, " members, motd=", data.motd);
|
LOG_INFO("Parsed SMSG_GUILD_ROSTER: ", numMembers, " members, motd=", data.motd);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue