6 ms·
On Linux and most Unices (at least on ix86), the heap starts at the bottom of the address space, and the upper bound grows up as you allocate more memory, whil
by brohee 9y ago
On Linux and most Unices (at least on ix86), the heap starts at the bottom of the address space, and the upper bound grows up as you allocate more memory, while the stack starts at the top of the memory space and grows downward as you allocate more stack (stack allocation : mostly function calls and the odd alloca()). malloc() who manages the heap as no idea of where the stack currently ends, and thus can allocate memory at an adress that is also claimed by the stack. When writing to this address, this permit to smash the stack without needing an actual buffer overflow in the attacked code.
There is good news, this looks way harder to attack on 64 bit systems (all the CVE are on 32 bit OS), possibly because the adress space is so huge, and standard OS counter measure like ASLR, stack gap etc all help protecting against the attack.
- jwilk 9y ago> all the CVE are on 32 bit OS Section IV.1.7 ("64-bit exploitation") mentions CVE-2017-1000379.
- clarry 9y ago> malloc() who manages the heap as no idea of where the stack currently ends, and thus can allocate memory at an adress that is also claimed by the stack The other way around. The kernel has already mapped pages for the stack, and would never hand such pages to malloc unless there's a serious bug in the vm subsystem. However, code that uses the stack has no idea where the stack ends. And reaching out of the stack is only detected via page faults. If that access happens to land on a mapped page with the right permissions, there is no fault and the code can effectively grow the stack into heap region.
- geofft 9y agoRight. The malloc heap has defined starting and ending points. (I say "points" because it's common to use mmap to get a bunch of new pages, which might be discontiguous from the existing heap, but you still know exactly where those pages are.) The stack, on the other hand, is just a pointer. In theory, you can always decrement the stack pointer for your variables. If you find an unallocated page, the kernel will notice that you're right below the stack and give you more stack pages. There's no other to request more stack memory, the way you can use brk() or mmap() to request more heap memory: you're supposed to page-fault and let the kernel come up with more stack. In slightly less theory, you can decrement the stack pointer, and if you reach more memory than the kernel is willing to give you, the page fault will turn into an actual segfault, because you'll hit a specially-defined guard page that prevents you from infinitely growing the stack. In practice, you can decrement the stack pointer by any arbitrary amount and now you just have a pointer somewhere and you have to hope it's either within the stack or in the guard page....
- makomk 9y agoYeah, it's basically inherently unsafe to allocate large buffers on the stack. grsecurity/PaX have apparently mitigated this somewhat by increasing the size of buffer required to exploit this, but there's no way for the kernel to fix the issue.
- AnimalMuppet 9y agoSlightly more specifically, if the stack is growing into space that the heap already owns, you don't get a page fault. So the OS assumes that the stack already owns that memory, since there was no page fault. Now if you write to the heap, you can corrupt the stack.
- clarry 9y ago> Slightly more specifically, if the stack is growing into space that the heap already owns, you don't get a page fault. Exactly. But the stack is only growing in the program's view of the world. > So the OS assumes that the stack already owns that memory Nah, the OS is blissfully unaware that the program has moved its stack pointer to point off the stack. If anything, the OS wrongly assumes the program is still operating within the stack space explicitly and rightly reserved for it. And the program in turn wrongly assumes this memory newly referenced via the stack pointer has been reserved for its stack, because it wasn't killed for accessing it. > Now if you write to the heap, you can corrupt the stack. That is right. You will corrupt what the program believes to be stack.
- AnimalMuppet 9y agoFrom TFA: The user-space stack of a process is automatically expanded by the kernel: - if the stack-pointer (the esp register, on i386) reaches the start of the stack and the unmapped memory pages below (the stack grows down, on i386), - then a "page-fault" exception is raised and caught by the kernel, - and the page-fault handler transparently expands the user-space stack of the process (it decreases the start address of the stack), - or it terminates the process with a SIGSEGV if the stack expansion fails (for example, if the RLIMIT_STACK is reached). Unfortunately, this stack expansion mechanism is implicit and fragile: it relies on page-fault exceptions, but if another memory region is mapped directly below the stack, then the stack-pointer can move from the stack into the other memory region without raising a page-fault, and: - the kernel cannot tell that the process needed more stack memory; - the process cannot tell that its stack-pointer moved from the stack into another memory region.