TaintLess: UIDropDownMenu_Refresh accesses uninitialized buttons

UIDropDownMenu_Refresh, called by UIDropDownMenu_SetSelected*, accesses buttons beyond those present and initialized by the current dropdown. If the previously-open dropdown was insecure and used specific info keys, this will taint the current execution.

UIDropDownMenu_SetSelected* is widely used on secure execution paths. Blizzard_CUFProfiles is one example of this, causing errors when the Interface Options frame is closed while in combat lockdown.

Affected versions: Classic (unfixed), Modern until 11.0 (mitigated by migrating FrameXML to another menu implementation)

To reproduce

  1. Create the following macro and place it on your action bars:
    Patch 8.2.5+
    /click GameMenuButtonUIOptions /run t = {} for i=1,8 do t[i] = {text="boo", checked=true} end EasyMenu(t, CreateFrame("Frame", "T", nil, "UIDropDownMenuTemplate"), "cursor", 0,0,"MENU") /click InterfaceOptionsFrameCancel
  2. Reload the UI: run /reload
  3. While in combat lockdown and in a group, click the macro.
    Proving Grounds Healer/Tank trials work well for this.
  4. Interface action failed because of an AddOn
    An action was blocked in combat because of taint from MACRO_TAINT - CompactRaidFrameContainer:Show() Interface\AddOns\Blizzard_CompactRaidFrames\Blizzard_CompactRaidFrameManager.lua:525 CompactRaidFrameManager_UpdateContainerVisibility() Interface\AddOns\Blizzard_CompactRaidFrames\Blizzard_CompactRaidFrameManager.lua:472 CompactRaidFrameManager_SetIsShown() Interface\AddOns\Blizzard_CompactRaidFrames\Blizzard_CompactRaidFrameManager.lua:511 CompactRaidFrameManager_SetSetting() Interface\AddOns\Blizzard_CUFProfiles\Blizzard_CompactUnitFrameProfiles.lua:594 func() Interface\AddOns\Blizzard_CUFProfiles\Blizzard_CompactUnitFrameProfiles.lua:571 CompactUnitFrameProfiles_ApplyProfile() Interface\AddOns\Blizzard_CUFProfiles\Blizzard_CompactUnitFrameProfiles.lua:174 CompactUnitFrameProfiles_ApplyCurrentSettings() Interface\AddOns\Blizzard_CUFProfiles\Blizzard_CompactUnitFrameProfiles.lua:83 CompactUnitFrameProfiles_CancelChanges() Interface\AddOns\Blizzard_CUFProfiles\Blizzard_CompactUnitFrameProfiles.lua:76 pcall() Interface\FrameXML\InterfaceOptionsFrame.lua:214 securecall() Interface\FrameXML\InterfaceOptionsFrame.lua:249

How this gets tainted

  1. Cancelling out of the Interface Options frame calls the cancel handler on all registered panels. For Blizzard_CUFProfiles, this eventually reaches CompactUnitFrameProfiles​_CancelChanges.
  2. CUFProfiles' cancel handler requests that the client restore the relevant settings, updates the panel's UI controls to reflect the restored settings, and finally pushes the settings to secure elements.
  3. While updating UI controls, UIDropDownMenu_SetSelectedValue is called for dropdowns, which in turn calls UIDropDownMenu_Refresh.
  4. UIDropDownMenu_Refresh reads past the entries in the currently-open dropdown, accessing the checked and notCheckable keys on buttons that may have been set insecurely. This taints the execution.

Possible fixes

  1. Patch UIDropDownMenu_Refresh:
    -- Just redraws the existing menu - for i=1, UIDROPDOWNMENU_MAXBUTTONS do + for i=1, listFrame.numButtons do

AddOn workaround

A workaround for this issue is included in TaintLess.