Short question
Would checking if the faulting address for every page fault points to kernel memory reliably detect an attempted Meltdown exploit, on systems that lack Intel TSX (and thus cannot suppress exceptions)? The Linux kernel do_page_fault()
function is called on every page fault, and the address of the faulting memory access is available in that function, so it should be possible to check if that address points to a range of memory specific to the kernel. For a threat model where KPTI is not an option and where trading off availability (unprivileged DoS) for confidentiality is acceptable, is this idea correct?
Long question
The recently disclosed Meltdown vulnerability in Intel processors leverages the out-of-order execution property of all modern processors to read arbitrary physical memory. This relies on being able to repeatedly attempt accesses to kernel memory, despite the fact that the access attempts will fail. These failed access attempts trigger a #PF
, or page fault, resulting in the userspace process being sent SIGSEGV
due to its attempt to access kernel memory. The Meltdown attack takes advantage of the fact that the cache is still populated with the privileged memory despite the access being denied. The next stage of the attack is to use a timing attack to retrieve the cache contents. Without TSX support however, it seems to be impossible (to the best of my knowledge) to inhibit these page faults. On such systems, Meltdown necessarily triggers a large number of page faults caused by attempts to access kernel memory. This can be detected by the kernel.
From the paper, three steps are described for performing the attack:
Meltdown combines the two building blocks discussed in Section 4. First, an attacker makes the CPU execute a transient instruction sequence which uses an inacces- sible secret value stored somewhere in physical memory (cf. Section 4.1). The transient instruction sequence acts as the transmitter of a covert channel (cf. Section 4.2), ultimately leaking the secret value to the attacker. Meltdown consists of 3 steps: Step 1 The content of an attacker-chosen memory loca- tion, which is inaccessible to the attacker, is loaded into a register. Step 2 A transient instruction accesses a cache line based on the secret content of the register. Step 3 The attacker uses Flush+Reload to determine the accessed cache line and hence the secret stored at the chosen memory location. By repeating these steps for different memory locations, the attacker can dump the kernel memory, including the entire physical memory.
After reading about a supposed mitigation utilizing the kernel tracing subsystem, it makes sense that detecting an abnormally large number of page faults due to accessing kernel memory would be able to reliably detect Meltdown. Is my thinking correct? Such a mitigation would simply check at each page fault whether or not CR2
(the register holding the faulting address) is greater than 0xffff000000000000
, which would indicate that an attempt was made to access kernel memory. The system could then induce a kernel panic, preventing the attack from continuing.
From the kernel source, the do_page_fault()
function is defined as:
dotraplinkage void notrace
do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
unsigned long address = read_cr2(); /* Get the faulting address */
enum ctx_state prev_state;
prev_state = exception_enter();
if (trace_pagefault_enabled())
trace_page_fault_entries(address, regs, error_code);
__do_page_fault(regs, error_code, address);
exception_exit(prev_state);
}
NOKPROBE_SYMBOL(do_page_fault);
This function is called every time there is a page fault, with the faulting address being saved to address
. Without TSX support, to the best of my knowledge, it is impossible for the attack to avoid entering this function. It seems to me like adding a simple check to see if the address is in a dangerous range, so something along the lines of BUG_ON(address > 0xffff000000000000)
would reliably detect 100% of Meltdown attacks under these constraints. This could be made more reliable (less likely to trigger false positives) by only triggering a panic when a sufficient number of violations occur and to ignore a violation if address
is less than mmap_min_addr
(which would imply a benign NULL pointer dereference), but that would only be necessary if a given workload triggers false positives.
This mitigation would not work if any of the following are true:
- Spectre, which causes no page faults, can perform Meltdown without abusing eBPF.
- There is another way to use Meltdown without triggering a page fault, on systems without TSX.
- The
do_page_fault()
function can be avoided when a page fault occurs.
When re-reading the paper, it mentions exception suppression using other methods than TSX, but it is not entirely clear to me. It makes it sound as if a page fault could be avoided like this:
if (condition_mispredicted_as_false)
access_kernel_memory();
If this is true, then this detection mechanism (and the one by Capsule8) would not work.
Would this mitigation work on systems without TSX, assuming natural false positives are not an issue and ignoring the fact that it would open up unprivileged DoS bugs?