요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=====================================
Virtually Mapped Kernel Stack Support
=====================================
:Author: Shuah Khan <[email protected]>
.. contents:: :local:
Overview
--------
This is a compilation of information from the code and original patch
series that introduced the `Virtually Mapped Kernel Stacks feature
<https://lwn.net/Articles/694348/>`
Introduction
------------
Kernel stack overflows are often hard to debug and make the kernel
susceptible to exploits. Problems could show up at a later time making
it difficult to isolate and root-cause.
Virtually mapped kernel stacks with guard pages cause kernel stack
overflows to be caught immediately rather than causing difficult to
diagnose corruptions.
HAVE_ARCH_VMAP_STACK and VMAP_STACK configuration options enable
support for virtually mapped stacks with guard pages. This feature
causes reliable faults when the stack overflows. The usability of
the stack trace after overflow and response to the overflow itself
is architecture dependent.
.. note::
As of this writing, arm64, powerpc, riscv, s390, um, and x86 have
support for VMAP_STACK.
HAVE_ARCH_VMAP_STACK
--------------------
Architectures that can support Virtually Mapped Kernel Stacks should
enable this bool configuration option. The requirements are:
- vmalloc space must be large enough to hold many kernel stacks. This
may rule out many 32-bit architectures.
- Stacks in vmalloc space need to work reliably. For example, if
vmap page tables are created on demand, either this mechanism
needs to work while the stack points to a virtual address with
unpopulated page tables or arch code (switch_to() and switch_mm(),
most likely) needs to ensure that the stack's page table entries
are populated before running on a possibly unpopulated stack.
- If the stack overflows into a guard page, something reasonable
should happen. The definition of "reasonable" is flexible, but
instantly rebooting without logging anything would be unfriendly.
VMAP_STACK
----------
When enabled, the VMAP_STACK bool configuration option allocates virtually
mapped task stacks. This option depends on HAVE_ARCH_VMAP_STACK.
- Enable this if you want the use virtually-mapped kernel stacks
with guard pages. This causes kernel stack overflows to be caught
immediately rather than causing difficult-to-diagnose corruption.
.. note::
Using this feature with KASAN requires architecture support
for backing virtual mappings with real shadow memory, and
KASAN_VMALLOC must be enabled.
.. note::
VMAP_STACK is enabled, it is not possible to run DMA on stack
allocated data.
Kernel configuration options and dependencies keep changing. Refer to
the latest code base:
`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
Allocation
-----------
When a new kernel thread is created, a thread stack is allocated from
virtually contiguous memory pages from the page level allocator. These
pages are mapped into contiguous kernel virtual space with PAGE_KERNEL
protections.
alloc_thread_stack_node() calls __vmalloc_node_range() to allocate stack
with PAGE_KERNEL protections.
- Allocated stacks are cached and later reused by new threads, so memcg
accounting is performed manually on assigning/releasing stacks to tasks.
Hence, __vmalloc_node_range is called without __GFP_ACCOUNT.
- vm_struct is cached to be able to find when thread free is initiated
in interrupt context. free_thread_stack() can be called in interrupt
context.
- On arm64, all VMAP's stacks need to have the same alignment to ensure
that VMAP'd stack overflow detection works correctly. Arch specific
vmap stack allocator takes care of this detail.
- This does not address interrupt stacks - according to the original patch
Thread stack allocation is initiated from clone(), fork(), vfork(),
kernel_thread() via kernel_clone(). These are a few hints for searching
the code base to understand when and how a thread stack is allocated.
Bulk of the code is in:
`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
stack_vm_area pointer in task_struct keeps track of the virtually allocated
stack and a non-null stack_vm_area pointer serves as an indication that the
virtually mapped kernel stacks are enabled.
::
struct vm_struct *stack_vm_area;
Stack overflow handling
-----------------------
Leading and trailing guard pages help detect stack overflows. When the stack
overflows into the guard pages, handlers have to be careful not to overflow
the stack again. When handlers are called, it is likely that very little
stack space is left.
On x86, this is done by handling the page fault indicating the kernel
stack overflow on the double-fault stack.
Testing VMAP allocation with guard pages
----------------------------------------
How do we ensure that VMAP_STACK is actually allocating with a leading
and trailing guard page? The following lkdtm tests can help detect any
regressions.
::
void lkdtm_STACK_GUARD_PAGE_LEADING()
void lkdtm_STACK_GUARD_PAGE_TRAILING()
Conclusions
-----------
- A percpu cache of vmalloced stacks appears to be a bit faster than a
high-order stack allocation, at least when the cache hits.
- THREAD_INFO_IN_TASK gets rid of arch-specific thread_info entirely and
simply embed the thread_info (containing only flags) and 'int cpu' into
task_struct.
- The thread stack can be freed as soon as the task is dead (without
waiting for RCU) and then, if vmapped stacks are in use, cache the
entire stack for reuse on the same cpu.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Vmapped kernel stack 개요
1-38가상 mapping된 kernel stack 지원
저자: Shuah Khan <[email protected]>
개요
이 문서는 `Virtually Mapped Kernel Stacks` 기능을 도입한 원래 patch series와 code의 정보를 한데 모은 것입니다.
소개
Kernel stack overflow는 debug하기 어렵고 kernel을 exploit에 취약하게 만들 수 있습니다. 손상이 나중에 드러날 수 있어 원인 구간을 분리하고 root cause를 찾기도 어렵습니다.
Guard page를 둔 virtually mapped kernel stack은 진단하기 어려운 corruption을 만들기 전에 overflow 순간에 즉시 fault를 일으킵니다.
`HAVE_ARCH_VMAP_STACK`과 `VMAP_STACK` config option이 guard page를 포함한 virtually mapped stack 지원을 켭니다. Stack trace가 overflow 뒤에도 얼마나 쓸 만한지와 overflow 자체에 대한 대응은 architecture마다 다릅니다.
이 문서 작성 시점에 arm64, powerpc, riscv, s390, um, x86이 `VMAP_STACK`을 지원합니다.
.. SPDX-License-Identifier: GPL-2.0
=====================================
Virtually Mapped Kernel Stack Support
=====================================
:Author: Shuah Khan <[email protected]>
.. contents:: :local:
Overview
--------
This is a compilation of information from the code and original patch
series that introduced the `Virtually Mapped Kernel Stacks feature
<https://lwn.net/Articles/694348/>`
Introduction
------------
Kernel stack overflows are often hard to debug and make the kernel
susceptible to exploits. Problems could show up at a later time making
it difficult to isolate and root-cause.
Virtually mapped kernel stacks with guard pages cause kernel stack
overflows to be caught immediately rather than causing difficult to
diagnose corruptions.
HAVE_ARCH_VMAP_STACK and VMAP_STACK configuration options enable
support for virtually mapped stacks with guard pages. This feature
causes reliable faults when the stack overflows. The usability of
the stack trace after overflow and response to the overflow itself
is architecture dependent.
.. note::
As of this writing, arm64, powerpc, riscv, s390, um, and x86 have
support for VMAP_STACK.
HAVE_ARCH_VMAP_STACK 요구 사항
39-56`HAVE_ARCH_VMAP_STACK`
Virtually mapped kernel stack을 지원할 수 있는 architecture는 이 bool config option을 활성화해야 합니다. 요구 사항은 다음과 같습니다.
- Vmalloc space가 많은 kernel stack을 담을 만큼 커야 합니다. 이 조건 때문에 많은 32비트 architecture가 제외될 수 있습니다.
- Vmalloc space의 stack이 안정적으로 동작해야 합니다. Vmap page table을 on-demand로 만든다면 현재 stack이 아직 채워지지 않은 page table의 virtual address를 가리킬 때도 생성 mechanism이 작동해야 합니다. 그렇지 않으면 `switch_to()`와 `switch_mm()` 같은 arch code가 stack에서 실행하기 전에 page-table entry를 채워야 합니다.
- Stack이 guard page까지 넘치면 합리적인 처리가 이루어져야 합니다. 합리적이라는 정의는 유연하지만 아무 log 없이 즉시 reboot하는 것은 사용자 친화적이지 않습니다.
HAVE_ARCH_VMAP_STACK
--------------------
Architectures that can support Virtually Mapped Kernel Stacks should
enable this bool configuration option. The requirements are:
- vmalloc space must be large enough to hold many kernel stacks. This
may rule out many 32-bit architectures.
- Stacks in vmalloc space need to work reliably. For example, if
vmap page tables are created on demand, either this mechanism
needs to work while the stack points to a virtual address with
unpopulated page tables or arch code (switch_to() and switch_mm(),
most likely) needs to ensure that the stack's page table entries
are populated before running on a possibly unpopulated stack.
- If the stack overflows into a guard page, something reasonable
should happen. The definition of "reasonable" is flexible, but
instantly rebooting without logging anything would be unfriendly.
VMAP_STACK 구성과 제약
57-82`VMAP_STACK`
이 bool config option을 활성화하면 task stack을 가상 mapping 방식으로 할당합니다. `HAVE_ARCH_VMAP_STACK`에 의존합니다.
Guard page를 둔 virtually mapped kernel stack을 사용해 stack overflow를 즉시 잡고 진단하기 어려운 corruption을 피하려면 이 option을 켭니다.
KASAN과 함께 사용하려면 architecture가 virtual mapping을 실제 shadow memory로 backing할 수 있어야 하며 `KASAN_VMALLOC`도 활성화해야 합니다.
`VMAP_STACK`을 사용하면 stack에 할당된 data를 DMA 대상으로 사용할 수 없습니다.
Kernel config option과 dependency는 계속 바뀌므로 최신 `arch/Kconfig` code를 확인하십시오.
VMAP_STACK
----------
When enabled, the VMAP_STACK bool configuration option allocates virtually
mapped task stacks. This option depends on HAVE_ARCH_VMAP_STACK.
- Enable this if you want the use virtually-mapped kernel stacks
with guard pages. This causes kernel stack overflows to be caught
immediately rather than causing difficult-to-diagnose corruption.
.. note::
Using this feature with KASAN requires architecture support
for backing virtual mappings with real shadow memory, and
KASAN_VMALLOC must be enabled.
.. note::
VMAP_STACK is enabled, it is not possible to run DMA on stack
allocated data.
Kernel configuration options and dependencies keep changing. Refer to
the latest code base:
`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
할당·cache·memcg accounting
83-119할당
새 kernel thread를 만들 때 page-level allocator에서 얻은 물리적으로 떨어져 있을 수 있는 memory page를 stack으로 할당하고, 이를 `PAGE_KERNEL` protection을 사용해 연속된 kernel virtual space에 mapping합니다.
`alloc_thread_stack_node()`는 `__vmalloc_node_range()`를 호출해 `PAGE_KERNEL` protection의 stack을 할당합니다.
- 할당한 stack은 cache했다가 새 thread가 재사용합니다. Task에 stack을 배정하거나 회수할 때 memcg accounting을 수동으로 처리하므로 `__vmalloc_node_range()`는 `__GFP_ACCOUNT` 없이 호출합니다.
- `free_thread_stack()`이 interrupt context에서 호출될 수 있으므로 thread free가 시작될 때 stack을 찾을 수 있게 `vm_struct`도 cache합니다.
- Arm64에서는 VMAP stack overflow 감지가 올바르게 동작하도록 모든 VMAP stack의 alignment가 같아야 합니다. Architecture 전용 vmap-stack allocator가 이를 처리합니다.
- 원래 patch 기준으로 interrupt stack은 이 기능의 대상이 아닙니다.
Thread stack 할당은 `clone()`, `fork()`, `vfork()`, `kernel_thread()`에서 `kernel_clone()`을 거쳐 시작됩니다. 주요 code는 `kernel/fork.c`에 있습니다.
`task_struct`의 `stack_vm_area` pointer가 가상 할당된 stack을 추적합니다. 이 pointer가 non-NULL이면 virtually mapped kernel stack이 활성화된 것으로 볼 수 있습니다.
struct vm_struct *stack_vm_area;
Allocation
-----------
When a new kernel thread is created, a thread stack is allocated from
virtually contiguous memory pages from the page level allocator. These
pages are mapped into contiguous kernel virtual space with PAGE_KERNEL
protections.
alloc_thread_stack_node() calls __vmalloc_node_range() to allocate stack
with PAGE_KERNEL protections.
- Allocated stacks are cached and later reused by new threads, so memcg
accounting is performed manually on assigning/releasing stacks to tasks.
Hence, __vmalloc_node_range is called without __GFP_ACCOUNT.
- vm_struct is cached to be able to find when thread free is initiated
in interrupt context. free_thread_stack() can be called in interrupt
context.
- On arm64, all VMAP's stacks need to have the same alignment to ensure
that VMAP'd stack overflow detection works correctly. Arch specific
vmap stack allocator takes care of this detail.
- This does not address interrupt stacks - according to the original patch
Thread stack allocation is initiated from clone(), fork(), vfork(),
kernel_thread() via kernel_clone(). These are a few hints for searching
the code base to understand when and how a thread stack is allocated.
Bulk of the code is in:
`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
stack_vm_area pointer in task_struct keeps track of the virtually allocated
stack and a non-null stack_vm_area pointer serves as an indication that the
virtually mapped kernel stacks are enabled.
::
struct vm_struct *stack_vm_area;
Overflow 처리와 guard-page 시험
120-142Stack overflow 처리
Stack 앞뒤의 guard page가 overflow를 감지합니다. Overflow가 guard page에 닿았을 때 handler가 다시 stack을 넘치게 하지 않도록 조심해야 합니다. Handler가 호출될 때는 stack 공간이 거의 남지 않았을 가능성이 큽니다.
x86에서는 kernel stack overflow를 나타내는 page fault를 double-fault stack에서 처리합니다.
Guard page를 포함한 VMAP 할당 시험
`VMAP_STACK`이 실제로 앞뒤 guard page를 포함해 할당하는지는 다음 lkdtm test로 regression을 검사할 수 있습니다.
void lkdtm_STACK_GUARD_PAGE_LEADING()
void lkdtm_STACK_GUARD_PAGE_TRAILING()
Stack overflow handling
-----------------------
Leading and trailing guard pages help detect stack overflows. When the stack
overflows into the guard pages, handlers have to be careful not to overflow
the stack again. When handlers are called, it is likely that very little
stack space is left.
On x86, this is done by handling the page fault indicating the kernel
stack overflow on the double-fault stack.
Testing VMAP allocation with guard pages
----------------------------------------
How do we ensure that VMAP_STACK is actually allocating with a leading
and trailing guard page? The following lkdtm tests can help detect any
regressions.
::
void lkdtm_STACK_GUARD_PAGE_LEADING()
void lkdtm_STACK_GUARD_PAGE_TRAILING()
구현상 결론
143-153결론
- Cache hit일 때 vmalloc stack의 percpu cache는 high-order stack allocation보다 조금 빠른 것으로 보입니다.
- `THREAD_INFO_IN_TASK`는 architecture별 `thread_info`를 없애고 flag만 담는 `thread_info`와 `int cpu`를 `task_struct`에 직접 포함합니다.
- Task가 죽으면 RCU를 기다리지 않고 thread stack을 바로 free할 수 있습니다. Vmapped stack을 사용한다면 stack 전체를 같은 CPU에서 재사용하도록 cache합니다.
Conclusions
-----------
- A percpu cache of vmalloced stacks appears to be a bit faster than a
high-order stack allocation, at least when the cache hits.
- THREAD_INFO_IN_TASK gets rid of arch-specific thread_info entirely and
simply embed the thread_info (containing only flags) and 'int cpu' into
task_struct.
- The thread stack can be freed as soon as the task is dead (without
waiting for RCU) and then, if vmapped stacks are in use, cache the
entire stack for reuse on the same cpu.
요약·해설
vmalloced-kernel-stacks.rst:1-153Vmapped kernel stack은 물리적으로 연속할 필요가 없는 page를 연속된 kernel virtual range에 mapping하고 양쪽에 guard page를 둡니다. Overflow가 조용한 memory corruption으로 번지기 전에 확실한 fault로 바뀌며, stack cache와 수동 memcg accounting으로 생성·해제 비용을 줄입니다.
물리 page는 흩어져 있어도 virtual address에서는 stack과 guard page가 연속됩니다.
Architecture capability, 기능 활성화와 KASAN 연동을 구분합니다.
Per-CPU cache hit이면 전체 vmapped stack을 같은 CPU에서 재사용합니다.
남은 stack이 거의 없으므로 architecture 전용 비상 stack에서 fault를 처리해야 합니다.