Dopamine jailbreak developer discusses elusive Spinlock Timeout Panic issue

The Dopamine jailbreak tool for A12-A15 devices running iOS & iPadOS 15.0-15.4.1 is the single latest jailbreak available for any device newer than the iPhone X at this point in time. Having said that, it’s no surprise that it’s a popular choice for jailbreakers today.

Lars Fröder Tweets to a GitHub page about Spinlock Timeout Panics on the Dopamine jailbreak.

But if you’ve been using Dopamine or following the project since its inception, then you’ve likely heard a certain word thrown around quite a few times by project lead developer Lars Fröder (@opa334dev) and users alike: Spinlock.

Indeed, there is a known issue that affects the Dopamine jailbreak called a Spinlock Timeout Panic, and it ultimately results a user’s device showing a pink screen and then rebooting in a seemingly unprovoked fashion. The issue has been described in technical terms as follows:

Mapping on top of dyld_shared_cache executable pages seems to trigger an edge case behavior in the PPL that sometimes causes a timeout on the spinlock of a memory page, resulting in a kernel panic.

The more tweaks that hook C functions are installed and the more processes those inject into, the more often this behavior seems to be triggered.

It appears this issue could be fixed by wiring down all pages that have been hooked, but the userspace cannot take such a lock and finding the vm_page object in kernel memory to flip the wired bit directly is proving to be difficult.

Since I’ve never personally experienced one of these issues on my Dopamine device, it’s been difficult to explain how this looks or when it happens, but I did speak with Fröder to ask about what they think could be causing it to learn more about how they’re attempting to address it.

Fröder’s response was insightful for non-technical people like me and probably many others in the jailbreak community and has since been published to a GitHub issue page for the public to see. The full response, quoted below, reveals Fröder’s understanding of the Spinlock Timeout Panic issue:

Here is an attempt at a more in-depth explanation of the issue, to my best current understanding of it. Keep in mind it is based on assumptions that are basically impossible to verify.

So in a multi-threaded system “locks” are used to prevent two threads from interfering with each other. By that one thread can acquire a lock, make the modification, and unlock it. While locked, another thread trying to acquire the lock will wait until the object has been unlocked again.

A spinlock is essentially the same thing, just used for performance relevant stuff and the main difference is that a spinlock can time out if something takes the lock too long while another thread is trying to acquire the lock. So when acquiring a lock and the object is already locked, it would wait for a few ticks and if the object doesn’t get unlocked in that time frame, it will time out.

This mechanism by itself is not the issue, the issue has to do with memory pages. Every memory page (which describes an area of 16kB of RAM) has a spinlock so that there are no issues when multiple processes try to acquire the same page at the same time.

Specific pages can be mapped into multiple processes (e.g. if both load the same library), they reuse the same page in order to save memory. Tweaks want to overwrite such memory on a per-process basis, so they have to first make a process-specific copy of the existing mapping and map it on top of it, so that e.g. one page can be modified in one process while remaining stock in the other processes. The issue seems to specifically happen when mapping on top of a page that resides inside the dyld_shared_cache.

The problem is now that Apple probably never tested this kind of hooking and apparently when you do it in a lot of processes, it can cause the original page (the one of the shared mapping) to be paged out, because it’s not actively being used. Paging out a page essentially removes it from RAM and when it is accessed again it will be loaded again. On a stock system this will not happen because nothing has been hooked.

Now the root cause appears to be something trying to page a previously paged out shared/executable page back in, this triggers a preemption issue where one thread takes the spinlock and while it has that, it gets preempted to a different context which also takes the same spinlock (Preemption essentially is a mechanism that allows one thread to be used for something else even if it’s currently busy, code has to explicitly disable and re-enable it if there is a piece of code that should always be executed in one go). So there seems to be one code path which is only invoked from this particular behavior where Apple does not correctly disable preemption, leading to one thread taking the same spinlock two times, which makes it time out because the old context isn’t executing anymore and can’t unlock the spinlock again.

As for mitigating it, I tried messing with spinlock related variables to make the threshold that it takes for it to time out higher, unfortunately Apple screwed us over because everything related to that is KTRR protected, for which we do not have a bypass. I guess the proper fix would be to “wire down” (wiring down a page prevents it from being paged out) every to-be-hooked page before it’s overwritten to ensure that the page out never happens and therefore the code path involved in the issue doesn’t trigger, I tried a bunch of stuff so far but it seems it’s straight up impossible to acquire such a wiring from userspace, so it has to be done inside the kernel. Unfortunately, the structures involved in this specific shared mapping that causes the issue are very convoluted and I have yet to find a way to get the correct page object to apply the wiring to.

The Spinlock Timeout Panic issue has been around since Dopamine first became available and continues to linger to this day despite many attempts to fix the issue. Having said that, opening the dialogue for more people to see and contribute to is the right step forward, as it makes it easier for more minds to brainstorm the issue and a possible solution.

Fröder explains their next idea for attempting to thwart the issue in a follow-up comment:

So the next step to try and fix it would be to find the vm_page structure of a DSC page in kernel memory, so far all my attempts at finding such a structure have failed.

While it hasn’t directly affected me yet, it will indeed be interesting to see if Fröder is able to solve the Spinlock Timeout Panic issue. It seems to be more prevalent for users who install more jailbreak tweaks that hook C functions. It could just be that my test device doesn’t have a lot of tweaks installed on it to trigger the issue, but I know there are many jailbreakers out there who install tons of jailbreak tweaks – more than I ever would.

Also see: How to jailbreak A12-A15 devices running iOS & iPadOS 15.0-15.4.1 with Dopamine

Have you ever been affected by a Spinlock Timeout Panic described as a pink screen before a sudden reboot while using the Dopamine jailbreak? Let us know in the comments section down below.