mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
fix(dbc): reject negative IDs in DBC::Record
DBC::Record checked the ID against the store's count as a signed int. A DBC column uses -1 for "none", and -1 passed that check, so the helper returned records[0xFFFFFFFF]. On 32-bit that wraps to records[-1], the heap word in front of the index array. A caller that passed an unchecked column read a garbage "record": an empty SpellVisualKit effect column gave the pointer 0xF and crashed. The engine rejects these IDs before it compares with the count: the char-select ghost kit's effect step tests the ID with a signed jl at 0x00472797, then a jg against the count. Record now compares unsigned, so it rejects 0, every negative ID, an ID above the count, and an unloaded store, whose count is -1. PvP::Arena had its own copy of the lookup with the same signed compare. It now calls DBC::Record.
This commit is contained in:
+5
-3
@@ -25,11 +25,13 @@ const char *NormalizeEmpty(const char *s) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// The id is compared unsigned: a DBC column's "none" is -1, which a signed
|
||||
// compare passes as less than the count and then indexes records[-1]. The
|
||||
// engine rejects it with a signed `jl` before its `jg` against the count
|
||||
// (0x00472797). An unloaded store's count is -1, so it takes no id.
|
||||
const uint8_t *Record(uintptr_t recordsVar, uintptr_t countVar, uint32_t id) {
|
||||
if (id == 0)
|
||||
return nullptr;
|
||||
const int count = *reinterpret_cast<const int *>(countVar);
|
||||
if (static_cast<int>(id) > count)
|
||||
if (id == 0 || count <= 0 || id > static_cast<uint32_t>(count))
|
||||
return nullptr;
|
||||
const uint8_t *const *records =
|
||||
*reinterpret_cast<const uint8_t *const *const *>(recordsVar);
|
||||
|
||||
+2
-1
@@ -39,7 +39,8 @@ namespace DBC {
|
||||
|
||||
// Resolves `id` to its record pointer in the table whose records-base
|
||||
// global is at `recordsVar` and count global at `countVar`. Returns
|
||||
// `nullptr` for `id == 0`, OOR ids (> count), and unpopulated slots.
|
||||
// `nullptr` for `id == 0`, OOR ids (> count, which includes -1 and every
|
||||
// other negative id), and unpopulated slots.
|
||||
// Caller reads whatever fields it needs off the returned record.
|
||||
//
|
||||
// Use this when `LocalizedField` / `StringField` don't fit — e.g. for
|
||||
|
||||
+3
-11
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -46,17 +47,8 @@ namespace {
|
||||
|
||||
constexpr uint32_t MAP_INSTANCE_TYPE_PVP = 3;
|
||||
|
||||
const uint8_t *DbcRecord(uintptr_t recordsVar, uintptr_t countVar, uint32_t id) {
|
||||
const int count = *reinterpret_cast<const int *>(countVar);
|
||||
if (id == 0 || static_cast<int>(id) > count)
|
||||
return nullptr;
|
||||
const uint8_t *const *records =
|
||||
*reinterpret_cast<const uint8_t *const *const *>(recordsVar);
|
||||
return records != nullptr ? records[id] : nullptr;
|
||||
}
|
||||
|
||||
bool IsArenaArea(uint32_t areaID) {
|
||||
const uint8_t *rec = DbcRecord(Offsets::VAR_AREATABLE_RECORDS,
|
||||
const uint8_t *rec = DBC::Record(Offsets::VAR_AREATABLE_RECORDS,
|
||||
Offsets::VAR_AREATABLE_COUNT, areaID);
|
||||
if (rec == nullptr)
|
||||
return false;
|
||||
@@ -69,7 +61,7 @@ bool InArena() {
|
||||
const uint32_t mapID = *reinterpret_cast<const uint32_t *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_CURRENT_MAP_ID));
|
||||
const uint8_t *map =
|
||||
DbcRecord(Offsets::VAR_MAP_RECORDS, Offsets::VAR_MAP_COUNT, mapID);
|
||||
DBC::Record(Offsets::VAR_MAP_RECORDS, Offsets::VAR_MAP_COUNT, mapID);
|
||||
if (map == nullptr)
|
||||
return false;
|
||||
const uint32_t type =
|
||||
|
||||
Reference in New Issue
Block a user