CPU DoS Attacks
Also known as CPU starvation or CPU consumption attacks, such attacks present a difficult challenge to commodity computing platforms: users typically believe that commodity hardware is a high-assurance product and that software errors present more of a threat to reliability, quality of service, or security.
A Denial-of-Service (DoS) attack on a Central Processing Unit (CPU) represents an intentionally induced state of partially or completely degraded CPU performance in terms of the ability of the CPU to make progress on legitimate instruction streams.
Background
This type of attack represents a condition of the CPU whereby its available resources (registers, data path, arithmetic functional units, floating point units, and logic units) remain in an intentionally induced state of overload, livelock, or deadlock.
An attacker can prevent the CPU from making progress on the execution of benign processes in a number of ways, but at least two mainstream methods suffice to overload the CPU or impair its ability to multiplex between a collection of processes. The first method involves the exploitation of a hardware error in CPU design or construction to halt or loop the CPU or otherwise place it in an error state requiring a hard reset. The Intel F00F bug provides an example of this method of attack. The second method involves exploiting a software error in the operating system (example 1, example 2) or user-level software to cause the CPU to continuously service the faulting software (sometimes in spite of the kernel scheduler’s attempts to ensure fair CPU multiplexing). This style of attack is closely related to Algorithmic Complexity DoS attacks (they differ in that such attacks can also overload or impair memory performance rather than the CPU as the chief avenue of service degradation).
Applications
Attackers can cause the CPU to hang, halt, or execute malicious (or useless) code rather than legitimate, benign processes. They can do so by identifying and building on an error in the CPU hardware or by causing the kernel or one or more user-level processes to consume more than their fair share of execution time. Often, the latter attack involves identifying a software error ({\it e.g.,} to cause an unterminated loop) or supplying data to system calls specially constructed to cause uncharacteristically long system call execution time.
Low-tech versions of this latter style of attack can include manipulating the scheduler priority for one or more processes, disabling or removing resource limits (if provided by the operating system) or issuing a fork bomb or similar resource exhaustion attack.
Descriptions of hardware bugs are often available in the published CPU errata lists for each CPU model. The errata lists often describe such errors and their preconditions in enough detail to enable the reconstruction of code sequences that manifest the error state (which often, but not always, results in a CPU hang or inconsistent state rapidly leading to a hard or soft reset). Attackers can then either directly upload and run such code sequences on a target platform or construct data aimed at eliciting such instruction sequences from the execution of existing program binaries.
The aforementioned Pentium F00F bug supplies one prominent example of a hardware error leading to a hung CPU. The Pentium processor failed to correctly handle an illegal formulation of the CMPXCHG8B instruction. Specifically, if this instruction was given a non-memory operand (the implicit operand is the concatenation of the EDX and EAX registers, and the explicit operand must refer to memory)and the instruction was given the LOCK prefix, then the CPU entered a complex failure state. Normally, supplying a non-memory operand to this instruction should generate an illegal opcode exception. Unfortunately, simultaneously specifying the LOCK prefix (which is also illegal for this type of instruction) exploited a bug in the CPU: when the CPU recognized the invalid opcode due to the non-memory operand, it attempted to invoke the invalid instruction handler vector, thus causing two reads to the memory bus. The LOCK prefix, however, caused the bus to enter a state where it expects a read-write pair of bus requests rather than two memory bus reads, and the CPU subsequently hung. Intel introduced clever workarounds, including some that took advantage of the bug’s behavior, but the ease with which this hardware error could be exploited should serve as a warning that commodity computing hardware remains complex and full of significant errors.
Open Problems
Preventing DoS attacks is notoriously difficult — the point of most software and hardware computing systems, is, after all, to provide service, and exhausting available bandwidth, memory, or CPU cycles remains a major concern in the absence of redundancy or strict and well-calibrated resource limits.
Hardware errors will continue to present a troubling source of potential CPU DoS attacks. Hardware cannot be patched as easily as software, and simply executing a user-level program with the right mixture of instructions can compromise an entire machine, including software layers like the OS or a Virtual Machine Monitor that are traditionally supposed to enforce isolation or access control.
Latent software errors in the OS kernel and a wide variety of user-level applications also present opportunities for CPU exhaustion, livelock, or deadlock. With the increasing emphasis on parallel computing models and multicore systems, software errors involving improper lock ordering or bugs in threading libraries supply ample material for impairing the ability of the CPU to make progress on benign instruction streams.
Related Work
- “Intel Core 2” Theo de Raadt. The openbsd-misc mailing
list. June 27, 2007. - “The Pentium F00F Bug” Robert R. Collins.
- “Denial of service (CPU consumption) via a long argument to the
MAIL command.” The Apache Software Foundation. 15 June 2006. - “Remote Code Execution Through Intel CPU Bugs” Kris
Kaspersky. HITBSecConf2008.