요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
Microarchitectural Data Sampling (MDS) mitigation
=================================================
.. _mds:
Overview
--------
Microarchitectural Data Sampling (MDS) is a family of side channel attacks
on internal buffers in Intel CPUs. The variants are:
- Microarchitectural Store Buffer Data Sampling (MSBDS) (CVE-2018-12126)
- Microarchitectural Fill Buffer Data Sampling (MFBDS) (CVE-2018-12130)
- Microarchitectural Load Port Data Sampling (MLPDS) (CVE-2018-12127)
- Microarchitectural Data Sampling Uncacheable Memory (MDSUM) (CVE-2019-11091)
MSBDS leaks Store Buffer Entries which can be speculatively forwarded to a
dependent load (store-to-load forwarding) as an optimization. The forward
can also happen to a faulting or assisting load operation for a different
memory address, which can be exploited under certain conditions. Store
buffers are partitioned between Hyper-Threads so cross thread forwarding is
not possible. But if a thread enters or exits a sleep state the store
buffer is repartitioned which can expose data from one thread to the other.
MFBDS leaks Fill Buffer Entries. Fill buffers are used internally to manage
L1 miss situations and to hold data which is returned or sent in response
to a memory or I/O operation. Fill buffers can forward data to a load
operation and also write data to the cache. When the fill buffer is
deallocated it can retain the stale data of the preceding operations which
can then be forwarded to a faulting or assisting load operation, which can
be exploited under certain conditions. Fill buffers are shared between
Hyper-Threads so cross thread leakage is possible.
MLPDS leaks Load Port Data. Load ports are used to perform load operations
from memory or I/O. The received data is then forwarded to the register
file or a subsequent operation. In some implementations the Load Port can
contain stale data from a previous operation which can be forwarded to
faulting or assisting loads under certain conditions, which again can be
exploited eventually. Load ports are shared between Hyper-Threads so cross
thread leakage is possible.
MDSUM is a special case of MSBDS, MFBDS and MLPDS. An uncacheable load from
memory that takes a fault or assist can leave data in a microarchitectural
structure that may later be observed using one of the same methods used by
MSBDS, MFBDS or MLPDS.
Exposure assumptions
--------------------
It is assumed that attack code resides in user space or in a guest with one
exception. The rationale behind this assumption is that the code construct
needed for exploiting MDS requires:
- to control the load to trigger a fault or assist
- to have a disclosure gadget which exposes the speculatively accessed
data for consumption through a side channel.
- to control the pointer through which the disclosure gadget exposes the
data
The existence of such a construct in the kernel cannot be excluded with
100% certainty, but the complexity involved makes it extremely unlikely.
There is one exception, which is untrusted BPF. The functionality of
untrusted BPF is limited, but it needs to be thoroughly investigated
whether it can be used to create such a construct.
Mitigation strategy
-------------------
All variants have the same mitigation strategy at least for the single CPU
thread case (SMT off): Force the CPU to clear the affected buffers.
This is achieved by using the otherwise unused and obsolete VERW
instruction in combination with a microcode update. The microcode clears
the affected CPU buffers when the VERW instruction is executed.
For virtualization there are two ways to achieve CPU buffer
clearing. Either the modified VERW instruction or via the L1D Flush
command. The latter is issued when L1TF mitigation is enabled so the extra
VERW can be avoided. If the CPU is not affected by L1TF then VERW needs to
be issued.
If the VERW instruction with the supplied segment selector argument is
executed on a CPU without the microcode update there is no side effect
other than a small number of pointlessly wasted CPU cycles.
This does not protect against cross Hyper-Thread attacks except for MSBDS
which is only exploitable cross Hyper-thread when one of the Hyper-Threads
enters a C-state.
The kernel provides a function to invoke the buffer clearing:
x86_clear_cpu_buffers()
Also macro CLEAR_CPU_BUFFERS can be used in ASM late in exit-to-user path.
Other than CFLAGS.ZF, this macro doesn't clobber any registers.
The mitigation is invoked on kernel/userspace, hypervisor/guest and C-state
(idle) transitions.
As a special quirk to address virtualization scenarios where the host has
the microcode updated, but the hypervisor does not (yet) expose the
MD_CLEAR CPUID bit to guests, the kernel issues the VERW instruction in the
hope that it might actually clear the buffers. The state is reflected
accordingly.
According to current knowledge additional mitigations inside the kernel
itself are not required because the necessary gadgets to expose the leaked
data cannot be controlled in a way which allows exploitation from malicious
user space or VM guests.
Kernel internal mitigation modes
--------------------------------
======= ============================================================
off Mitigation is disabled. Either the CPU is not affected or
mds=off is supplied on the kernel command line
full Mitigation is enabled. CPU is affected and MD_CLEAR is
advertised in CPUID.
vmwerv Mitigation is enabled. CPU is affected and MD_CLEAR is not
advertised in CPUID. That is mainly for virtualization
scenarios where the host has the updated microcode but the
hypervisor does not expose MD_CLEAR in CPUID. It's a best
effort approach without guarantee.
======= ============================================================
If the CPU is affected and mds=off is not supplied on the kernel command
line then the kernel selects the appropriate mitigation mode depending on
the availability of the MD_CLEAR CPUID bit.
Mitigation points
-----------------
1. Return to user space
^^^^^^^^^^^^^^^^^^^^^^^
When transitioning from kernel to user space the CPU buffers are flushed
on affected CPUs when the mitigation is not disabled on the kernel
command line. The mitigation is enabled through the feature flag
X86_FEATURE_CLEAR_CPU_BUF.
The mitigation is invoked just before transitioning to userspace after
user registers are restored. This is done to minimize the window in
which kernel data could be accessed after VERW e.g. via an NMI after
VERW.
**Corner case not handled**
Interrupts returning to kernel don't clear CPUs buffers since the
exit-to-user path is expected to do that anyways. But, there could be
a case when an NMI is generated in kernel after the exit-to-user path
has cleared the buffers. This case is not handled and NMI returning to
kernel don't clear CPU buffers because:
1. It is rare to get an NMI after VERW, but before returning to userspace.
2. For an unprivileged user, there is no known way to make that NMI
less rare or target it.
3. It would take a large number of these precisely-timed NMIs to mount
an actual attack. There's presumably not enough bandwidth.
4. The NMI in question occurs after a VERW, i.e. when user state is
restored and most interesting data is already scrubbed. What's left
is only the data that NMI touches, and that may or may not be of
any interest.
2. C-State transition
^^^^^^^^^^^^^^^^^^^^^
When a CPU goes idle and enters a C-State the CPU buffers need to be
cleared on affected CPUs when SMT is active. This addresses the
repartitioning of the store buffer when one of the Hyper-Threads enters
a C-State.
When SMT is inactive, i.e. either the CPU does not support it or all
sibling threads are offline CPU buffer clearing is not required.
The idle clearing is enabled on CPUs which are only affected by MSBDS
and not by any other MDS variant. The other MDS variants cannot be
protected against cross Hyper-Thread attacks because the Fill Buffer and
the Load Ports are shared. So on CPUs affected by other variants, the
idle clearing would be a window dressing exercise and is therefore not
activated.
The invocation is controlled by the static key cpu_buf_idle_clear which is
switched depending on the chosen mitigation mode and the SMT state of the
system.
The buffer clear is only invoked before entering the C-State to prevent
that stale data from the idling CPU from spilling to the Hyper-Thread
sibling after the store buffer got repartitioned and all entries are
available to the non idle sibling.
When coming out of idle the store buffer is partitioned again so each
sibling has half of it available. The back from idle CPU could be then
speculatively exposed to contents of the sibling. The buffers are
flushed either on exit to user space or on VMENTER so malicious code
in user space or the guest cannot speculatively access them.
The mitigation is hooked into all variants of halt()/mwait(), but does
not cover the legacy ACPI IO-Port mechanism because the ACPI idle driver
has been superseded by the intel_idle driver around 2010 and is
preferred on all affected CPUs which are expected to gain the MD_CLEAR
functionality in microcode. Aside of that the IO-Port mechanism is a
legacy interface which is only used on older systems which are either
not affected or do not receive microcode updates anymore.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
MDS 개요와 공격 변종
1-45Microarchitectural Data Sampling(MDS)은 Intel CPU 내부 buffer를 대상으로 하는 side-channel attack 계열입니다.
- Microarchitectural Store Buffer Data Sampling(MSBDS), `CVE-2018-12126`
- Microarchitectural Fill Buffer Data Sampling(MFBDS), `CVE-2018-12130`
- Microarchitectural Load Port Data Sampling(MLPDS), `CVE-2018-12127`
- Microarchitectural Data Sampling Uncacheable Memory(MDSUM), `CVE-2019-11091`
MSBDS는 optimization인 store-to-load forwarding을 통해 dependent load로 speculative forwarding될 수 있는 Store Buffer Entry를 유출합니다. 다른 memory address를 대상으로 fault나 assist가 발생하는 load operation에도 forwarding될 수 있어 특정 조건에서 악용할 수 있습니다. Store buffer는 Hyper-Thread 사이에 분할되어 cross-thread forwarding은 불가능하지만, thread가 sleep state에 들어가거나 나올 때 buffer가 다시 분할되면서 한 thread의 data가 다른 thread에 노출될 수 있습니다.
MFBDS는 Fill Buffer Entry를 유출합니다. Fill buffer는 L1 miss를 관리하고 memory 또는 I/O operation에 응답해 반환하거나 전송할 data를 보관합니다. data를 load operation으로 forwarding하거나 cache에 쓸 수도 있습니다. deallocate된 뒤 이전 operation의 stale data가 남아 faulting 또는 assisting load로 전달될 수 있으며, Fill buffer가 Hyper-Thread 사이에 공유되므로 cross-thread leakage가 가능합니다.
MLPDS는 Load Port Data를 유출합니다. Load port는 memory 또는 I/O에서 load operation을 수행하고 받은 data를 register file이나 다음 operation으로 전달합니다. 일부 구현에서는 이전 operation의 stale data가 남아 faulting 또는 assisting load로 전달될 수 있습니다. Load port도 Hyper-Thread 사이에 공유되므로 cross-thread leakage가 가능합니다.
MDSUM은 MSBDS, MFBDS, MLPDS의 특수 사례입니다. fault 또는 assist가 발생하는 uncacheable memory load가 microarchitectural structure에 data를 남길 수 있고, 나중에 세 변종과 같은 방법으로 이를 관찰할 수 있습니다.
공격 code에 대한 노출 가정
46-69한 가지 예외를 제외하면 attack code가 user space 또는 guest에 있다고 가정합니다. MDS를 악용하는 데 필요한 code construct가 다음 능력을 요구하기 때문입니다.
- fault 또는 assist를 일으키는 load를 제어할 수 있어야 합니다.
- speculative access된 data를 side channel로 소비할 수 있게 노출하는 disclosure gadget이 있어야 합니다.
- disclosure gadget이 data를 노출하는 데 사용하는 pointer를 제어할 수 있어야 합니다.
이러한 construct가 kernel 안에 존재할 가능성을 100% 배제할 수는 없지만, 필요한 복잡도를 고려하면 극히 가능성이 낮습니다.
예외는 untrusted BPF입니다. untrusted BPF의 기능은 제한되어 있지만, 이런 construct를 만드는 데 사용할 수 있는지 철저히 조사해야 합니다.
buffer clearing 완화 전략
70-114적어도 단일 CPU thread, 즉 SMT가 꺼진 경우에는 모든 변종에 같은 완화 전략을 적용합니다. CPU가 영향을 받는 buffer를 강제로 지우게 합니다.
사용되지 않고 obsolete 상태였던 `VERW` instruction과 microcode update를 결합해 이를 구현합니다. update된 microcode는 `VERW`가 실행될 때 영향을 받는 CPU buffer를 지웁니다.
virtualization에서는 수정된 `VERW` 또는 L1D Flush command로 CPU buffer를 지울 수 있습니다. L1TF mitigation이 활성화되어 있으면 L1D Flush가 이미 실행되므로 추가 `VERW`를 피할 수 있습니다. CPU가 L1TF의 영향을 받지 않으면 `VERW`를 실행해야 합니다.
microcode update가 없는 CPU에서 제공된 segment selector argument로 `VERW`를 실행해도, 몇 cycle을 쓸데없이 소비하는 것 외에는 side effect가 없습니다.
이 방식은 cross Hyper-Thread attack을 막지 못합니다. 예외적으로 MSBDS는 한 Hyper-Thread가 C-state에 들어갈 때만 cross Hyper-Thread로 악용할 수 있습니다.
kernel은 buffer clearing을 호출하는 다음 함수를 제공합니다.
x86_clear_cpu_buffers()
exit-to-user path의 후반 ASM에서는 `CLEAR_CPU_BUFFERS` macro도 사용할 수 있습니다. 이 macro는 `CFLAGS.ZF` 외의 register를 clobber하지 않습니다.
완화는 kernel/userspace, hypervisor/guest, C-state(idle) transition에서 호출됩니다.
host에는 update된 microcode가 있지만 hypervisor가 아직 guest에 `MD_CLEAR` CPUID bit를 노출하지 않는 virtualization 상황을 위해 특별한 quirk가 있습니다. kernel은 실제로 buffer가 지워질 가능성을 기대하며 `VERW`를 실행하고, state도 이에 맞춰 표시합니다.
현재 알려진 바로는 유출 data를 노출하는 데 필요한 gadget을 malicious user space나 VM guest가 악용할 수 있게 제어할 수 없으므로 kernel 내부의 추가 완화는 필요하지 않습니다.
kernel 내부 완화 mode
115-135| mode | 동작 |
|---|---|
| `off` | 완화를 비활성화합니다. CPU가 영향을 받지 않거나 kernel command line에 `mds=off`가 지정된 경우입니다. |
| `full` | 완화를 활성화합니다. CPU가 영향을 받고 CPUID가 `MD_CLEAR`를 advertise합니다. |
| `vmwerv` | 완화를 활성화합니다. CPU가 영향을 받지만 CPUID가 `MD_CLEAR`를 advertise하지 않습니다. host microcode는 update되었으나 hypervisor가 CPUID의 `MD_CLEAR`를 노출하지 않는 virtualization 상황을 위한 best-effort 방식이며 보장은 없습니다. |
CPU가 영향을 받고 kernel command line에 `mds=off`가 없으면 kernel은 `MD_CLEAR` CPUID bit의 가용성에 따라 적절한 mitigation mode를 선택합니다.
완화 지점 1: user space 복귀
136-169영향받는 CPU에서 kernel이 user space로 전환할 때, kernel command line에서 완화를 비활성화하지 않았다면 CPU buffer를 flush합니다. 이 완화는 `X86_FEATURE_CLEAR_CPU_BUF` feature flag로 활성화됩니다.
user register를 restore한 뒤 user space로 전환하기 직전에 완화를 호출합니다. `VERW` 이후 NMI 등을 통해 kernel data에 접근할 수 있는 window를 최소화하기 위한 순서입니다.
kernel로 돌아가는 interrupt는 exit-to-user path가 buffer를 지울 것으로 예상하므로 CPU buffer를 지우지 않습니다. 하지만 exit-to-user path가 buffer를 지운 뒤 kernel에서 NMI가 발생하는 corner case는 처리하지 않습니다.
kernel로 돌아가는 NMI가 CPU buffer를 지우지 않는 이유는 다음과 같습니다.
- `VERW` 이후 user space로 돌아가기 전에 NMI가 발생하는 일은 드뭅니다.
- unprivileged user가 이 NMI를 더 자주 발생시키거나 목표 시점에 맞추는 알려진 방법은 없습니다.
- 실제 공격에는 정밀하게 timing된 NMI가 대량으로 필요하며, 충분한 bandwidth가 없을 것으로 봅니다.
- 해당 NMI는 user state가 restore되고 관심 있는 data 대부분이 이미 scrub된 `VERW` 이후에 발생합니다. 남는 것은 NMI가 접근한 data뿐이며, 그 data가 유용할지는 확실하지 않습니다.
완화 지점 2: C-state transition
170-209CPU가 idle 상태가 되어 C-state에 들어갈 때 SMT가 활성화되어 있다면 영향을 받는 CPU의 buffer를 지워야 합니다. 한 Hyper-Thread가 C-state에 들어갈 때 store buffer가 다시 분할되는 문제를 해결합니다.
CPU가 SMT를 지원하지 않거나 sibling thread가 모두 offline이라 SMT가 비활성화된 경우에는 CPU buffer clearing이 필요하지 않습니다.
idle clearing은 MSBDS만의 영향을 받고 다른 MDS 변종에는 영향을 받지 않는 CPU에서 활성화됩니다. 다른 변종의 경우 Fill Buffer와 Load Port가 공유되므로 cross Hyper-Thread attack을 막을 수 없습니다. 따라서 그런 CPU에서 idle clearing은 실질 효과가 없어 활성화하지 않습니다.
호출 여부는 선택한 mitigation mode와 system의 SMT state에 따라 전환되는 static key `cpu_buf_idle_clear`가 제어합니다.
buffer clear는 C-state에 들어가기 직전에만 호출합니다. idle CPU의 stale data가 store buffer 재분할 뒤 모든 entry를 사용할 수 있게 된 non-idle Hyper-Thread sibling으로 흘러가는 것을 막습니다.
idle에서 돌아오면 store buffer가 다시 나뉘어 각 sibling이 절반씩 사용합니다. 깨어난 CPU는 sibling의 내용에 speculative하게 노출될 수 있습니다. user space로 나갈 때 또는 `VMENTER`에서 buffer가 flush되므로 malicious user code나 guest는 이에 speculative access할 수 없습니다.
완화는 모든 `halt()`/`mwait()` 변종에 연결되지만 legacy ACPI I/O-Port 방식은 다루지 않습니다. ACPI idle driver는 2010년 무렵 `intel_idle` driver로 대체되었고, microcode로 `MD_CLEAR` 기능을 받을 것으로 예상되는 모든 영향받는 CPU에서 `intel_idle`이 선호됩니다. I/O-Port 방식은 영향을 받지 않거나 더 이상 microcode update를 받지 않는 오래된 system에서만 사용하는 legacy interface이기도 합니다.
요약과 해설
mds.rst:1-209MDS는 Store Buffer, Fill Buffer, Load Port에 남은 data를 speculative path로 관찰하는 공격 계열입니다. update된 microcode와 `VERW`를 결합해 전환 시 CPU buffer를 비우며, `MD_CLEAR` 노출 여부에 따라 kernel mode가 결정됩니다.