diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index f2270b047..eec3f3e1c 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -2522,6 +2522,19 @@ void Spell::cast(bool skipCheck) { SetExecutedCurrently(true); + if (!m_caster->CheckAndIncreaseCastCounter()) + { + if (m_triggeredByAuraSpell) + sLog.outError("Spell %u triggered by aura spell %u too deep in cast chain for cast. Cast not allowed for prevent overflow stack crash."); + else + sLog.outError("Spell %u too deep in cast chain for cast. Cast not allowed for prevent overflow stack crash."); + + SendCastResult(SPELL_FAILED_ERROR); + finish(false); + SetExecutedCurrently(false); + return; + } + // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); @@ -2529,6 +2542,7 @@ void Spell::cast(bool skipCheck) if(!m_targets.getUnitTarget() && m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID()) { cancel(); + m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); return; } @@ -2541,6 +2555,7 @@ void Spell::cast(bool skipCheck) { SendCastResult(castResult); finish(false); + m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); return; } @@ -2553,6 +2568,7 @@ void Spell::cast(bool skipCheck) { SendCastResult(castResult); finish(false); + m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); return; } @@ -2667,6 +2683,7 @@ void Spell::cast(bool skipCheck) if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in FillTargetMap { + m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); return; } @@ -2699,6 +2716,7 @@ void Spell::cast(bool skipCheck) handle_immediate(); } + m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 503233876..8453e6554 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -208,6 +208,8 @@ Unit::Unit() for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) m_currentSpells[i] = NULL; + m_castCounter = 0; + m_addDmgOnce = 0; for(int i = 0; i < MAX_TOTEM; ++i) @@ -13435,3 +13437,14 @@ void Unit::CleanupDeletedAuras() delete *itr; m_deletedAuras.clear(); } + +bool Unit::CheckAndIncreaseCastCounter() +{ + uint32 maxCasts = sWorld.getConfig(CONFIG_MAX_SPELL_CASTS_IN_CHAIN); + + if (maxCasts && m_castCounter >= maxCasts) + return false; + + ++m_castCounter; + return true; +} \ No newline at end of file diff --git a/src/game/Unit.h b/src/game/Unit.h index b85327068..dff9adc42 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1520,6 +1520,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; + bool CheckAndIncreaseCastCounter(); + void DecreaseCastCounter() { if (m_castCounter) --m_castCounter; } + uint32 m_addDmgOnce; uint64 m_TotemSlot[MAX_TOTEM]; uint64 m_ObjectSlot[4]; @@ -1848,6 +1851,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject uint32 m_CombatTimer; Spell* m_currentSpells[CURRENT_MAX_SPELL]; + uint32 m_castCounter; // count casts chain of triggered spells for prevent infinity cast crashes UnitVisibility m_Visibility; diff --git a/src/game/World.cpp b/src/game/World.cpp index 256626297..da0ad385f 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -795,6 +795,8 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfig.GetBoolDefault("Instance.IgnoreRaid", false); m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true); + m_configs[CONFIG_MAX_SPELL_CASTS_IN_CHAIN] = sConfig.GetIntDefault("MaxSpellCastsInChain", 10); + m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4); m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 30 * MINUTE * IN_MILISECONDS); diff --git a/src/game/World.h b/src/game/World.h index aef1cde7f..7508de28f 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -127,6 +127,7 @@ enum WorldConfigs CONFIG_INSTANCE_RESET_TIME_HOUR, CONFIG_INSTANCE_UNLOAD_DELAY, CONFIG_CAST_UNSTUCK, + CONFIG_MAX_SPELL_CASTS_IN_CHAIN, CONFIG_MAX_PRIMARY_TRADE_SKILL, CONFIG_MIN_PETITION_SIGNS, CONFIG_GM_LOGIN_STATE, diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 479a6ce5d..63201c584 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -508,6 +508,12 @@ LogColors = "" # Default: 1 (true) # 0 (false) # +# MaxSpellCastsInChain +# Max amount triggered spell casts in chain by one caster, prevent stack overflow crash +# Too Low value will make some correct triggered casts fail +# 0 (no limit) +# Default: 10 +# # Instance.IgnoreLevel # Ignore level requirement to enter instance # Default: 0 (false) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index c8551108d..e32c328d3 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9387" + #define REVISION_NR "9388" #endif // __REVISION_NR_H__