Yet Another Futex Vulnerability Found in the Kernel (CVE-2021-3347)
Linux vulnerabilities pile up. Year in, year out. You could say it’s inevitable really, given today’s complex computing environment. It is nonetheless frustrating when the same critical elements of the Linux OS kernel continue to surface as a vulnerable area.
Up to and including 2020, there are fourteen listed CVEs that cover the Linux futex implementation. Granted, futexes are formidably complex. Though futexes provide essential functionality, futexes are often not clearly understood, and some might argue that vulnerabilities are inevitable given the complexity of futex implementation.
Unfortunately, at the end of January 2021, another Linux kernel vulnerability that involves futex mishandling emerged. Worse, it involves a dangerous use-after-free vulnerability.
In this article, we outline why the Linux kernel has a futex system call and explain why the complexities of futex implementations can lead to vulnerabilities. You will also read more about use-after-free vulnerabilities and why these are so dangerous.
We’ll cover what we know about the unique characteristics of the new vulnerability – and what your organization can do to guard against it, and against Linux kernel vulnerabilities in the broad.
Introduction to futexes
To understand why the latest Linux vulnerability is so critical we must first understand how futexes work.
The use of a futex, and the vulnerability created by futex misuse, involve low-level OS capabilities that are used to help co-ordinate processes in an application, particularly where an application needs to co-ordinate interdependent tasks that carry different priorities.
So, what is a futex?
Simultaneous tasks often require control over the same resources such as variables but do so at different times, and at a differing pace. The operating system needs to co-ordinate this control over resources. Before futexes were introduced in the Linux kernel in 2003, system calls were required to lock and to unlock resources that are shared.
However, systems calls are relatively processor intensive. A futex (short for “fast userspace mutex”) provides an alternative to constantly locking and unlocking a resource via system calls. Instead, a futex provides a resource-efficient way for processes to share a lock over a variable. Futexes are complex, and an exact explanation is beyond the scope of this article. You can read a bit more in the Wikipedia article here.
Futexes have evolved with time to adapt to a changing computing environment. One of these evolutions is the introduction of what’s called a priority inheritance or PI-futex.
Why do we need a PI-futex? Under the standard futex architecture, the operating system can struggle to guarantee that a high priority task enjoys deterministic execution where that high priority task shares a lock with a low priority task.
As a real-life example, consider a media player. First, the media player must ensure that the audio or video media is fed through your computer in real-time with no hiccups or disruptions. It is a high priority task. Users will quickly get frustrated if media does not play smoothly.
At the same time, the media player must handle other tasks with medium and low priorities: think about player controls, album art, and recommendations, for example. Juggling these operations in a way that doesn’t affect the user experience can be challenging for programmers as there are common elements involved – all progressing at different speeds.
There are countless other situations where an application must handle a variety of tasks with different priorities – but oftentimes involving the same data. One example would be a web server that services concurrent queries from users around the globe.
A PI-futex is therefore a specially designed futex that enables the operating system to implement shared locks, while simultaneously ensuring that instructions with different priorities are executed in a timely manner.
A long history of futex exploits
The complexity of PI-futexes should give you some insight into why handling futexes can relatively easily lead to security issues. In the broad, multiple core systems exacerbate problems with futexes as programmers can struggle to handle the complexity of futexes, and therefore apply poorly implemented concurrent programming that opens the door to exploits.
It is no surprise then that there are already 14 known futex vulnerabilities in the Linux kernel. The first one, CVE-2005-0937, was reported back in 2005. One of these vulnerabilities received a lot of coverage as it led to the possibility of a privilege-escalation attack in Android which enabled users to root Android phones just by using a web browser.
Called the Towelroot exploit, the vulnerability was assigned CVE-2014-3153. The exploit revolves around a bug in the futex implementation, and in the futex queuing functionality to be specific. Just like the latest vulnerability, Towelroot also involves PI-futexes.
By overloading the futex queue a hacker can overload Android, leaving it in a state where it thinks that the active app should be root. This, in turn, opens the door to further exploits. The vulnerability applies beyond Android to include many Linux-based operating systems.
Understanding use-after-free vulnerabilities
At the core of the latest Linux futex exploit is something called a use-after-free or UAF vulnerability. Before we examine the exploit itself, let’s take a look at what a UAF vulnerability entails.
The user-after-free scenario happens when an application tries to access a piece of memory even though that piece of memory has already been freed.
UAF scenarios vary, but one example can be where a pointer in a program points to a data set in dynamic memory that has already been deleted. Ordinarily, the pointer should be cleared to reflect the deleted data. However, sometimes due to programming errors the pointer continues to exist and refers to empty programming memory. It’s called a dangling pointer.
A dangling pointer can lead to data corruption and program crashes. Dangling pointers and UAFs in the broad can be exploited by hackers to execute arbitrary, malicious code.
Use-after-free vulnerabilities are relatively common and exists across the development space. UAF exploits are common in part because it is quite difficult to identify a UAF manually: UAFs tend to exist because of actions combining across different parts of an application, so detecting a UAF requires inspecting the entire codebase as a whole rather than just individual segments of code.
It is critical, therefore, that the latest Linux futex vulnerability creates the space for UAF exploits that can be extremely hard to detect.
What we know about the new futex vulnerability
As we stated earlier, the latest vulnerability also involves PI-futexes. Full details are still emerging, but essentially this particular futex vulnerability creates the risk of a user-after-free exploit.
What we know so far is that, in essence, when an application mishandles a PI-futex the Linux kernel can return an inconsistent state. In some cases, this inconsistent state can result in a use-after-free opportunity in the Linux tasks kernel stack.
The full details of the latest vulnerability have not yet been published but we expect this to emerge soon. Investigations done by our team suggests that the problem affects all Linux distributions using a kernel from 2008 and later – essentially covering all Linux implementations in the wild.
The potential impact of the latest futex vulnerability
There is particular concern around the fact that this particular futex vulnerability opens the door to use-after-free abuse. And, clearly, the number of Linux systems affected is very disconcerting.
Of course, the issue with futex vulnerabilities and UAF vulnerabilities in the broad is that they can lead to a wide variety of exploits if a programmer gets creative. Currently, there is no exploit for the latest vulnerability, but the risk is high that exploit code will emerge.
We expect the risks of this vulnerability to revolve around the risks typical of UAF exploits. Think about DoS attacks, for example. There is a risk that the new futex vulnerability can be used to crash systems with the goal to disrupt operations – or to deny service to your clients. This is because UAF vulnerabilities can often be used to crash running applications.
UAFs also commonly enable hackers to execute malicious code, which in turn can lead to a whole array of common cybercrime issues such as data theft. Readers will understand the potential consequences – financial loss, reputational damage, and the like.
Guarding against futex exploits – and other kernel vulnerabilities
When it comes to Linux kernel vulnerabilities patching is, and has always been, the answer. Futex vulnerabilities have consistently led to kernel changes – and matching patches. What is less consistent, however, is the application of these patches.
If your organization consistently applies patches it will likely enjoy protection against the latest futex vulnerability before exploits start to emerge in the wild. However, patching consistently isn’t as easy as it sounds.
Patching is time-consuming and often requires restarts that implies downtime – and either unhappy customers, or loss of revenue. For that reason, many companies neglect patching which can mean known vulnerabilities remain exploitable.
Automated, rebootless patching is an alternative. KernelCare, for example, can effortlessly keep your Linux distributions patched, covering you against known vulnerabilities automatically – including the latest futex-related exploits.
Currently, there is no exploit in the wild that we know of that will put your systems in danger based on this newly discovered Linux kernel vulnerability. However, there is a high risk that exploits will emerge, it is simply in the nature of UAF vulnerabilities.
As a concerned sysadmin or security expert, you should treat this new vulnerability just as you would any other vulnerability. Patch it when the patch is released.
Don’t have the resources to patch? Fed up with patching-related downtime? Give KernelCare a try.