Taint in World of Warcraft
The taint system was introduced to World of Warcraft UI in Patch 2.0.1, with the goal of preventing addons from automating gameplay in ways the game designers find unacceptable. At the time of release, this primarily included automated debuff removal addons, macros selecting the right rank of healing abilities to cast (and cancelling would-be overheals), and potentially addons automating primary combat rotations.
The system works by designating some API functions (responsible for player actions like casting spells, learning talents, targetting players/mobs) as protected, meaning they only function if called from a secure execution path. All Blizzard UI code is considered secure by default, and can therefore call these functions as it pleases. Addon code, meanwhile, is considered tainted, and may not call these functions directly.
To prevent tainted addon code from manipulating secure code into calling protected functions on its behalf, the secure/tainted classification also applies to most locations that can store values: global variables, local variables, table keys, widget script handler slots, and function closures. Widget attributes are the one exception to this, and are always considered to be secure.
Once the current execution path becomes tainted (by either executing addon code, or accessing a tainted value), any variables it writes to, and any function closures it creates, are also marked as tainted. This can cause other execution paths to become tainted in the future, making it possible for taint to propagate through the UI in non-obvious ways.
Taint errors can manifest in various ways, including "Interface action failed because of an AddOn" messages appearing in chat, and popups inviting players to disable specific addons. Most of the errors actually encountered by players are a result of unintended propagation of taint, rather than actual automation behavior being prevented (because most published addons are not trying to automate gameplay). Unfortunately, tracking the root cause of those issues is often not straightforward, as some errors require a specific sequence of events to occur before they trigger.
To assist in fixing taint errors, the World of Warcraft client can log some taint-related information to a text file (World of Warcraft\Logs\taint.log). To enable this, type /console taintlog 1 into the chat box and press Enter. There are four different logging levels that you can use in place of the 1 in the chat command:
- Level 0
- Nothing is logged. This is the default.
- Level 1
- Actions blocked due to taint are logged.
- Level 2
- Also logs tainting or accessing tainted global variables.
- Level 11
- Also logs tainting or accessing tainted table entries.
The client buffers writing to taint.log, so log entries may not appear immediately. Additionally, the entire file is overwritten when the taint log is first written to after an interface reload or log in. If you are debugging a specific issue, it may be useful to set the log level to 0 before reloading the interface after triggering the issue.
Level 1: blocked protected actions
Level 1 logs consist entirely of stack traces of actions prevented due to taint. For example, if you were to type the /run CastSpellByName("Mark of the Wild") command in chat, the following entry would be added to the log; it consists of the client's best guess at when execution became tainted, and a stack trace at the location of the blocked action:
Some actions are only blocked when in combat, which is reflected in the log entry:
Level 2: global taint tracing
Level 2 logs also include stack traces of previously-secure global variables being tainted, and whenever a tainted global variable is accessed.
The following fragment illustrates a global variable being tainted by an addon:
It also logs when tainted global variables tainted are accessed*, which looks like this:
* Actual behavior of logging of access to tainted global variables is somewhat weird.
Level 11: tainted table keys
This log level can only be enabled on non-Release builds of World of Warcraft — in practice, it is only available on PTR and Beta realms while the next patch is being tested. While log level 2 only tracks tainting and accessing global variables (i.e. string keys in the _G table), log level 11 tracks taining and acessing tainted string keys in all tables.
Messages generated at this log level do not correctly distinguish between _G and other tables, refering to both of these as "global variables", so whether a message refers to tainting a global variable or a table key can only be determined by examining the code referenced by the stack trace.
The correct interpretation of the following fragment is that the key "Mangle" in some table has been tainted:
Similarly, the following fragment implies that the current execution path accessed a table key tainted by a different addon:
Not logged at any level
None of the taint log levels trace taint to or from local variables, widget properties, and non-string table keys. Therefore, it is possible for a taint log to contain nothing but the "action blocked" event even at higher levels of logging.