iOS in-the-wild vulnerability in vouchers [CVE-2021-1782]

We discussed this vulnerability during Episode 138 on 19 April 2022

There is a good deal of complexity in the object structure that is detailed in the post that I’m going to gloss over. Effectively you have an array of entry objects, and each entry has a pointer to a user_data_value_element. Since there is generally a one-to-one mapping of entries to these user data values only the entry is reference counted.

There is a race condition in the allocation process for a new entry where a pointer to the user_data_value_element has been claimed but not yet accounted for by the entry. During this window, another thread can drop the last reference to the entry resulting in the user_data_value_element getting unexpectedly freed.

To prevent this the value element tracks the number of pointers handed out in an e_made field, and the entry tracks the count. Then when the last reference is dropped before it free’s the user_data_value_element it checks these two counts, and if there is a desync it knows that it is not safe to free the user_data_value_element.

And so we have the core bug in that the e_made field of user_data_value_element is incremented without holding the appropriate lock. So you can have threads that should increment e_made both read the same value and add one to it, resulting in an e_made that is too low. Creating a situation where the value element can be inappropriately freed.