UIDropDownMenu displayMode taints dropdown initialization

If a tainted dropdown menu using the MENU displayMode is open, calling UIDropDownMenu_Initialize will taint the current execution path.

If this occurs when the Communities UI is loaded, the player will not be able to leave communities or create new community chat channels.

Affected versions: 8.0.1.26949, 8.1.0.29297 (unfixed).

To reproduce (Communities UI)

  1. Join at least one community.
  2. Create the following macro:
    /run CreateFrame("Frame", "T", nil, "UIDropDownMenuTemplate")
    /run T.displayMode = "MENU"
    /run UIDropDownMenu_Initialize(T, function() end)
    /run ToggleDropDownMenu(1, nil, T)
    /click GuildMicroButton
  3. Place it on your action bars, and run /reload
  4. Click the macro, then run the following command:
    /dump issecurevariable(CommunitiesFrameCommunitiesListListScrollFrameButton3, "clubId")
    It outputs [1]=false, [2]="", indicating that the clubId key on the community list entry is tainted, and will taint the context menu/streams dropdown when those are set up.

To reproduce (HonorFrame.type)

  1. Create the following macro:
    /run CreateFrame("Frame", "T", nil, "UIDropDownMenuTemplate")
    /run T.displayMode = "MENU"
    /run UIDropDownMenu_Initialize(T, function() end)
    /run ToggleDropDownMenu(1, nil, T)
    /click LFDMicroButton
    /click PVEFrameTab2
    /click HonorFrameQueueButton
  2. Place it on your action bars.
  3. Reload the UI: run /reload
  4. Click the macro on your action bar.
  5. A macro script has been blocked form an action only available to the Blizzard UI.

How this gets tainted

  1. UIDropDownMenu_AddButton accesses UIDROPDOWNMENU_OPEN_MENU.displayMode. If this key is tainted at the moment of secure dropdown initialization, it will taint the current execution path.
    Note that UIDROPDOWNMENU_OPEN_MENU refers to the last-opened dropdown menu (i.e. the one for which ToggleDropDownMenu was most recently called), which is not necessarily the menu currently being initialized (i.e. through UIDropDownMenu_Initialize).
  2. Somewhere in the Blizzard_Communities addon is a dropdown that is initialized on a critical path to initializing the list of communities.

Possible fixes

  1. UIDROPDOWNMENU_OPEN_MENU could be reset when initializing a menu.
    Modify UIDropDownMenuDelegate_OnAttributeChanged to overwrite UIDROPDOWNMENU_OPEN_MENU when the initmenu attribute changes.
    The currently-open menu does not presently remain open if another menu is initialized, even if the initializing menu is never actually opened/shown.
  2. Delay/securecall dropdown initialization in Blizzard_Communities.
    UIDropDownMenu_Initialize is usually a nice boundary, as the caller really doesn't care about what the dropdown does.

AddOn workaround

Note that the frame.displayMode key does not appear to be tainted if you assign it using the third argument of UIDropDownMenu_Initialize, i.e. the following macro does not trigger the issue:

/run CreateFrame("Frame", "T", nil, "UIDropDownMenuTemplate")
/run UIDropDownMenu_Initialize(T, function() end, "MENU")
/run ToggleDropDownMenu(1, nil, T)
/click GuildMicroButton
Initially secure code assigning literal constants to table entries does not taint the table entry even if the current execution path is tainted.

You can also work around this issue by including the following code in your addon:

if (UIDROPDOWNMENU_OPEN_PATCH_VERSION or 0) < 1 then UIDROPDOWNMENU_OPEN_PATCH_VERSION = 1 hooksecurefunc("UIDropDownMenu_InitializeHelper", function(frame) if UIDROPDOWNMENU_OPEN_PATCH_VERSION ~= 1 then return end if UIDROPDOWNMENU_OPEN_MENU and UIDROPDOWNMENU_OPEN_MENU ~= frame and not issecurevariable(UIDROPDOWNMENU_OPEN_MENU, "displayMode") then UIDROPDOWNMENU_OPEN_MENU = nil local t, f, prefix, i = _G, issecurevariable, " \0", 1 repeat i, t[prefix .. i] = i + 1 until f("UIDROPDOWNMENU_OPEN_MENU") end end) end