← Documents Documentation/livepatch/reliable-stacktrace.rst GitHub 원문 ↗

Linux 6.18.37 · Livepatch

Reliable Stacktrace

라이브패치 안전 판정을 위한 누락 없는 stacktrace의 구현 계약과 architecture별 검증 조건입니다.

Source pathDocumentation/livepatch/reliable-stacktrace.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약·해설

reliable-stacktrace.rst:1-309

Reliable stacktrace는 복귀 가능한 모든 live function을 포함해 0을 반환하거나, 완전성을 보장할 수 없으면 non-zero 오류를 반환해야 합니다.

성공 종료점, unwind 가능한 code 범위, exception 경계, ftrace·kretprobe trampoline의 return address 이동을 검증해야 하며 손상된 상태에서 무한 loop나 잘못된 memory 접근을 일으켜서는 안 됩니다.

Link register architecture에서는 LR과 stack이 같은 주소를 중복 표현하거나 LR이 임시로 다른 대상을 가리킬 수 있으므로, `objtool` metadata 등으로 올바른 위치를 확정하지 못하면 unwind를 거부해야 합니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 ===================
2 Reliable Stacktrace
3 ===================
4
5 This document outlines basic information about reliable stacktracing.
6
7 .. Table of Contents:
8
9 .. contents:: :local:
10
11 1. Introduction
12 ===============
13
14 The kernel livepatch consistency model relies on accurately identifying which
15 functions may have live state and therefore may not be safe to patch. One way
16 to identify which functions are live is to use a stacktrace.
17
18 Existing stacktrace code may not always give an accurate picture of all
19 functions with live state, and best-effort approaches which can be helpful for
20 debugging are unsound for livepatching. Livepatching depends on architectures
21 to provide a *reliable* stacktrace which ensures it never omits any live
22 functions from a trace.
23
24
25 2. Requirements
26 ===============
27
28 Architectures must implement one of the reliable stacktrace functions.
29 Architectures using CONFIG_ARCH_STACKWALK must implement
30 'arch_stack_walk_reliable', and other architectures must implement
31 'save_stack_trace_tsk_reliable'.
32
33 Principally, the reliable stacktrace function must ensure that either:
34
35 * The trace includes all functions that the task may be returned to, and the
36 return code is zero to indicate that the trace is reliable.
37
38 * The return code is non-zero to indicate that the trace is not reliable.
39
40 .. note::
41 In some cases it is legitimate to omit specific functions from the trace,
42 but all other functions must be reported. These cases are described in
43 further detail below.
44
45 Secondly, the reliable stacktrace function must be robust to cases where
46 the stack or other unwind state is corrupt or otherwise unreliable. The
47 function should attempt to detect such cases and return a non-zero error
48 code, and should not get stuck in an infinite loop or access memory in
49 an unsafe way. Specific cases are described in further detail below.
50
51
52 3. Compile-time analysis
53 ========================
54
55 To ensure that kernel code can be correctly unwound in all cases,
56 architectures may need to verify that code has been compiled in a manner
57 expected by the unwinder. For example, an unwinder may expect that
58 functions manipulate the stack pointer in a limited way, or that all
59 functions use specific prologue and epilogue sequences. Architectures
60 with such requirements should verify the kernel compilation using
61 objtool.
62
63 In some cases, an unwinder may require metadata to correctly unwind.
64 Where necessary, this metadata should be generated at build time using
65 objtool.
66
67
68 4. Considerations
69 =================
70
71 The unwinding process varies across architectures, their respective procedure
72 call standards, and kernel configurations. This section describes common
73 details that architectures should consider.
74
75 4.1 Identifying successful termination
76 --------------------------------------
77
78 Unwinding may terminate early for a number of reasons, including:
79
80 * Stack or frame pointer corruption.
81
82 * Missing unwind support for an uncommon scenario, or a bug in the unwinder.
83
84 * Dynamically generated code (e.g. eBPF) or foreign code (e.g. EFI runtime
85 services) not following the conventions expected by the unwinder.
86
87 To ensure that this does not result in functions being omitted from the trace,
88 even if not caught by other checks, it is strongly recommended that
89 architectures verify that a stacktrace ends at an expected location, e.g.
90
91 * Within a specific function that is an entry point to the kernel.
92
93 * At a specific location on a stack expected for a kernel entry point.
94
95 * On a specific stack expected for a kernel entry point (e.g. if the
96 architecture has separate task and IRQ stacks).
97
98 4.2 Identifying unwindable code
99 -------------------------------
100
101 Unwinding typically relies on code following specific conventions (e.g.
102 manipulating a frame pointer), but there can be code which may not follow these
103 conventions and may require special handling in the unwinder, e.g.
104
105 * Exception vectors and entry assembly.
106
107 * Procedure Linkage Table (PLT) entries and veneer functions.
108
109 * Trampoline assembly (e.g. ftrace, kprobes).
110
111 * Dynamically generated code (e.g. eBPF, optprobe trampolines).
112
113 * Foreign code (e.g. EFI runtime services).
114
115 To ensure that such cases do not result in functions being omitted from a
116 trace, it is strongly recommended that architectures positively identify code
117 which is known to be reliable to unwind from, and reject unwinding from all
118 other code.
119
120 Kernel code including modules and eBPF can be distinguished from foreign code
121 using '__kernel_text_address()'. Checking for this also helps to detect stack
122 corruption.
123
124 There are several ways an architecture may identify kernel code which is deemed
125 unreliable to unwind from, e.g.
126
127 * Placing such code into special linker sections, and rejecting unwinding from
128 any code in these sections.
129
130 * Identifying specific portions of code using bounds information.
131
132 4.3 Unwinding across interrupts and exceptions
133 ----------------------------------------------
134
135 At function call boundaries the stack and other unwind state is expected to be
136 in a consistent state suitable for reliable unwinding, but this may not be the
137 case part-way through a function. For example, during a function prologue or
138 epilogue a frame pointer may be transiently invalid, or during the function
139 body the return address may be held in an arbitrary general purpose register.
140 For some architectures this may change at runtime as a result of dynamic
141 instrumentation.
142
143 If an interrupt or other exception is taken while the stack or other unwind
144 state is in an inconsistent state, it may not be possible to reliably unwind,
145 and it may not be possible to identify whether such unwinding will be reliable.
146 See below for examples.
147
148 Architectures which cannot identify when it is reliable to unwind such cases
149 (or where it is never reliable) must reject unwinding across exception
150 boundaries. Note that it may be reliable to unwind across certain
151 exceptions (e.g. IRQ) but unreliable to unwind across other exceptions
152 (e.g. NMI).
153
154 Architectures which can identify when it is reliable to unwind such cases (or
155 have no such cases) should attempt to unwind across exception boundaries, as
156 doing so can prevent unnecessarily stalling livepatch consistency checks and
157 permits livepatch transitions to complete more quickly.
158
159 4.4 Rewriting of return addresses
160 ---------------------------------
161
162 Some trampolines temporarily modify the return address of a function in order
163 to intercept when that function returns with a return trampoline, e.g.
164
165 * An ftrace trampoline may modify the return address so that function graph
166 tracing can intercept returns.
167
168 * A kprobes (or optprobes) trampoline may modify the return address so that
169 kretprobes can intercept returns.
170
171 When this happens, the original return address will not be in its usual
172 location. For trampolines which are not subject to live patching, where an
173 unwinder can reliably determine the original return address and no unwind state
174 is altered by the trampoline, the unwinder may report the original return
175 address in place of the trampoline and report this as reliable. Otherwise, an
176 unwinder must report these cases as unreliable.
177
178 Special care is required when identifying the original return address, as this
179 information is not in a consistent location for the duration of the entry
180 trampoline or return trampoline. For example, considering the x86_64
181 'return_to_handler' return trampoline:
182
183 .. code-block:: none
184
185 SYM_CODE_START(return_to_handler)
186 UNWIND_HINT_UNDEFINED
187 subq $24, %rsp
188
189 /* Save the return values */
190 movq %rax, (%rsp)
191 movq %rdx, 8(%rsp)
192 movq %rbp, %rdi
193
194 call ftrace_return_to_handler
195
196 movq %rax, %rdi
197 movq 8(%rsp), %rdx
198 movq (%rsp), %rax
199 addq $24, %rsp
200 JMP_NOSPEC rdi
201 SYM_CODE_END(return_to_handler)
202
203 While the traced function runs its return address on the stack points to
204 the start of return_to_handler, and the original return address is stored in
205 the task's cur_ret_stack. During this time the unwinder can find the return
206 address using ftrace_graph_ret_addr().
207
208 When the traced function returns to return_to_handler, there is no longer a
209 return address on the stack, though the original return address is still stored
210 in the task's cur_ret_stack. Within ftrace_return_to_handler(), the original
211 return address is removed from cur_ret_stack and is transiently moved
212 arbitrarily by the compiler before being returned in rax. The return_to_handler
213 trampoline moves this into rdi before jumping to it.
214
215 Architectures might not always be able to unwind such sequences, such as when
216 ftrace_return_to_handler() has removed the address from cur_ret_stack, and the
217 location of the return address cannot be reliably determined.
218
219 It is recommended that architectures unwind cases where return_to_handler has
220 not yet been returned to, but architectures are not required to unwind from the
221 middle of return_to_handler and can report this as unreliable. Architectures
222 are not required to unwind from other trampolines which modify the return
223 address.
224
225 4.5 Obscuring of return addresses
226 ---------------------------------
227
228 Some trampolines do not rewrite the return address in order to intercept
229 returns, but do transiently clobber the return address or other unwind state.
230
231 For example, the x86_64 implementation of optprobes patches the probed function
232 with a JMP instruction which targets the associated optprobe trampoline. When
233 the probe is hit, the CPU will branch to the optprobe trampoline, and the
234 address of the probed function is not held in any register or on the stack.
235
236 Similarly, the arm64 implementation of DYNAMIC_FTRACE_WITH_REGS patches traced
237 functions with the following:
238
239 .. code-block:: none
240
241 MOV X9, X30
242 BL <trampoline>
243
244 The MOV saves the link register (X30) into X9 to preserve the return address
245 before the BL clobbers the link register and branches to the trampoline. At the
246 start of the trampoline, the address of the traced function is in X9 rather
247 than the link register as would usually be the case.
248
249 Architectures must either ensure that unwinders either reliably unwind
250 such cases, or report the unwinding as unreliable.
251
252 4.6 Link register unreliability
253 -------------------------------
254
255 On some other architectures, 'call' instructions place the return address into a
256 link register, and 'return' instructions consume the return address from the
257 link register without modifying the register. On these architectures software
258 must save the return address to the stack prior to making a function call. Over
259 the duration of a function call, the return address may be held in the link
260 register alone, on the stack alone, or in both locations.
261
262 Unwinders typically assume the link register is always live, but this
263 assumption can lead to unreliable stack traces. For example, consider the
264 following arm64 assembly for a simple function:
265
266 .. code-block:: none
267
268 function:
269 STP X29, X30, [SP, -16]!
270 MOV X29, SP
271 BL <other_function>
272 LDP X29, X30, [SP], #16
273 RET
274
275 At entry to the function, the link register (x30) points to the caller, and the
276 frame pointer (X29) points to the caller's frame including the caller's return
277 address. The first two instructions create a new stackframe and update the
278 frame pointer, and at this point the link register and the frame pointer both
279 describe this function's return address. A trace at this point may describe
280 this function twice, and if the function return is being traced, the unwinder
281 may consume two entries from the fgraph return stack rather than one entry.
282
283 The BL invokes 'other_function' with the link register pointing to this
284 function's LDR and the frame pointer pointing to this function's stackframe.
285 When 'other_function' returns, the link register is left pointing at the BL,
286 and so a trace at this point could result in 'function' appearing twice in the
287 backtrace.
288
289 Similarly, a function may deliberately clobber the LR, e.g.
290
291 .. code-block:: none
292
293 caller:
294 STP X29, X30, [SP, -16]!
295 MOV X29, SP
296 ADR LR, <callee>
297 BLR LR
298 LDP X29, X30, [SP], #16
299 RET
300
301 The ADR places the address of 'callee' into the LR, before the BLR branches to
302 this address. If a trace is made immediately after the ADR, 'callee' will
303 appear to be the parent of 'caller', rather than the child.
304
305 Due to cases such as the above, it may only be possible to reliably consume a
306 link register value at a function call boundary. Architectures where this is
307 the case must reject unwinding across exception boundaries unless they can
308 reliably identify when the LR or stack value should be used (e.g. using
309 metadata generated by objtool).
310

3. 한국어 전문 번역

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

도입: 라이브패치에 필요한 신뢰성

1-24

이 문서는 신뢰 가능한 stacktrace의 기본 요구사항을 설명합니다. 라이브패치 consistency model은 현재 실행 상태를 가진 함수, 즉 그 순간 교체하면 안전하지 않을 수 있는 함수를 정확히 식별해야 합니다. Stacktrace는 그런 live function을 찾는 한 가지 방법입니다.

기존 stacktrace 구현은 live state를 가진 모든 함수를 항상 정확하게 보여 주지는 않습니다. 디버깅에는 유용한 best-effort trace도 누락을 허용하므로 라이브패치의 안전 판정에는 사용할 수 없습니다.

따라서 각 architecture는 trace에서 live function을 절대로 누락하지 않는 reliable stacktrace를 제공해야 합니다. 완전성을 확신할 수 없는 경우에는 성공한 것처럼 불완전한 trace를 반환하지 말고 명시적으로 실패해야 합니다.

일반 trace와 reliable trace
구분허용되는 결과라이브패치 적합성
Best-effort stacktrace일부 frame 누락 가능부적합
Reliable stacktrace모든 복귀 가능 함수 포함 또는 명시적 오류적합

라이브패치는 진단 편의보다 누락 없는 안전 판정을 우선합니다.

===================
Reliable Stacktrace
===================

This document outlines basic information about reliable stacktracing.

.. Table of Contents:

.. contents:: :local:

1. Introduction
===============

The kernel livepatch consistency model relies on accurately identifying which
functions may have live state and therefore may not be safe to patch. One way
to identify which functions are live is to use a stacktrace.

Existing stacktrace code may not always give an accurate picture of all
functions with live state, and best-effort approaches which can be helpful for
debugging are unsound for livepatching. Livepatching depends on architectures
to provide a *reliable* stacktrace which ensures it never omits any live
functions from a trace.

필수 구현과 반환 계약

25-51

Architecture는 신뢰 가능한 stacktrace 함수 중 하나를 구현해야 합니다. `CONFIG_ARCH_STACKWALK`를 사용하는 architecture는 `arch_stack_walk_reliable()`을, 그렇지 않은 architecture는 `save_stack_trace_tsk_reliable()`을 구현합니다.

핵심 계약은 둘 중 하나입니다. Trace가 task가 복귀할 수 있는 모든 함수를 포함하면 신뢰 가능함을 나타내는 0을 반환하고, 그 조건을 보장할 수 없으면 0이 아닌 값을 반환해야 합니다.

특정 함수의 생략이 정당한 예외도 있지만, 이 문서가 뒤에서 명시하는 경우에 한정됩니다. 그 밖의 모든 복귀 가능 함수는 반드시 보고해야 합니다.

또한 stack이나 다른 unwind state가 손상되었거나 신뢰할 수 없는 상황에도 견고해야 합니다. 이런 상태를 가능한 한 탐지해 0이 아닌 오류를 반환해야 하며, 무한 loop에 빠지거나 안전하지 않은 memory에 접근해서는 안 됩니다.

Reliable stacktrace 반환 계약
Task unwind 시작각 frame과 unwind state의 유효성 검사모든 복귀 가능 function을 포함했는지 판정완전하고 안전하면 trace와 `0` 반환손상·누락 가능성·미지원 상태가 있으면 non-zero 반환

완전성을 입증하지 못한 trace는 성공으로 취급하지 않습니다.

2. Requirements
===============

Architectures must implement one of the reliable stacktrace functions.
Architectures using CONFIG_ARCH_STACKWALK must implement
'arch_stack_walk_reliable', and other architectures must implement
'save_stack_trace_tsk_reliable'.

Principally, the reliable stacktrace function must ensure that either:

* The trace includes all functions that the task may be returned to, and the
  return code is zero to indicate that the trace is reliable.

* The return code is non-zero to indicate that the trace is not reliable.

.. note::
   In some cases it is legitimate to omit specific functions from the trace,
   but all other functions must be reported. These cases are described in
   further detail below.

Secondly, the reliable stacktrace function must be robust to cases where
the stack or other unwind state is corrupt or otherwise unreliable. The
function should attempt to detect such cases and return a non-zero error
code, and should not get stuck in an infinite loop or access memory in
an unsafe way.  Specific cases are described in further detail below.

컴파일 시점 분석

52-67

모든 kernel code를 올바르게 unwind하려면 compiler가 unwinder의 예상 규칙을 따랐는지 architecture가 검증해야 할 수 있습니다. 예를 들어 stack pointer를 제한된 방식으로만 변경하거나, 모든 함수가 특정 prologue와 epilogue 순서를 사용해야 할 수 있습니다.

이런 제약이 있는 architecture는 `objtool`로 kernel compilation 결과를 검증해야 합니다. 소스의 의도만 보는 것이 아니라 실제 생성된 code가 unwinder의 가정을 만족하는지 확인하는 단계입니다.

Unwinder가 정확한 복원을 위해 metadata를 요구한다면 그 정보도 build 때 `objtool`로 생성해야 합니다. 런타임 추측 대신 검증된 metadata를 사용하면 함수 중간이나 예외 경계의 상태를 더 안전하게 판별할 수 있습니다.

objtool의 두 역할
역할검사·생성 대상효과
정적 검증stack pointer 조작, prologue, epilogueunwinder 가정 위반 탐지
Metadata 생성architecture별 unwind 상태런타임 frame 복원 근거 제공

Build 단계에서 code 규칙과 unwind metadata를 준비합니다.

3. Compile-time analysis
========================

To ensure that kernel code can be correctly unwound in all cases,
architectures may need to verify that code has been compiled in a manner
expected by the unwinder. For example, an unwinder may expect that
functions manipulate the stack pointer in a limited way, or that all
functions use specific prologue and epilogue sequences. Architectures
with such requirements should verify the kernel compilation using
objtool.

In some cases, an unwinder may require metadata to correctly unwind.
Where necessary, this metadata should be generated at build time using
objtool.

고려사항과 성공 종료 식별

68-97

Unwind 과정은 architecture의 procedure call standard와 kernel configuration에 따라 달라집니다. 공통적으로 stack 또는 frame pointer 손상, 드문 상황의 unwind 지원 누락이나 unwinder bug, eBPF 같은 동적 code 또는 EFI runtime service 같은 외부 code 때문에 예상보다 일찍 끝날 수 있습니다.

다른 검사에서 조기 종료를 잡지 못하더라도 함수 누락을 방지하려면 trace가 예상 위치에서 끝났는지 검증하는 것이 강하게 권장됩니다.

예상 종료점은 kernel 진입점인 특정 함수 내부, kernel entry point에 대응하는 stack의 특정 위치, 또는 architecture가 task stack과 IRQ stack을 나누는 경우처럼 진입점에 대응하는 특정 stack일 수 있습니다.

성공 종료 판정
마지막으로 복원한 frame 확인허용된 kernel entry function 또는 위치인지 검사현재 stack 종류가 기대한 task·IRQ stack인지 검사모든 조건이 맞으면 정상 종료예상 밖 위치나 stack이면 unreliable 오류

Frame 탐색이 멈췄다는 사실만으로 성공을 선언하지 않습니다.

4. Considerations
=================

The unwinding process varies across architectures, their respective procedure
call standards, and kernel configurations. This section describes common
details that architectures should consider.

4.1 Identifying successful termination
--------------------------------------

Unwinding may terminate early for a number of reasons, including:

* Stack or frame pointer corruption.

* Missing unwind support for an uncommon scenario, or a bug in the unwinder.

* Dynamically generated code (e.g. eBPF) or foreign code (e.g. EFI runtime
  services) not following the conventions expected by the unwinder.

To ensure that this does not result in functions being omitted from the trace,
even if not caught by other checks, it is strongly recommended that
architectures verify that a stacktrace ends at an expected location, e.g.

* Within a specific function that is an entry point to the kernel.

* At a specific location on a stack expected for a kernel entry point.

* On a specific stack expected for a kernel entry point (e.g. if the
  architecture has separate task and IRQ stacks).

Unwind 가능한 code 식별

98-131

Unwinding은 보통 frame pointer 조작 같은 정해진 convention을 code가 따른다고 가정합니다. Exception vector와 entry assembly, PLT entry와 veneer function, ftrace·kprobes trampoline assembly, eBPF·optprobe trampoline처럼 동적으로 생성된 code, EFI runtime service 같은 외부 code는 이 규칙을 따르지 않거나 별도 처리가 필요할 수 있습니다.

이런 code 때문에 함수가 trace에서 누락되지 않도록 architecture는 unwind가 신뢰 가능하다고 알려진 code를 적극적으로 식별하고, 그 밖의 code에서 시작하는 unwind는 거부하는 방식이 권장됩니다.

`__kernel_text_address()`를 사용하면 module과 eBPF를 포함한 kernel code를 외부 code와 구별할 수 있습니다. 이 검사는 잘못된 return address가 kernel text 밖을 가리키는 stack 손상도 찾아내는 데 도움이 됩니다.

Architecture는 신뢰할 수 없는 code를 특별한 linker section에 배치해 그 범위에서의 unwind를 거부하거나, bounds information으로 특정 code 구간을 식별할 수 있습니다.

별도 판정이 필요한 code
범주권장 처리
진입 codeexception vector, entry assembly전용 규칙 또는 거부
연결 보조 codePLT entry, veneer function범위와 unwind 규칙 확인
Instrumentationftrace, kprobes trampoline원래 return state 복원 가능성 검사
동적 codeeBPF, optprobe trampoline`__kernel_text_address()`와 전용 metadata
외부 codeEFI runtime servicekernel code가 아니면 거부

일반 함수 convention을 보장할 수 없는 대표 범주입니다.

4.2 Identifying unwindable code
-------------------------------

Unwinding typically relies on code following specific conventions (e.g.
manipulating a frame pointer), but there can be code which may not follow these
conventions and may require special handling in the unwinder, e.g.

* Exception vectors and entry assembly.

* Procedure Linkage Table (PLT) entries and veneer functions.

* Trampoline assembly (e.g. ftrace, kprobes).

* Dynamically generated code (e.g. eBPF, optprobe trampolines).

* Foreign code (e.g. EFI runtime services).

To ensure that such cases do not result in functions being omitted from a
trace, it is strongly recommended that architectures positively identify code
which is known to be reliable to unwind from, and reject unwinding from all
other code.

Kernel code including modules and eBPF can be distinguished from foreign code
using '__kernel_text_address()'. Checking for this also helps to detect stack
corruption.

There are several ways an architecture may identify kernel code which is deemed
unreliable to unwind from, e.g.

* Placing such code into special linker sections, and rejecting unwinding from
  any code in these sections.

* Identifying specific portions of code using bounds information.

Interrupt와 exception 경계 넘기

132-158

함수 호출 경계에서는 stack과 unwind state가 일관된 상태일 것으로 기대할 수 있지만, 함수 실행 중간에는 그렇지 않을 수 있습니다. Prologue나 epilogue 도중에는 frame pointer가 일시적으로 무효할 수 있고, 함수 본문에서는 return address가 임의의 general-purpose register에 들어 있을 수 있습니다. Dynamic instrumentation 때문에 이 상태가 런타임에 바뀌기도 합니다.

Stack이나 unwind state가 불일치한 순간 interrupt 또는 다른 exception이 발생하면 reliable unwind가 불가능할 수 있으며, 그 결과가 신뢰 가능한지조차 판별하기 어려울 수 있습니다.

이 경우의 신뢰성을 식별할 수 없거나 항상 신뢰할 수 없는 architecture는 exception boundary를 넘는 unwind를 거부해야 합니다. IRQ는 안전하지만 NMI는 안전하지 않은 것처럼 exception 종류별 판단이 달라질 수 있습니다.

반대로 신뢰 가능한 시점을 식별할 수 있거나 이런 문제가 없는 architecture는 exception boundary를 넘어 unwind를 시도해야 합니다. 그러면 라이브패치 consistency check가 불필요하게 멈추는 일을 줄이고 transition을 더 빨리 완료할 수 있습니다.

Exception 경계 정책
판정 능력경계별 상태동작
신뢰성을 식별할 수 없음IRQ·NMI 포함 불확실경계를 넘는 unwind 거부
일부만 식별 가능예: IRQ 신뢰, NMI 불신종류별 허용·거부
신뢰성을 식별 가능metadata와 상태가 일치경계를 넘어 계속 unwind

지원 여부는 낙관적 추측이 아니라 architecture의 판별 능력으로 정합니다.

4.3 Unwinding across interrupts and exceptions
----------------------------------------------

At function call boundaries the stack and other unwind state is expected to be
in a consistent state suitable for reliable unwinding, but this may not be the
case part-way through a function. For example, during a function prologue or
epilogue a frame pointer may be transiently invalid, or during the function
body the return address may be held in an arbitrary general purpose register.
For some architectures this may change at runtime as a result of dynamic
instrumentation.

If an interrupt or other exception is taken while the stack or other unwind
state is in an inconsistent state, it may not be possible to reliably unwind,
and it may not be possible to identify whether such unwinding will be reliable.
See below for examples.

Architectures which cannot identify when it is reliable to unwind such cases
(or where it is never reliable) must reject unwinding across exception
boundaries. Note that it may be reliable to unwind across certain
exceptions (e.g. IRQ) but unreliable to unwind across other exceptions
(e.g. NMI).

Architectures which can identify when it is reliable to unwind such cases (or
have no such cases) should attempt to unwind across exception boundaries, as
doing so can prevent unnecessarily stalling livepatch consistency checks and
permits livepatch transitions to complete more quickly.

Return address 재기록

159-224

일부 trampoline은 함수가 반환할 때 개입하기 위해 return address를 임시로 바꿉니다. Function graph tracing의 ftrace trampoline과 kretprobe를 위한 kprobes 또는 optprobes trampoline이 대표적입니다.

이때 원래 return address는 평소 위치에 없습니다. 라이브패치 대상이 아닌 trampoline에서 unwinder가 원래 주소를 확실하게 결정할 수 있고 다른 unwind state도 변경되지 않았다면 trampoline 대신 원래 return address를 보고하면서 trace를 신뢰 가능으로 처리할 수 있습니다. 그렇지 않으면 반드시 unreliable로 보고해야 합니다.

원래 return address의 위치는 entry trampoline과 return trampoline의 전체 실행 동안 일정하지 않으므로 특별한 주의가 필요합니다. x86_64의 `return_to_handler`가 그 예입니다.

추적 중인 함수가 실행되는 동안 stack의 return address는 `return_to_handler` 시작점을 가리키고, 원래 주소는 task의 `cur_ret_stack`에 저장됩니다. 이 구간에서는 `ftrace_graph_ret_addr()`로 원래 주소를 찾을 수 있습니다.

추적 함수가 `return_to_handler`로 반환한 뒤에는 stack에 return address가 더 이상 없습니다. 원래 주소는 잠시 `cur_ret_stack`에 남아 있지만, `ftrace_return_to_handler()`가 이를 꺼낸 뒤 compiler가 임의 위치로 옮겼다가 `rax`로 반환합니다. Trampoline은 값을 `rdi`로 옮긴 다음 그 주소로 jump합니다.

따라서 `ftrace_return_to_handler()`가 `cur_ret_stack`에서 주소를 제거한 뒤처럼 원래 주소의 위치를 확실히 알 수 없는 구간은 unwind하지 못할 수 있습니다.

Architecture는 아직 `return_to_handler`로 복귀하기 전 상태는 unwind하는 것이 권장됩니다. 하지만 `return_to_handler` 한가운데나 return address를 바꾸는 다른 trampoline 내부를 반드시 unwind할 의무는 없으며, 이 구간을 unreliable로 보고해도 됩니다.

x86_64 return address 위치 변화
추적 함수 실행: stack은 `return_to_handler`, 원본은 `cur_ret_stack``ftrace_graph_ret_addr()`로 원본 주소 복원 가능`return_to_handler` 진입: stack의 return address가 사라짐`ftrace_return_to_handler()`가 `cur_ret_stack`에서 원본 제거Compiler가 원본을 임시 위치로 이동한 뒤 `rax`로 반환Trampoline이 `rax`를 `rdi`로 옮겨 `JMP_NOSPEC rdi` 수행

원문의 trampoline 코드는 유지하면서 주소 소유 위치를 단계별로 나타냅니다.

4.4 Rewriting of return addresses
---------------------------------

Some trampolines temporarily modify the return address of a function in order
to intercept when that function returns with a return trampoline, e.g.

* An ftrace trampoline may modify the return address so that function graph
  tracing can intercept returns.

* A kprobes (or optprobes) trampoline may modify the return address so that
  kretprobes can intercept returns.

When this happens, the original return address will not be in its usual
location. For trampolines which are not subject to live patching, where an
unwinder can reliably determine the original return address and no unwind state
is altered by the trampoline, the unwinder may report the original return
address in place of the trampoline and report this as reliable. Otherwise, an
unwinder must report these cases as unreliable.

Special care is required when identifying the original return address, as this
information is not in a consistent location for the duration of the entry
trampoline or return trampoline. For example, considering the x86_64
'return_to_handler' return trampoline:

.. code-block:: none

   SYM_CODE_START(return_to_handler)
           UNWIND_HINT_UNDEFINED
           subq  $24, %rsp

           /* Save the return values */
           movq %rax, (%rsp)
           movq %rdx, 8(%rsp)
           movq %rbp, %rdi

           call ftrace_return_to_handler

           movq %rax, %rdi
           movq 8(%rsp), %rdx
           movq (%rsp), %rax
           addq $24, %rsp
           JMP_NOSPEC rdi
   SYM_CODE_END(return_to_handler)

While the traced function runs its return address on the stack points to
the start of return_to_handler, and the original return address is stored in
the task's cur_ret_stack. During this time the unwinder can find the return
address using ftrace_graph_ret_addr().

When the traced function returns to return_to_handler, there is no longer a
return address on the stack, though the original return address is still stored
in the task's cur_ret_stack. Within ftrace_return_to_handler(), the original
return address is removed from cur_ret_stack and is transiently moved
arbitrarily by the compiler before being returned in rax. The return_to_handler
trampoline moves this into rdi before jumping to it.

Architectures might not always be able to unwind such sequences, such as when
ftrace_return_to_handler() has removed the address from cur_ret_stack, and the
location of the return address cannot be reliably determined.

It is recommended that architectures unwind cases where return_to_handler has
not yet been returned to, but architectures are not required to unwind from the
middle of return_to_handler and can report this as unreliable. Architectures
are not required to unwind from other trampolines which modify the return
address.

Return address 가림

225-251

일부 trampoline은 반환을 가로채려고 return address를 다시 쓰지는 않지만, 실행 도중 return address 또는 다른 unwind state를 일시적으로 덮어씁니다.

x86_64 optprobes는 probe 대상 함수에 연관된 optprobe trampoline으로 가는 `JMP` instruction을 삽입합니다. Probe가 실행되면 CPU가 trampoline으로 branch하지만 대상 함수 주소는 register에도 stack에도 남아 있지 않습니다.

arm64의 `DYNAMIC_FTRACE_WITH_REGS`는 `MOV X9, X30`으로 link register `X30`의 return address를 `X9`에 보존한 다음 `BL <trampoline>`을 수행합니다. `BL`이 link register를 덮어쓰므로 trampoline 시작점에서 추적 함수 주소는 일반적인 link register가 아니라 `X9`에 있습니다.

Architecture는 unwinder가 이런 경우를 확실하게 복원하도록 구현하거나, 복원을 보장할 수 없다면 해당 unwind를 unreliable로 보고해야 합니다.

가려진 return address 사례
Architecture·기능동작원래 주소 상태
x86_64 optprobes대상 함수에서 trampoline으로 `JMP`register와 stack 어디에도 대상 함수 주소가 없음
arm64 `DYNAMIC_FTRACE_WITH_REGS``MOV X9, X30`; `BL <trampoline>`trampoline 진입 시 `X9`에 보존

Instruction sequence에 따라 원래 주소가 사라지거나 다른 register로 이동합니다.

4.5 Obscuring of return addresses
---------------------------------

Some trampolines do not rewrite the return address in order to intercept
returns, but do transiently clobber the return address or other unwind state.

For example, the x86_64 implementation of optprobes patches the probed function
with a JMP instruction which targets the associated optprobe trampoline. When
the probe is hit, the CPU will branch to the optprobe trampoline, and the
address of the probed function is not held in any register or on the stack.

Similarly, the arm64 implementation of DYNAMIC_FTRACE_WITH_REGS patches traced
functions with the following:

.. code-block:: none

   MOV X9, X30
   BL <trampoline>

The MOV saves the link register (X30) into X9 to preserve the return address
before the BL clobbers the link register and branches to the trampoline. At the
start of the trampoline, the address of the traced function is in X9 rather
than the link register as would usually be the case.

Architectures must either ensure that unwinders either reliably unwind
such cases, or report the unwinding as unreliable.