// For flags

CVE-2021-47465

KVM: PPC: Book3S HV: Fix stack handling in idle_kvm_start_guest()

Severity Score

"-"
*CVSS v-

Exploit Likelihood

*EPSS

Affected Versions

*CPE

Public Exploits

0
*Multiple Sources

Exploited in Wild

-
*KEV

Decision

Track
*SSVC
Descriptions

In the Linux kernel, the following vulnerability has been resolved:

KVM: PPC: Book3S HV: Fix stack handling in idle_kvm_start_guest()

In commit 10d91611f426 ("powerpc/64s: Reimplement book3s idle code in
C") kvm_start_guest() became idle_kvm_start_guest(). The old code
allocated a stack frame on the emergency stack, but didn't use the
frame to store anything, and also didn't store anything in its caller's
frame.

idle_kvm_start_guest() on the other hand is written more like a normal C
function, it creates a frame on entry, and also stores CR/LR into its
callers frame (per the ABI). The problem is that there is no caller
frame on the emergency stack.

The emergency stack for a given CPU is allocated with:

paca_ptrs[i]->emergency_sp = alloc_stack(limit, i) + THREAD_SIZE;

So emergency_sp actually points to the first address above the emergency
stack allocation for a given CPU, we must not store above it without
first decrementing it to create a frame. This is different to the
regular kernel stack, paca->kstack, which is initialised to point at an
initial frame that is ready to use.

idle_kvm_start_guest() stores the backchain, CR and LR all of which
write outside the allocation for the emergency stack. It then creates a
stack frame and saves the non-volatile registers. Unfortunately the
frame it creates is not large enough to fit the non-volatiles, and so
the saving of the non-volatile registers also writes outside the
emergency stack allocation.

The end result is that we corrupt whatever is at 0-24 bytes, and 112-248
bytes above the emergency stack allocation.

In practice this has gone unnoticed because the memory immediately above
the emergency stack happens to be used for other stack allocations,
either another CPUs mc_emergency_sp or an IRQ stack. See the order of
calls to irqstack_early_init() and emergency_stack_init().

The low addresses of another stack are the top of that stack, and so are
only used if that stack is under extreme pressue, which essentially
never happens in practice - and if it did there's a high likelyhood we'd
crash due to that stack overflowing.

Still, we shouldn't be corrupting someone else's stack, and it is purely
luck that we aren't corrupting something else.

To fix it we save CR/LR into the caller's frame using the existing r1 on
entry, we then create a SWITCH_FRAME_SIZE frame (which has space for
pt_regs) on the emergency stack with the backchain pointing to the
existing stack, and then finally we switch to the new frame on the
emergency stack.

En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: KVM: PPC: Book3S HV: Arreglar el manejo de la pila en idle_kvm_start_guest() En el commit 10d91611f426 ("powerpc/64s: Reimplementar el código inactivo de book3s en C") kvm_start_guest() se convirtió en idle_kvm_start_guest() . El código antiguo asignaba un marco de pila en la pila de emergencia, pero no usaba el marco para almacenar nada y tampoco almacenaba nada en el marco de la persona que llama. idle_kvm_start_guest(), por otro lado, se escribe más como una función C normal, crea un marco al ingresar y también almacena CR/LR en el marco de la persona que llama (según la ABI). El problema es que no hay ningún marco de llamada en la pila de emergencia. La pila de emergencia para una CPU determinada se asigna con: paca_ptrs[i]->emergency_sp = alloc_stack(limit, i) + THREAD_SIZE; Entonces, Emergency_sp en realidad apunta a la primera dirección encima de la asignación de pila de emergencia para una CPU determinada; no debemos almacenar encima de ella sin primero disminuirla para crear un marco. Esto es diferente a la pila normal del kernel, paca->kstack, que se inicializa para apuntar a un marco inicial que está listo para usar. idle_kvm_start_guest() almacena la cadena posterior, CR y LR, todos los cuales escriben fuera de la asignación para la pila de emergencia. Luego crea un marco de pila y guarda los registros no volátiles. Desafortunadamente, el marco que crea no es lo suficientemente grande para acomodar los registros no volátiles, por lo que guardar los registros no volátiles también escribe fuera de la asignación de pila de emergencia. El resultado final es que corrompemos todo lo que esté entre 0 y 24 bytes y entre 112 y 248 bytes por encima de la asignación de pila de emergencia. En la práctica, esto ha pasado desapercibido porque la memoria inmediatamente encima de la pila de emergencia se usa para otras asignaciones de pila, ya sea otra CPU mc_emergency_sp o una pila IRQ. Vea el orden de las llamadas a irqstack_early_init() y Emergency_stack_init(). Las direcciones bajas de otra pila están en la parte superior de esa pila, por lo que solo se usan si esa pila está bajo una presión extrema, lo que esencialmente nunca sucede en la práctica, y si así fuera, existe una alta probabilidad de que fallemos debido a que esa pila se desborde. . Aún así, no deberíamos estar corrompiendo la pila de otra persona, y es pura suerte que no estemos corrompiendo algo más. Para solucionarlo, guardamos CR/LR en el marco de la persona que llama usando el r1 existente en la entrada, luego creamos un marco SWITCH_FRAME_SIZE (que tiene espacio para pt_regs) en la pila de emergencia con la cadena posterior apuntando a la pila existente, y finalmente cambiamos al nuevo marco en la pila de emergencia.

*Credits: N/A
CVSS Scores
Attack Vector
-
Attack Complexity
-
Privileges Required
-
User Interaction
-
Scope
-
Confidentiality
-
Integrity
-
Availability
-
* Common Vulnerability Scoring System
SSVC
  • Decision:Track
Exploitation
None
Automatable
No
Tech. Impact
Partial
* Organization's Worst-case Scenario
Timeline
  • 2024-05-22 CVE Reserved
  • 2024-05-22 CVE Published
  • 2024-05-22 EPSS Updated
  • 2024-08-04 CVE Updated
  • ---------- Exploited in Wild
  • ---------- KEV Due Date
  • ---------- First Exploit
CWE
CAPEC
Affected Vendors, Products, and Versions
Vendor Product Version Other Status
Vendor Product Version Other Status <-- --> Vendor Product Version Other Status
Linux
Search vendor "Linux"
Linux Kernel
Search vendor "Linux" for product "Linux Kernel"
>= 5.2 < 5.4.156
Search vendor "Linux" for product "Linux Kernel" and version " >= 5.2 < 5.4.156"
en
Affected
Linux
Search vendor "Linux"
Linux Kernel
Search vendor "Linux" for product "Linux Kernel"
>= 5.2 < 5.10.76
Search vendor "Linux" for product "Linux Kernel" and version " >= 5.2 < 5.10.76"
en
Affected
Linux
Search vendor "Linux"
Linux Kernel
Search vendor "Linux" for product "Linux Kernel"
>= 5.2 < 5.14.15
Search vendor "Linux" for product "Linux Kernel" and version " >= 5.2 < 5.14.15"
en
Affected
Linux
Search vendor "Linux"
Linux Kernel
Search vendor "Linux" for product "Linux Kernel"
>= 5.2 < 5.15
Search vendor "Linux" for product "Linux Kernel" and version " >= 5.2 < 5.15"
en
Affected