# Stack unwinding risks on 64-bit Windows

DevFeed: [Stack unwinding risks on 64-bit Windows](<https://devfeed.tech/articles/stack-unwinding-risks-on-64-bit-windows-21562.md>)

Original publisher: [Read original article](<http://lackingrhoticity.blogspot.com/2011/11/stack-unwinding-risks-on-64-bit-windows.html>)

Author: Mark Seaborn (noreply@blogger.com)

Published: 2011-11-19T20:24:00Z

Content type: article

Language: en

Sources: [Mark Seaborn](<https://devfeed.tech/sources/mark-seaborn.md>)

Topics: [x86](<https://devfeed.tech/topics/x86.md>), [Assembly](<https://devfeed.tech/topics/assembly.md>), [Exception](<https://devfeed.tech/topics/exception.md>), [Windows](<https://devfeed.tech/topics/windows.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [assembly](<https://devfeed.tech/tags/assembly.md>), [code](<https://devfeed.tech/tags/code.md>), [exception](<https://devfeed.tech/tags/exception.md>), [layout](<https://devfeed.tech/tags/layout.md>), [processes](<https://devfeed.tech/tags/processes.md>), [programming](<https://devfeed.tech/tags/programming.md>), [state](<https://devfeed.tech/tags/state.md>), [windows](<https://devfeed.tech/tags/windows.md>), [x86](<https://devfeed.tech/tags/x86.md>)

## AI overview

The article examines a fallback rule in x86-64 Windows stack unwinding when a return address lacks unwind information. It argues that repeatedly applying the rule can make the unwinder interpret invalid stack layouts as valid frames, potentially increasing exploitation risks in programs with corrupted stacks or assembly code without unwind information.

## Source excerpt

Recently, I've been looking at how x86-64 Windows does stack unwinding in 64-bit processes, and I've found some odd behaviour. If the stack unwinder finds a return address on the stack that does not have associated unwind info, it applies a fallback unwind rule that does not make much sense. I've been wondering if this could make some x86-64 programs more easily exploitable if they corrupt the stack or if they use x86-64 assembly code that does not have unwind info. In pseudocode, the current unwind logic looks something like this: void unwind_stack(struct register_state regs) { while (???) { unwind_info = get_unwind_info(regs.rip); if (unwind_info) { if (has_exception_handler(unwind_info) { // Run exception handler... } regs = unwind_stack_frame(unwind_info, regs); } else { // Fallback case for leaf functions: regs.rip = *(uint64_t *) regs.rsp; regs.rsp += 8; } } } The issue is that the fallback case only makes sense for the first iteration of the loop. The fact that it is applied to later iterations is probably just sloppy programming. The fallback case is intended to handle "leaf functions". This means functions that: do not adjust %rsp, and do not call other functions. These two properties are related: if a function calls other functions, it must adjust %rsp first, otherwise it does not conform to the x86-64 ABI. Since the fallback case is applied repeatedly, the unwinder will happily interpret the stack as a series of return addresses with no gaps between them: ... 8 bytes return address 8 bytes return address 8 bytes return address ... However, those are not valid stack frames in the x86-64 Windows ABI. A valid stack frame for a non-leaf function Foo() (i.e. a function that calls other functions) looks like this: ------------ 16-byte aligned 32 bytes "shadow space" (scratch space for Foo()) 8 bytes return address (points into Foo()'s caller) 8 bytes scratch space for Foo() 16*n bytes scratch space for Foo() (for some n >= 0) 32 bytes "shadow space" (scratch sp