fix(unitframes): stop reading the cast tint's alpha back, it can be secret - #1217
Merged
EllesmereGaming merged 1 commit intoAug 7, 2026
Merged
Conversation
…ecret Reported on 8.7.5 by @zoon: the target cast bar detaches to the centre of the screen, and 50x "attempt to compare a secret number value (execution tainted by 'EllesmereUIUnitFrames')" at ApplyCastFillOpacity. ApplyCastFillOpacity documents itself as value-blind, then does the one value-dependent thing in the function: if castbar.castTintLayer and castbar.castTintLayer:GetAlpha() > 0 then That read cannot be trusted to return a plain number. The same castbar drives _shieldedTint's alpha from the SECRET notInterruptible flag via SetAlphaFromBoolean, and once secrecy is in the castbar's render state an alpha read comes back secret. Comparing it inside our own tainted execution throws. It explains the reporter's exact repro. Nothing is wrong at login, because the tint has never been active and the alpha is still a plain 0. Fighting a FIELD BOSS is what seeds it: notInterruptible is secret for any non-player unit, so the boss's cast puts a secret into the castbar. Every later styling pass then throws, which is why it surfaces on the next thing that re-runs one, changing background transparency or zoning back to a city. That also accounts for the detached cast bar, which is the same bug and not a second one. The throw is at line 10202 of the per-unit styling pass, and the cast bar background's anchoring lives at 10975 in the same pass. Aborting at 10202 means castbarBg never gets its ClearAllPoints + SetPoint, so it keeps the creation-time anchor instead of the configured one. We write every one of these alphas ourselves, so own the state in a plain boolean and drop the comparison entirely. _castTintOn mirrors "the last alpha we wrote was above zero" and is set at all four write sites, so behaviour is identical to the old readback minus the secret hazard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The report
Reported by @zoon on 8.7.5. Two symptoms that turned out to be one bug:
attempt to compare a secret number value (execution tainted by 'EllesmereUIUnitFrames')atApplyCastFillOpacity.His repro is the part that decodes it: no error at login, but it appears after flying to a field boss, killing it, and taking a return stone back. Changing the cast bar's background transparency also triggers it.
Cause
ApplyCastFillOpacitydescribes itself as value-blind in its own header comment, then does the one value-dependent thing in the function:That read cannot be trusted to return a plain number. A safe secret sink is not a safe secret source. The same castbar drives
_shieldedTint's alpha from the secretnotInterruptibleflag viaSetAlphaFromBoolean, which is the sanctioned way to consume a secret. But it puts secrecy into that castbar's render state, and the alpha then reads back secret. Comparing it inside our own tainted execution throws.That is exactly why the field boss matters.
notInterruptibleis secret for any non-player unit, so the boss's cast is what seeds it. Before that the tint has never been active, the alpha is still a plain0, and0 > 0is harmless. Afterwards, every styling pass throws, which is why it surfaces on the next thing that re-runs one: a transparency change, or zoning back to a city.Why the cast bar also detaches
Same bug, not a second one. The throw is at line 10202 of the per-unit styling pass. The cast bar background's anchoring lives at 10975 in that same pass:
Aborting at 10202 means that never runs, so
castbarBgkeeps its creation-time anchor instead of the configured one.Fix
Own the state instead of interrogating the widget.
castbar._castTintOnmirrors "the last alpha we wrote was above zero", is set at all four write sites, and the comparison is deleted. We write every one of those alphas ourselves, so this removes the hazard rather than trying to reason about when the read happens to be safe.Behaviour is identical to the old readback, including the case where the flag stays true after a cast ends, which is what
GetAlpha()reported before.Notes for review
_castTintOnis written oncastbar, which isCreateFrame("StatusBar", nil, castbarBg), one of ours. No field is written to a Blizzard frame, so the fix cannot itself introduce the class of problem it repairs. The name also matches the castbar's existing_fillOp/_fillOpApplied/_durSidefields.git show v8.7.5:rather than trusting line numbers across versions: the failing line is byte-identical to current.GetAlpha()comparisons remain in the module.luac -pclean.Testing
Confirmed in game by the reporter on a build cut from this branch: the errors are gone and the cast bar stays attached across his original repro.