← Documents Documentation/mm/vmalloced-kernel-stacks.rst GitHub 원문 ↗

Linux 6.18.37 · Memory management

Virtually Mapped Kernel Stack Support

Guard page가 있는 vmapped kernel stack의 구성, 할당·cache와 overflow 처리를 설명합니다.

Source pathDocumentation/mm/vmalloced-kernel-stacks.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

vmalloced-kernel-stacks.rst:1-153

Vmapped kernel stack은 물리적으로 연속할 필요가 없는 page를 연속된 kernel virtual range에 mapping하고 양쪽에 guard page를 둡니다. Overflow가 조용한 memory corruption으로 번지기 전에 확실한 fault로 바뀌며, stack cache와 수동 memcg accounting으로 생성·해제 비용을 줄입니다.

Vmapped kernel stack 배치
Leading guard pageMapped stack pagesTrailing guard page
분산 physical pages`__vmalloc_node_range()`연속 kernel virtual stack

물리 page는 흩어져 있어도 virtual address에서는 stack과 guard page가 연속됩니다.

구성 option과 요구 사항
Option역할핵심 조건
`HAVE_ARCH_VMAP_STACK`Architecture 지원 선언충분한 vmalloc space·안정적 page table·overflow handler
`VMAP_STACK`Task stack을 vmapped로 할당`HAVE_ARCH_VMAP_STACK` 의존
`KASAN_VMALLOC`KASAN shadow backingKASAN과 VMAP_STACK 조합에 필요

Architecture capability, 기능 활성화와 KASAN 연동을 구분합니다.

Thread stack 할당과 재사용
`clone()`·`fork()`·`vfork()`·`kernel_thread()``kernel_clone()``alloc_thread_stack_node()``__vmalloc_node_range()``PAGE_KERNEL` mapping`task_struct.stack_vm_area`
Task 종료`free_thread_stack()`Per-CPU stack cache새 thread 재사용

Per-CPU cache hit이면 전체 vmapped stack을 같은 CPU에서 재사용합니다.

Guard-page overflow 처리
Kernel stack 증가Guard page 접근Page faultArchitecture overflow handlerLog·진단·정책 대응
x86 guard faultDouble-fault stackOverflow 처리

남은 stack이 거의 없으므로 architecture 전용 비상 stack에서 fault를 처리해야 합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 =====================================
4 Virtually Mapped Kernel Stack Support
5 =====================================
6
7 :Author: Shuah Khan <skhan@linuxfoundation.org>
8
9 .. contents:: :local:
10
11 Overview
12 --------
13
14 This is a compilation of information from the code and original patch
15 series that introduced the `Virtually Mapped Kernel Stacks feature
16 <https://lwn.net/Articles/694348/>`
17
18 Introduction
19 ------------
20
21 Kernel stack overflows are often hard to debug and make the kernel
22 susceptible to exploits. Problems could show up at a later time making
23 it difficult to isolate and root-cause.
24
25 Virtually mapped kernel stacks with guard pages cause kernel stack
26 overflows to be caught immediately rather than causing difficult to
27 diagnose corruptions.
28
29 HAVE_ARCH_VMAP_STACK and VMAP_STACK configuration options enable
30 support for virtually mapped stacks with guard pages. This feature
31 causes reliable faults when the stack overflows. The usability of
32 the stack trace after overflow and response to the overflow itself
33 is architecture dependent.
34
35 .. note::
36 As of this writing, arm64, powerpc, riscv, s390, um, and x86 have
37 support for VMAP_STACK.
38
39 HAVE_ARCH_VMAP_STACK
40 --------------------
41
42 Architectures that can support Virtually Mapped Kernel Stacks should
43 enable this bool configuration option. The requirements are:
44
45 - vmalloc space must be large enough to hold many kernel stacks. This
46 may rule out many 32-bit architectures.
47 - Stacks in vmalloc space need to work reliably. For example, if
48 vmap page tables are created on demand, either this mechanism
49 needs to work while the stack points to a virtual address with
50 unpopulated page tables or arch code (switch_to() and switch_mm(),
51 most likely) needs to ensure that the stack's page table entries
52 are populated before running on a possibly unpopulated stack.
53 - If the stack overflows into a guard page, something reasonable
54 should happen. The definition of "reasonable" is flexible, but
55 instantly rebooting without logging anything would be unfriendly.
56
57 VMAP_STACK
58 ----------
59
60 When enabled, the VMAP_STACK bool configuration option allocates virtually
61 mapped task stacks. This option depends on HAVE_ARCH_VMAP_STACK.
62
63 - Enable this if you want the use virtually-mapped kernel stacks
64 with guard pages. This causes kernel stack overflows to be caught
65 immediately rather than causing difficult-to-diagnose corruption.
66
67 .. note::
68
69 Using this feature with KASAN requires architecture support
70 for backing virtual mappings with real shadow memory, and
71 KASAN_VMALLOC must be enabled.
72
73 .. note::
74
75 VMAP_STACK is enabled, it is not possible to run DMA on stack
76 allocated data.
77
78 Kernel configuration options and dependencies keep changing. Refer to
79 the latest code base:
80
81 `Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>`
82
83 Allocation
84 -----------
85
86 When a new kernel thread is created, a thread stack is allocated from
87 virtually contiguous memory pages from the page level allocator. These
88 pages are mapped into contiguous kernel virtual space with PAGE_KERNEL
89 protections.
90
91 alloc_thread_stack_node() calls __vmalloc_node_range() to allocate stack
92 with PAGE_KERNEL protections.
93
94 - Allocated stacks are cached and later reused by new threads, so memcg
95 accounting is performed manually on assigning/releasing stacks to tasks.
96 Hence, __vmalloc_node_range is called without __GFP_ACCOUNT.
97 - vm_struct is cached to be able to find when thread free is initiated
98 in interrupt context. free_thread_stack() can be called in interrupt
99 context.
100 - On arm64, all VMAP's stacks need to have the same alignment to ensure
101 that VMAP'd stack overflow detection works correctly. Arch specific
102 vmap stack allocator takes care of this detail.
103 - This does not address interrupt stacks - according to the original patch
104
105 Thread stack allocation is initiated from clone(), fork(), vfork(),
106 kernel_thread() via kernel_clone(). These are a few hints for searching
107 the code base to understand when and how a thread stack is allocated.
108
109 Bulk of the code is in:
110 `kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`.
111
112 stack_vm_area pointer in task_struct keeps track of the virtually allocated
113 stack and a non-null stack_vm_area pointer serves as an indication that the
114 virtually mapped kernel stacks are enabled.
115
116 ::
117
118 struct vm_struct *stack_vm_area;
119
120 Stack overflow handling
121 -----------------------
122
123 Leading and trailing guard pages help detect stack overflows. When the stack
124 overflows into the guard pages, handlers have to be careful not to overflow
125 the stack again. When handlers are called, it is likely that very little
126 stack space is left.
127
128 On x86, this is done by handling the page fault indicating the kernel
129 stack overflow on the double-fault stack.
130
131 Testing VMAP allocation with guard pages
132 ----------------------------------------
133
134 How do we ensure that VMAP_STACK is actually allocating with a leading
135 and trailing guard page? The following lkdtm tests can help detect any
136 regressions.
137
138 ::
139
140 void lkdtm_STACK_GUARD_PAGE_LEADING()
141 void lkdtm_STACK_GUARD_PAGE_TRAILING()
142
143 Conclusions
144 -----------
145
146 - A percpu cache of vmalloced stacks appears to be a bit faster than a
147 high-order stack allocation, at least when the cache hits.
148 - THREAD_INFO_IN_TASK gets rid of arch-specific thread_info entirely and
149 simply embed the thread_info (containing only flags) and 'int cpu' into
150 task_struct.
151 - The thread stack can be freed as soon as the task is dead (without
152 waiting for RCU) and then, if vmapped stacks are in use, cache the
153 entire stack for reuse on the same cpu.
154

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Vmapped kernel stack 개요

1-38

가상 mapping된 kernel stack 지원

저자: Shuah Khan <skhan@linuxfoundation.org>

개요

이 문서는 `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 <skhan@linuxfoundation.org>

.. 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-142

Stack 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.