|
|
Log in / Subscribe / Register

Fighting Spectre with cache flushes

This article brought to you by LWN subscribers

Subscribers to LWN.net made this article — and everything that surrounds it — possible. If you appreciate our content, please buy a subscription and make the next set of articles possible.

By Jonathan Corbet
October 15, 2018
One of the more difficult aspects of the Spectre hardware vulnerability is finding all of the locations in the code that might be exploitable. There are many locations that look vulnerable that aren't, and others that are exploitable without being obvious. It has long been clear that finding all of the exploitable spots is a long-term task, and keeping new ones from being introduced will not be easy. But there may be a simple technique that can block a large subset of the possible exploits with a minimal cost.

Speculative-execution vulnerabilities are only exploitable if they leave a sign somewhere else in the system. As a general rule, that "somewhere else" is the CPU's memory cache. Speculative execution can be used to load data into the cache (or not) depending on the value of the data the attacker is trying to exfiltrate; timing attacks can then be employed to query the state of the cache and complete the attack. This side channel is a necessary part of any speculative-execution exploit.

It has thus been clear from the beginning that one way of blocking these attacks is to flush the memory caches at well-chosen times, clearing out the exfiltrated information before the attacker can get to it. That is, unfortunately, an expensive thing to do. Flushing the cache after every system call would likely block a wide range of speculative attacks, but it would also slow the system to the point that users would be looking for ways to turn the mechanism off. Security is all-important — except when you have to get some work done.

Kristen Carlson Accardi recently posted a patch that is based on an interesting observation. Attacks using speculative execution involve convincing the processor to speculate down a path that non-speculative execution will not follow. For example, a kernel function may contain a bounds check that will prevent the code from accessing beyond the end of an array, causing an error to be returned instead. An attack using the Spectre vulnerability will bypass that check speculatively, accessing data that the code was specifically (and correctly) written not to access.

In other words, the attack is doing something speculatively that, when the speculation is unwound, results in an error return to the calling program — but, by then, the damage is done. The error return is a clue that there maybe something inappropriate going on. So Accardi's patch will, in the case of certain error returns from system calls, flush the L1 processor cache before returning to user space. In particular, the core of the change looks like this:

    __visible inline void l1_cache_flush(struct pt_regs *regs)
    {
	if (IS_ENABLED(CONFIG_SYSCALL_FLUSH) &&
	    static_cpu_has(X86_FEATURE_FLUSH_L1D)) {
	    if (regs->ax == 0 || regs->ax == -EAGAIN ||
		regs->ax == -EEXIST || regs->ax == -ENOENT ||
		regs->ax == -EXDEV || regs->ax == -ETIMEDOUT ||
		regs->ax == -ENOTCONN || regs->ax == -EINPROGRESS)
			return;

	    wrmsrl(MSR_IA32_FLUSH_CMD, L1D_FLUSH);
	}
    }

The code exempts some of the most common errors from the cache-flush policy, which makes sense. Errors like EAGAIN and ENOENT are common in normal program execution but are not the sort of errors that are likely to be generated by speculative attacks; one would expect an error like EINVAL in such cases. So exempting those errors should significantly reduce the cost of this mitigation without significantly reducing the protection that it provides.

(Of course, the code as written above doesn't quite work right, as was pointed out by Thomas Gleixner, but the fix is easy and the posted patch shows the desired result.)

Alan Cox argued for this patch, saying:

The current process of trying to find them all with smatch and the like is a game of whack-a-mole that will go on for a long long time. In the meantime (and until the tools get better) it's nice to have an option that takes a totally non-hot path (the fast path change is a single test for >= 0) and provides additional defence in depth.

Andy Lutomirski is not convinced, though. He argued that there are a number of possible ways around this protection. An attacker running on a hyperthreaded sibling could attempt to get the data out of the L1 cache between the speculative exploit and the cache flush, though Cox said that the time window available would be difficult to hit. Fancier techniques, such as loading the cache lines of interest onto a different CPU and watching to see when they are "stolen" by the CPU running the attack could be attempted. Or perhaps the data of interest is still in the L2 cache and could be probed for there. In the end, he said:

Adding ~1600 cycles plus the slowdown due to the fact that the cache got flushed to a code path that we hope isn't hot to mitigate one particular means of exploiting potential bugs seems a bit dubious to me.

Answering Lutomirski's criticisms is probably necessary to get this patch set merged. Doing so would require providing some numbers for what the overhead of this change really is; Cox claimed that it is "pretty much zero" but no hard numbers have been posted. The other useful piece would be to show some current exploits that would be blocked by this change. If that information can be provided, though (and the bug in the patch fixed), flushing the L1 cache could yet prove to be a relatively cheap and effective way to block Spectre exploits that have not yet been fixed by more direct means. As a way of hardening the system overall, it seems worthy of consideration.

Index entries for this article
KernelSecurity/Meltdown and Spectre
SecurityMeltdown and Spectre


The LWN site is currently under high scraper load, so comment display has been suppressed for anonymous users. If you are a human, you may read the comments by clicking the button below:

Note: you can avoid this step in the future by logging into your LWN account.


Copyright © 2018, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds