요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
상태와 option
srso.rst:33-154microcode 요구 사항, sysfs 상태 9개와 완화 선택을 정리합니다.
Safe RET 구현
srso.rst:155-174Zen 세대별 untraining 및 safe-return thunk를 설명합니다.
perf·selftest 검증
srso.rst:175-242PMC event, 기대 counter 값과 x86 selftest 명령을 보존합니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
Speculative Return Stack Overflow (SRSO)
========================================
This is a mitigation for the speculative return stack overflow (SRSO)
vulnerability found on AMD processors. The mechanism is by now the well
known scenario of poisoning CPU functional units - the Branch Target
Buffer (BTB) and Return Address Predictor (RAP) in this case - and then
tricking the elevated privilege domain (the kernel) into leaking
sensitive data.
AMD CPUs predict RET instructions using a Return Address Predictor (aka
Return Address Stack/Return Stack Buffer). In some cases, a non-architectural
CALL instruction (i.e., an instruction predicted to be a CALL but is
not actually a CALL) can create an entry in the RAP which may be used
to predict the target of a subsequent RET instruction.
The specific circumstances that lead to this varies by microarchitecture
but the concern is that an attacker can mis-train the CPU BTB to predict
non-architectural CALL instructions in kernel space and use this to
control the speculative target of a subsequent kernel RET, potentially
leading to information disclosure via a speculative side-channel.
The issue is tracked under CVE-2023-20569.
Affected processors
-------------------
AMD Zen, generations 1-4. That is, all families 0x17 and 0x19. Older
processors have not been investigated.
System information and options
------------------------------
First of all, it is required that the latest microcode be loaded for
mitigations to be effective.
The sysfs file showing SRSO mitigation status is:
/sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow
The possible values in this file are:
* 'Not affected':
The processor is not vulnerable
* 'Vulnerable':
The processor is vulnerable and no mitigations have been applied.
* 'Vulnerable: No microcode':
The processor is vulnerable, no microcode extending IBPB
functionality to address the vulnerability has been applied.
* 'Vulnerable: Safe RET, no microcode':
The "Safe RET" mitigation (see below) has been applied to protect the
kernel, but the IBPB-extending microcode has not been applied. User
space tasks may still be vulnerable.
* 'Vulnerable: Microcode, no safe RET':
Extended IBPB functionality microcode patch has been applied. It does
not address User->Kernel and Guest->Host transitions protection but it
does address User->User and VM->VM attack vectors.
Note that User->User mitigation is controlled by how the IBPB aspect in
the Spectre v2 mitigation is selected:
* conditional IBPB:
where each process can select whether it needs an IBPB issued
around it PR_SPEC_DISABLE/_ENABLE etc, see :doc:`spectre`
* strict:
i.e., always on - by supplying spectre_v2_user=on on the kernel
command line
(spec_rstack_overflow=microcode)
* 'Mitigation: Safe RET':
Combined microcode/software mitigation. It complements the
extended IBPB microcode patch functionality by addressing
User->Kernel and Guest->Host transitions protection.
Selected by default or by spec_rstack_overflow=safe-ret
* 'Mitigation: IBPB':
Similar protection as "safe RET" above but employs an IBPB barrier on
privilege domain crossings (User->Kernel, Guest->Host).
(spec_rstack_overflow=ibpb)
* 'Mitigation: IBPB on VMEXIT':
Mitigation addressing the cloud provider scenario - the Guest->Host
transitions only.
(spec_rstack_overflow=ibpb-vmexit)
* 'Mitigation: Reduced Speculation':
This mitigation gets automatically enabled when the above one "IBPB on
VMEXIT" has been selected and the CPU supports the BpSpecReduce bit.
It gets automatically enabled on machines which have the
SRSO_USER_KERNEL_NO=1 CPUID bit. In that case, the code logic is to switch
to the above =ibpb-vmexit mitigation because the user/kernel boundary is
not affected anymore and thus "safe RET" is not needed.
After enabling the IBPB on VMEXIT mitigation option, the BpSpecReduce bit
is detected (functionality present on all such machines) and that
practically overrides IBPB on VMEXIT as it has a lot less performance
impact and takes care of the guest->host attack vector too.
In order to exploit vulnerability, an attacker needs to:
- gain local access on the machine
- break kASLR
- find gadgets in the running kernel in order to use them in the exploit
- potentially create and pin an additional workload on the sibling
thread, depending on the microarchitecture (not necessary on fam 0x19)
- run the exploit
Considering the performance implications of each mitigation type, the
default one is 'Mitigation: safe RET' which should take care of most
attack vectors, including the local User->Kernel one.
As always, the user is advised to keep her/his system up-to-date by
applying software updates regularly.
The default setting will be reevaluated when needed and especially when
new attack vectors appear.
As one can surmise, 'Mitigation: safe RET' does come at the cost of some
performance depending on the workload. If one trusts her/his userspace
and does not want to suffer the performance impact, one can always
disable the mitigation with spec_rstack_overflow=off.
Similarly, 'Mitigation: IBPB' is another full mitigation type employing
an indirect branch prediction barrier after having applied the required
microcode patch for one's system. This mitigation comes also at
a performance cost.
Mitigation: Safe RET
--------------------
The mitigation works by ensuring all RET instructions speculate to
a controlled location, similar to how speculation is controlled in the
retpoline sequence. To accomplish this, the __x86_return_thunk forces
the CPU to mispredict every function return using a 'safe return'
sequence.
To ensure the safety of this mitigation, the kernel must ensure that the
safe return sequence is itself free from attacker interference. In Zen3
and Zen4, this is accomplished by creating a BTB alias between the
untraining function srso_alias_untrain_ret() and the safe return
function srso_alias_safe_ret() which results in evicting a potentially
poisoned BTB entry and using that safe one for all function returns.
In older Zen1 and Zen2, this is accomplished using a reinterpretation
technique similar to Retbleed one: srso_untrain_ret() and
srso_safe_ret().
Checking the safe RET mitigation actually works
-----------------------------------------------
In case one wants to validate whether the SRSO safe RET mitigation works
on a kernel, one could use two performance counters
* PMC_0xc8 - Count of RET/RET lw retired
* PMC_0xc9 - Count of RET/RET lw retired mispredicted
and compare the number of RETs retired properly vs those retired
mispredicted, in kernel mode. Another way of specifying those events
is::
# perf list ex_ret_near_ret
List of pre-defined events (to be used in -e or -M):
core:
ex_ret_near_ret
[Retired Near Returns]
ex_ret_near_ret_mispred
[Retired Near Returns Mispredicted]
Either the command using the event mnemonics::
# perf stat -e ex_ret_near_ret:k -e ex_ret_near_ret_mispred:k sleep 10s
or using the raw PMC numbers::
# perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
should give the same amount. I.e., every RET retired should be
mispredicted::
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
137,167 cpu/event=0xc8,umask=0/k
137,173 cpu/event=0xc9,umask=0/k
10.004110303 seconds time elapsed
0.000000000 seconds user
0.004462000 seconds sys
vs the case when the mitigation is disabled (spec_rstack_overflow=off)
or not functioning properly, showing usually a lot smaller number of
mispredicted retired RETs vs the overall count of retired RETs during
a workload::
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
201,627 cpu/event=0xc8,umask=0/k
4,074 cpu/event=0xc9,umask=0/k
10.003267252 seconds time elapsed
0.002729000 seconds user
0.000000000 seconds sys
Also, there is a selftest which performs the above, go to
tools/testing/selftests/x86/ and do::
make srso
./srso
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
SRSO 개요
1-26이 문서는 AMD 프로세서에서 발견된 Speculative Return Stack Overflow(SRSO) 취약점의 완화를 설명합니다. 공격자는 Branch Target Buffer(BTB)와 Return Address Predictor(RAP) 같은 CPU functional unit을 오염시킨 뒤 높은 privilege domain인 kernel이 민감한 data를 누출하도록 유도합니다.
AMD CPU는 Return Address Predictor, 즉 Return Address Stack 또는 Return Stack Buffer로 `RET`를 예측합니다. 실제 `CALL`은 아니지만 `CALL`로 예측된 non-architectural CALL instruction이 RAP entry를 만들고 이후 `RET` target 예측에 사용될 수 있습니다.
구체적 조건은 microarchitecture마다 다르지만, 공격자가 CPU BTB를 잘못 train해 kernel space의 non-architectural CALL을 예측시키고 이후 kernel `RET`의 speculative target을 제어하면 speculative side channel로 정보가 노출될 수 있습니다.
이 문제는 `CVE-2023-20569`로 추적됩니다.
영향받는 프로세서
27-32AMD Zen 1~4, 즉 family `0x17`과 `0x19` 전체가 영향을 받습니다. 이전 프로세서는 조사되지 않았습니다.
system 정보와 완화 option
33-154완화가 효과를 내려면 먼저 최신 microcode를 load해야 합니다.
/sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow
| sysfs 상태 | 의미 |
|---|---|
| Not affected | 프로세서가 취약하지 않습니다. |
| Vulnerable | 프로세서가 취약하고 적용된 완화가 없습니다. |
| Vulnerable: No microcode | IBPB 기능을 확장하는 완화 microcode가 없습니다. |
| Vulnerable: Safe RET, no microcode | Safe RET가 kernel을 보호하지만 extended-IBPB microcode가 없어 userspace task는 취약할 수 있습니다. |
| Vulnerable: Microcode, no safe RET | extended IBPB microcode가 User-to-User와 VM-to-VM을 보호하지만 User-to-Kernel과 Guest-to-Host 전환은 보호하지 않습니다. `spec_rstack_overflow=microcode`에 해당합니다. |
| Mitigation: Safe RET | microcode와 software를 결합해 User-to-Kernel 및 Guest-to-Host까지 보호합니다. 기본값 또는 `spec_rstack_overflow=safe-ret`로 선택합니다. |
| Mitigation: IBPB | privilege-domain crossing에서 IBPB barrier를 사용해 Safe RET와 비슷한 보호를 제공합니다. `spec_rstack_overflow=ibpb`입니다. |
| Mitigation: IBPB on VMEXIT | cloud-provider 환경의 Guest-to-Host 전환만 보호합니다. `spec_rstack_overflow=ibpb-vmexit`입니다. |
| Mitigation: Reduced Speculation | IBPB-on-VMEXIT 선택 시 CPU가 `BpSpecReduce`를 지원하면 자동 활성화됩니다. `SRSO_USER_KERNEL_NO=1` CPU에서는 user/kernel boundary가 영향받지 않아 safe RET 대신 이 경로를 선택하며, 성능 비용이 훨씬 작으면서 guest-to-host도 보호합니다. |
`Vulnerable: Microcode, no safe RET`에서 User-to-User 보호는 Spectre v2의 IBPB 선택에 달립니다. conditional IBPB에서는 각 process가 `PR_SPEC_DISABLE/_ENABLE` 등으로 자신 주변의 IBPB 필요 여부를 선택합니다. strict mode는 kernel command line의 `spectre_v2_user=on`으로 항상 켭니다.
취약점을 악용하려면 공격자가 다음을 수행해야 합니다.
- machine에 local access를 얻습니다.
- kASLR을 깨뜨립니다.
- exploit에 사용할 running-kernel gadget을 찾습니다.
- microarchitecture에 따라 sibling thread에 추가 workload를 만들어 pin합니다. family 0x19에는 필요하지 않습니다.
- exploit을 실행합니다.
성능 영향을 고려한 기본값은 `Mitigation: Safe RET`이며 local User-to-Kernel을 포함한 대부분 attack vector를 다룹니다. system을 정기적으로 update해야 하며 새 attack vector가 등장하면 기본 설정을 재평가합니다.
Safe RET는 workload에 따라 성능 비용이 있습니다. userspace를 신뢰하고 비용을 피하려면 `spec_rstack_overflow=off`로 끌 수 있습니다. 필요한 microcode 뒤에 IBPB를 사용하는 `Mitigation: IBPB`도 완전한 완화지만 역시 성능 비용이 있습니다.
Safe RET 완화
155-174Safe RET는 retpoline과 비슷하게 모든 `RET`가 통제된 위치로 speculate하도록 합니다. `__x86_return_thunk`가 safe-return sequence로 모든 function return의 misprediction을 강제합니다.
safe-return sequence 자체가 공격자 간섭을 받지 않게 해야 합니다. Zen3·Zen4에서는 untraining function `srso_alias_untrain_ret()`와 safe-return function `srso_alias_safe_ret()` 사이에 BTB alias를 만들어 오염 가능 BTB entry를 evict하고 모든 function return에 안전한 entry를 사용합니다.
Zen1·Zen2에서는 Retbleed와 비슷한 reinterpretation 기법의 `srso_untrain_ret()`와 `srso_safe_ret()`를 사용합니다.
Safe RET 동작 검증
175-239kernel의 SRSO Safe RET가 동작하는지 검증하려면 `PMC_0xc8`(retired RET/RET lw 수)과 `PMC_0xc9`(mispredicted retired RET/RET lw 수)를 kernel mode에서 비교합니다.
# perf list ex_ret_near_ret
List of pre-defined events (to be used in -e or -M):
core:
ex_ret_near_ret
[Retired Near Returns]
ex_ret_near_ret_mispred
[Retired Near Returns Mispredicted]
event mnemonic을 사용한 명령은 다음과 같습니다.
# perf stat -e ex_ret_near_ret:k -e ex_ret_near_ret_mispred:k sleep 10s
raw PMC number를 사용하면 다음과 같습니다.
# perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
완화가 정상이라면 모든 retired RET가 mispredict되어 두 값이 거의 같아야 합니다.
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
137,167 cpu/event=0xc8,umask=0/k
137,173 cpu/event=0xc9,umask=0/k
10.004110303 seconds time elapsed
0.000000000 seconds user
0.004462000 seconds sys
`spec_rstack_overflow=off`로 완화를 껐거나 제대로 동작하지 않으면 workload 동안 전체 retired RET보다 mispredicted retired RET가 훨씬 적습니다.
[root@brent: ~/kernel/linux/tools/perf> ./perf stat -e cpu/event=0xc8,umask=0/k -e cpu/event=0xc9,umask=0/k sleep 10s
Performance counter stats for 'sleep 10s':
201,627 cpu/event=0xc8,umask=0/k
4,074 cpu/event=0xc9,umask=0/k
10.003267252 seconds time elapsed
0.002729000 seconds user
0.000000000 seconds sys
SRSO selftest
240-242같은 검사를 수행하는 selftest는 `tools/testing/selftests/x86/`에서 실행합니다.
make srso
./srso
취약점과 CPU
srso.rst:1-32non-architectural CALL과 RAP poisoning 및 영향 Zen family를 설명합니다.