The Linux Virtual Memory System: Security And Buffer Overflows
Learn how Linux protects itself using different defense mechanisms against buffer overflow attacks.
We'll cover the following
Probably the biggest difference between modern VM systems (Linux, Solaris, or one of the BSD variants) and ancient ones (VAX/VMS) is the emphasis on security in the modern era. Protection has always been a serious concern for operating systems, but with machines more interconnected than ever, it is no surprise that developers have implemented a variety of defensive countermeasures to halt those wily hackers from gaining control of systems.
Buffer overflow attacks
One major threat is found in
int some_function(char *input) {char dest_buffer[100];strcpy(dest_buffer, input); // oops, unbounded copy!}
In many cases, such an overflow is not catastrophic, e.g., bad input innocently given to a user program or even the OS will probably cause it to crash, but no worse. However, malicious programmers can carefully craft the input that overflows the buffer so as to inject their own code into the targeted system, essentially allowing them to take it over and do their own bidding. If successful upon a network-connected user program, attackers can run arbitrary computations or even rent out cycles on the compromised system; if successful upon the operating system itself, the attack can access even more resources, and is a form of what is called privilege escalation (i.e., user code gaining kernel access rights). If you can’t guess, these are all Bad Things.
NX bit
The first and most simple defense against buffer overflow is to prevent execution of any code found within certain regions of an address space (e.g., within the stack). The NX bit (for No-eXecute), introduced by AMD into their version of x86 (a similar XD bit is now available on Intel’s), is one such defense; it just prevents execution from any page which has this bit set in its corresponding page table entry. The approach prevents code, injected by an attacker into the target’s stack, from being executed, and thus mitigates the problem.
Return-oriented programming
However, clever attackers are … clever, and even when injected code cannot be added explicitly by the attacker, arbitrary code sequences can be executed by malicious code. The idea is known, in its most general form, as a
Address space layout randomization (ASLR)
To defend against ROP (
Interestingly, you can observe this randomness in practice rather easily. Here’s a piece of code that demonstrates it on a modern Linux system:
int main(int argc, char *argv[]) {int stack = 0;printf("%p\n", &stack);return 0;}
This code just prints out the (virtual) address of a variable on the stack. In older non-ASLR systems, this value would be the same each time. But, as you can see below, the value changes with each run:
prompt> ./random0x7ffd3e55d2b4prompt> ./random0x7ffe1033b8f4prompt> ./random0x7ffe45522e94
ASLR is such a useful defense for user-level programs that it has also been incorporated into the kernel, in a feature unimaginatively called kernel address space layout randomization (KASLR). However, it turns out the kernel may have even bigger problems to handle, as we discuss next.
Get hands-on with 1400+ tech skills courses.