* [PATCH v5 01/13] KVM: s390: Fix unlikely NULL gmap dereference
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 15:43 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma() Claudio Imbrenda
` (11 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
When creating a new vCPU, kvm_vm_ioctl_create_vcpu() will call
kvm_arch_vcpu_postcreate() after the file descriptor for the new vCPU
has been created. The new file descriptor has not been returned yet,
but a malicious userspace program could try to guess it.
If a malicious userspace program manages to start the newly created vCPU
before kvm_arch_vcpu_postcreate() is called, __vcpu_run() will try to
dereference vcpu->arch.gmap and trigger a NULL pointer dereference.
Fix this by adding a new field to struct kvm_vcpu_arch to keep track of
the initialization status of the vCPU. Refuse to run a vCPU that is not
fully initialized.
Fixes: dafd032a15f8 ("KVM: s390: move vcpu specific initalization to a later point")
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com>
---
arch/s390/include/asm/kvm_host.h | 1 +
arch/s390/kvm/kvm-s390.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index eaa34c5bd3c1..edf75b6ad20c 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -440,6 +440,7 @@ struct kvm_vcpu_arch {
bool skey_enabled;
/* Indicator if the access registers have been loaded from guest */
bool acrs_loaded;
+ bool initialized;
struct kvm_s390_pv_vcpu pv;
union diag318_info diag318_info;
struct kvm_s390_mmu_cache *mc;
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 150b5dd2170e..f86b4b0b356f 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3613,6 +3613,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
if (test_kvm_facility(vcpu->kvm, 74) || vcpu->kvm->arch.user_instr0 ||
vcpu->kvm->arch.user_operexec)
vcpu->arch.sie_block->ictl |= ICTL_OPEREXC;
+
+ /* Pairs with smp_load_acquire() in kvm_arch_vcpu_ioctl_run() and kvm_arch_vcpu_ioctl() */
+ smp_store_release(&vcpu->arch.initialized, true);
}
static bool kvm_has_pckmo_subfunc(struct kvm *kvm, unsigned long nr)
@@ -5039,6 +5042,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
kvm_run->kvm_dirty_regs & ~KVM_SYNC_S390_VALID_FIELDS)
return -EINVAL;
+ /* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
+ if (!smp_load_acquire(&vcpu->arch.initialized))
+ return -EINVAL;
+
vcpu_load(vcpu);
if (guestdbg_exit_pending(vcpu)) {
@@ -5523,6 +5530,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
long r;
u16 rc, rrc;
+ /* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
+ if (!smp_load_acquire(&vcpu->arch.initialized))
+ return -EINVAL;
+
vcpu_load(vcpu);
switch (ioctl) {
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 01/13] KVM: s390: Fix unlikely NULL gmap dereference
2026-07-29 15:29 ` [PATCH v5 01/13] KVM: s390: Fix unlikely NULL gmap dereference Claudio Imbrenda
@ 2026-07-29 15:43 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 15:43 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: Alexander Gordeev, Heiko Carstens, Vasily Gorbik, kvm, linux-s390
> When creating a new vCPU, kvm_vm_ioctl_create_vcpu() will call
> kvm_arch_vcpu_postcreate() after the file descriptor for the new vCPU
> has been created. The new file descriptor has not been returned yet,
> but a malicious userspace program could try to guess it.
>
> If a malicious userspace program manages to start the newly created vCPU
> before kvm_arch_vcpu_postcreate() is called, __vcpu_run() will try to
> dereference vcpu->arch.gmap and trigger a NULL pointer dereference.
>
> Fix this by adding a new field to struct kvm_vcpu_arch to keep track of
> the initialization status of the vCPU. Refuse to run a vCPU that is not
> fully initialized.
>
> Fixes: dafd032a15f8 ("KVM: s390: move vcpu specific initalization to a later point")
> Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma()
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
2026-07-29 15:29 ` [PATCH v5 01/13] KVM: s390: Fix unlikely NULL gmap dereference Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 16:06 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error Claudio Imbrenda
` (10 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
In some cases kvm_s390_vcpu_unsetup_cmma() can be called with a 0
cbrlo; in such cases, if running with V != R, free_page() will attempt
to free physical page 0.
Fix by freeing cbrlo only if it's non-zero.
Fixes: b31605c12f4e ("KVM: s390: make cmma usage conditionally")
Fixes: 29b40f105ec8 ("KVM: s390: protvirt: Add initial vm and cpu lifecycle handling")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index f86b4b0b356f..9ec5833a0973 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3677,7 +3677,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
{
- free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
+ if (vcpu->arch.sie_block->cbrlo)
+ free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
vcpu->arch.sie_block->cbrlo = 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma()
2026-07-29 15:29 ` [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma() Claudio Imbrenda
@ 2026-07-29 16:06 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 16:06 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: kvm, Vasily Gorbik, Heiko Carstens, linux-s390, Alexander Gordeev
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The struct kvm_s390_mmu_cache allocated in kvm_arch_vcpu_create() is leaked on error paths.
--
--- Patch [2]: [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma() ---
commit 0d3b94e982f008ae0ede697fd75113aea3b668c3
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma()
In some cases kvm_s390_vcpu_unsetup_cmma() can be called with a 0 cbrlo.
This commit fixes an issue where running with V != R causes free_page() to
attempt freeing physical page 0 by ensuring cbrlo is only freed if it is
non-zero.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index 2fe2e46b7ac39..096b426c1281b 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
> @@ -3675,7 +3675,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
>
> void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
> {
> - free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
> + if (vcpu->arch.sie_block->cbrlo)
> + free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
> vcpu->arch.sie_block->cbrlo = 0;
> }
This is a pre-existing issue, but while reviewing this file I noticed a
memory leak in kvm_arch_vcpu_create().
When a subsequent initialization step inside kvm_arch_vcpu_create() fails,
the function jumps to the out_free_sie_block label:
arch/s390/kvm/kvm-s390.c:kvm_arch_vcpu_create() {
...
vcpu->arch.mc = kvm_s390_new_mmu_cache();
if (!vcpu->arch.mc)
return -ENOMEM;
...
out_free_sie_block:
free_page((unsigned long)(vcpu->arch.sie_block));
return rc;
}
[Severity: High]
Does this error path permanently leak the struct kvm_s390_mmu_cache
allocated earlier in the function, since vcpu->arch.mc is not freed here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
2026-07-29 15:29 ` [PATCH v5 01/13] KVM: s390: Fix unlikely NULL gmap dereference Claudio Imbrenda
2026-07-29 15:29 ` [PATCH v5 02/13] KVM: s390: Fix kvm_s390_vcpu_unsetup_cmma() Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 16:35 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call() Claudio Imbrenda
` (9 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
If an attempt is made to create a vCPU with an already existing ID,
the duplicated vCPU will be destroyed. When destroying a vCPU, its
ESCA entry will be cleared. In the above scenario, the spurious
duplicate vCPU is destroyed, but the ESCA entry corresponding to the
original vCPU is cleared.
Fix by skipping clearing the ESCA entry if the vCPU creation was not
successful, i.e. if the pointer to the ESCA in the state description is
not set.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
---
arch/s390/kvm/interrupt.c | 2 +-
arch/s390/kvm/kvm-s390.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 9e3e6b0d72ad..2acdff130fa6 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -86,7 +86,7 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
struct esca_block *sca = vcpu->kvm->arch.sca;
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
- if (!kvm_s390_use_sca_entries())
+ if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
return;
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 9ec5833a0973..58cec3703c2d 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3461,7 +3461,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu)
{
struct esca_block *sca = vcpu->kvm->arch.sca;
- if (!kvm_s390_use_sca_entries())
+ if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
return;
clear_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error
2026-07-29 15:29 ` [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error Claudio Imbrenda
@ 2026-07-29 16:35 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 16:35 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: linux-s390, kvm, Heiko Carstens, Alexander Gordeev, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Creating and destroying a KVM_VM_S390_UCONTROL VM with vCPU IDs >= KVM_S390_ESCA_CPU_SLOTS (248) causes out-of-bounds memory accesses on `esca_block->cpu`.
- [High] gmap_remove_child() is called without holding the parent gmap's children_lock in the error path of kvm_arch_vcpu_create().
--
--- Patch [3]: [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error ---
commit de5b0e7f34127f8db5eb8b0fa184043139c25a8c
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: Fix overclearing ESCA in case of error
If an attempt is made to create a vCPU with an already existing ID,
the duplicated vCPU is destroyed. This patch ensures the ESCA entry
is only cleared if the vCPU was successfully created to avoid
clearing the original vCPU's ESCA entry by mistake.
> diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
> index 9e3e6b0d72ad6..2acdff130fa6c 100644
> --- a/arch/s390/kvm/interrupt.c
> +++ b/arch/s390/kvm/interrupt.c
> @@ -86,7 +86,7 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
> struct esca_block *sca = vcpu->kvm->arch.sca;
> union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
>
> - if (!kvm_s390_use_sca_entries())
> + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
> return;
[Severity: Critical]
This is a pre-existing issue, but can this out-of-bounds access still occur
for UCONTROL VMs?
In kvm_arch_vcpu_precreate(), the sca_can_add_vcpu() bounds check is skipped
for UCONTROL VMs, which allows vcpu_id to be up to KVM_MAX_VCPUS - 1 (254):
arch/s390/kvm/kvm-s390.c:kvm_arch_vcpu_precreate() {
...
if (!kvm_is_ucontrol(kvm) && !sca_can_add_vcpu(kvm, id))
return -EINVAL;
...
}
Because the esca_block->cpu array only has KVM_S390_ESCA_CPU_SLOTS (248)
elements, if a UCONTROL vCPU with an ID between 248 and 254 successfully
initializes, it would bypass the new !vcpu->arch.initialized check here.
When this vCPU is destroyed in sca_clear_ext_call() or handles an external call
interrupt in sca_inject_ext_call(), computing &sca->cpu[vcpu->vcpu_id] results
in a pointer outside the allocated esca_block.
Could this allow writing out of bounds when WRITE_ONCE(sigp_ctrl->value, 0) is
called?
> kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
>
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index 096b426c1281b..3e40a52bfd152 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
> @@ -3459,7 +3459,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu)
> {
> struct esca_block *sca = vcpu->kvm->arch.sca;
>
> - if (!kvm_s390_use_sca_entries())
> + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
> return;
>
> clear_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
[Severity: High]
This isn't a bug introduced by this patch, but is there a data race in the
error handling path of kvm_arch_vcpu_create() for UCONTROL VMs?
If kvm_s390_vcpu_setup() fails, the code jumps to out_ucontrol_uninit:
arch/s390/kvm/kvm-s390.c:kvm_arch_vcpu_create() {
...
out_ucontrol_uninit:
if (kvm_is_ucontrol(vcpu->kvm)) {
gmap_remove_child(vcpu->arch.gmap);
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
}
...
}
Because kvm->lock is released prior to calling kvm_arch_vcpu_create(), multiple
concurrent vCPU creations could fail and execute this error path simultaneously.
Since gmap_remove_child() mutates the parent gmap's children list without
holding the children_lock, could this lead to list corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call()
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (2 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 03/13] KVM: s390: Fix overclearing ESCA in case of error Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 16:55 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace Claudio Imbrenda
` (8 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
When cleaning up a UCONTROL VM, sca_clear_ext_call() will touch memory
outside of the allocated ESCA block, and UCONTROL VMs don't even use
ESCA.
Fix by not touching ESCA for UCONTROL VMs.
Opportunistically add checks in sca_ext_call_pending() and
sca_inject_ext_call() to make sure UCONTROL VMs won't call those
functions.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: 7d43bafcff17 ("KVM: s390: Make provisions for ESCA utilization")
---
arch/s390/kvm/interrupt.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 2acdff130fa6..d02724bd1829 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -50,6 +50,8 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
return 0;
+ if (KVM_BUG_ON(kvm_is_ucontrol(vcpu->kvm), vcpu->kvm))
+ return -EINVAL;
BUG_ON(!kvm_s390_use_sca_entries());
if (src_id)
@@ -66,6 +68,8 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
int expect, rc;
BUG_ON(!kvm_s390_use_sca_entries());
+ if (KVM_BUG_ON(kvm_is_ucontrol(vcpu->kvm), vcpu->kvm))
+ return -EINVAL;
old_val = READ_ONCE(*sigp_ctrl);
old_val.c = 0;
@@ -84,10 +88,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
{
struct esca_block *sca = vcpu->kvm->arch.sca;
- union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
+ union esca_sigp_ctrl *sigp_ctrl;
- if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
+ if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized || kvm_is_ucontrol(vcpu->kvm))
return;
+
+ /* Initialize after the above check, to prevent going out of bounds */
+ sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
WRITE_ONCE(sigp_ctrl->value, 0);
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call()
2026-07-29 15:29 ` [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call() Claudio Imbrenda
@ 2026-07-29 16:55 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 16:55 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: Vasily Gorbik, kvm, Alexander Gordeev, linux-s390, Heiko Carstens
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The newly added `KVM_BUG_ON` checks for UCONTROL VMs violate KVM subsystem guidelines against userspace-reachable assertions, creating a Denial of Service vulnerability.
Pre-existing issues:
- [Medium] Incomplete fix leaves an out-of-bounds memory read in `sca_ext_call_pending()` for UCONTROL VMs.
- [Low] Inconsistent fix leaves an out-of-bounds pointer calculation in `sca_inject_ext_call()` for UCONTROL VMs.
--
--- Patch [4]: [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call() ---
commit 497994f9fead637806bdf9fa1fbe4ded72deee52
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: ucontrol: Fix sca_clear_ext_call()
When cleaning up a UCONTROL VM, sca_clear_ext_call() will touch memory
outside of the allocated ESCA block, and UCONTROL VMs don't even use
ESCA. Fix by not touching ESCA for UCONTROL VMs, and opportunistically
add checks in sca_ext_call_pending() and sca_inject_ext_call().
> diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
> index 2acdff130fa6c..d02724bd1829c 100644
> --- a/arch/s390/kvm/interrupt.c
> +++ b/arch/s390/kvm/interrupt.c
> @@ -50,6 +50,8 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
> if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
> return 0;
>
> + if (KVM_BUG_ON(kvm_is_ucontrol(vcpu->kvm), vcpu->kvm))
> + return -EINVAL;
[Severity: Medium]
This is a pre-existing issue, but does this function still perform an
out-of-bounds memory read before we even reach this new check?
At the top of sca_ext_call_pending(), sigp_ctrl is initialized by value:
static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
{
struct esca_block *sca = vcpu->kvm->arch.sca;
union esca_sigp_ctrl sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
For a UCONTROL VM, vcpu_id can be up to 254, but the ESCA array only has
248 slots. Does this cause an unconditional out-of-bounds memory read when
entering the function?
[Severity: High]
Could this KVM_BUG_ON check be triggered by host userspace?
According to the KVM subsystem guidelines, userspace-reachable BUG_ON or
WARN_ON calls act as a denial of service. If a host userspace process invokes
the KVM_S390_INTERRUPT ioctl to inject a KVM_S390_INT_EXTERNAL_CALL into a
UCONTROL VM, wouldn't this wedge the VM and potentially trigger a panic?
> BUG_ON(!kvm_s390_use_sca_entries());
>
> if (src_id)
[ ... ]
> @@ -66,6 +68,8 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
> int expect, rc;
>
> BUG_ON(!kvm_s390_use_sca_entries());
> + if (KVM_BUG_ON(kvm_is_ucontrol(vcpu->kvm), vcpu->kvm))
> + return -EINVAL;
[Severity: High]
Similar to the check in sca_ext_call_pending() above, is this KVM_BUG_ON
reachable from host userspace and acting as a denial of service?
[Severity: Low]
This isn't a bug introduced by this patch, but is the pointer calculation
for sigp_ctrl at the top of sca_inject_ext_call() also occurring before
this new check?
static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
{
struct esca_block *sca = vcpu->kvm->arch.sca;
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
...
While it is just an address calculation, for UCONTROL VMs it calculates an
out-of-bounds pointer. Could this be moved after the check, similar to how
it was fixed in sca_clear_ext_call() below?
>
> old_val = READ_ONCE(*sigp_ctrl);
> old_val.c = 0;
[ ... ]
> @@ -84,10 +88,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
> static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
> {
> struct esca_block *sca = vcpu->kvm->arch.sca;
> - union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
> + union esca_sigp_ctrl *sigp_ctrl;
>
> - if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
> + if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized || kvm_is_ucontrol(vcpu->kvm))
> return;
> +
> + /* Initialize after the above check, to prevent going out of bounds */
> + sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
> kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
>
> WRITE_ONCE(sigp_ctrl->value, 0);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=4
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (3 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 04/13] KVM: s390: ucontrol: Fix sca_clear_ext_call() Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 17:08 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 06/13] KVM: s390: Fix race in __do_essa() Claudio Imbrenda
` (7 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
If kvm_s390_set_cmma_bits() is asked to set CMMA values outside of a
memslot, PGM_ADDRESSING (5) is returned, instead of a negative error
value.
Same issue with kvm_s390_{g,s}et_skeys(), kvm_s390_keyop(), and
dat_reset_reference_bit().
Fix by returning -EFAULT whenever the return value would be > 0, which
is consistent with the behaviour before the gmap rewrite.
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/dat.c | 16 ++++++++++------
arch/s390/kvm/dat.h | 2 +-
arch/s390/kvm/kvm-s390.c | 16 ++++++++--------
arch/s390/kvm/priv.c | 5 +++--
4 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c
index ed4259d17629..171b61959908 100644
--- a/arch/s390/kvm/dat.c
+++ b/arch/s390/kvm/dat.c
@@ -755,13 +755,15 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
return rc;
}
-int dat_reset_reference_bit(union asce asce, gfn_t gfn)
+int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
{
union pgste pgste, old;
union crste *crstep;
union pte *ptep;
int rc;
+ skey->skey = 0;
+
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
if (rc)
return rc;
@@ -771,21 +773,23 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn)
if (!crste.h.fc || !crste.s.fc1.pr)
return 0;
- return page_reset_referenced(large_crste_to_phys(*crstep, gfn));
+ skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
+ return 0;
}
old = pgste_get_lock(ptep);
pgste = old;
if (!ptep->h.i) {
- rc = page_reset_referenced(pte_origin(*ptep));
- pgste.hr = rc >> 1;
+ skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1;
+ pgste.hr = skey->r;
}
- rc |= (pgste.gr << 1) | pgste.gc;
+ skey->r |= pgste.gr;
+ skey->c |= pgste.gc;
pgste.gr = 0;
dat_update_ptep_sd(old, pgste, ptep);
pgste_set_unlock(ptep, pgste);
- return rc;
+ return 0;
}
static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
diff --git a/arch/s390/kvm/dat.h b/arch/s390/kvm/dat.h
index fad605305e05..141ee7b9f019 100644
--- a/arch/s390/kvm/dat.h
+++ b/arch/s390/kvm/dat.h
@@ -537,7 +537,7 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf
union skey skey, bool nq);
int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
union skey skey, union skey *oldkey, bool nq, bool mr, bool mc);
-int dat_reset_reference_bit(union asce asce, gfn_t gfn);
+int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey);
long dat_reset_skeys(union asce asce, gfn_t start);
unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 58cec3703c2d..39626649901d 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -571,7 +571,7 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op
switch (op) {
case KVM_S390_KEYOP_SSKE:
r = dat_cond_set_storage_key(mc, asce, gfn, skey, &skey, 0, 0, 0);
- if (r >= 0)
+ if (r == 0 || r == 1)
return skey.skey;
break;
case KVM_S390_KEYOP_ISKE:
@@ -580,14 +580,14 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op
return skey.skey;
break;
case KVM_S390_KEYOP_RRBE:
- r = dat_reset_reference_bit(asce, gfn);
- if (r > 0)
- return r << 1;
+ r = dat_reset_reference_bit(asce, gfn, &skey);
+ if (!r)
+ return skey.skey;
break;
default:
return -EINVAL;
}
- return r;
+ return r > 0 ? -EFAULT : r;
}
/* Section: device related */
@@ -2214,7 +2214,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
}
kvfree(keys);
- return r;
+ return r <= 0 ? r : -EFAULT;
}
static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
@@ -2276,7 +2276,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
kvm_s390_free_mmu_cache(mc);
out:
kvfree(keys);
- return r;
+ return r <= 0 ? r : -EFAULT;
}
/*
@@ -2386,7 +2386,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
set_bit(GMAP_FLAG_USES_CMM, &kvm->arch.gmap->flags);
- return r;
+ return r <= 0 ? r : -EFAULT;
}
/**
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index ad0ddc433a73..ea5a99537346 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -289,6 +289,7 @@ static int handle_iske(struct kvm_vcpu *vcpu)
static int handle_rrbe(struct kvm_vcpu *vcpu)
{
unsigned long gaddr;
+ union skey skey;
int reg1, reg2;
int rc;
@@ -307,12 +308,12 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
- rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr));
+ rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr), &skey);
if (rc > 0)
return kvm_s390_inject_program_int(vcpu, rc);
if (rc < 0)
return rc;
- kvm_s390_set_psw_cc(vcpu, rc);
+ kvm_s390_set_psw_cc(vcpu, (skey.skey >> 1) & 3);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace
2026-07-29 15:29 ` [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace Claudio Imbrenda
@ 2026-07-29 17:08 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 17:08 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: kvm, linux-s390, Vasily Gorbik, Alexander Gordeev, Heiko Carstens
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] O(N^2) execution and potential soft lockup in `kvm_s390_set_skeys` and `kvm_s390_set_cmma_bits` due to restarting the iteration from `0` on every MMU cache top-up.
--
--- Patch [5]: [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace ---
commit 89e2ddf835cf981fcd7459eaa6f4853002045254
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: Fix leaking of PGM_ADDRESSING to userspace
This commit fixes KVM operations like kvm_s390_set_skeys and
kvm_s390_set_cmma_bits by converting positive error codes (such as
PGM_ADDRESSING) to -EFAULT. This ensures negative error values are properly
returned to userspace.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
[ ... ]
> @@ -2274,7 +2274,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
> kvm_s390_free_mmu_cache(mc);
> out:
> kvfree(keys);
> - return r;
> + return r <= 0 ? r : -EFAULT;
> }
[Severity: High]
This is a pre-existing issue, but does the iteration in kvm_s390_set_skeys()
risk a soft lockup?
If dat_set_storage_key() returns -ENOMEM because the MMU cache is depleted,
the outer do-while loop tops up the cache and retries. However, the inner
for-loop unconditionally restarts from i = 0:
scoped_guard(read_lock, &kvm->mmu_lock) {
for (i = 0 ; i < args->count; i++) {
r = dat_set_storage_key(mc, kvm->arch.gmap->asce,
args->start_gfn + i, keys[i], 0);
If userspace passes a large count (up to KVM_S390_SKEYS_MAX), restarting from
0 on every page table allocation could result in O(N^2) execution time.
Since this is done while holding the mmu_lock with preemption disabled, could
this trigger RCU stalls or kernel panics?
[ ... ]
> @@ -2384,7 +2384,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
>
> set_bit(GMAP_FLAG_USES_CMM, &kvm->arch.gmap->flags);
>
> - return r;
> + return r <= 0 ? r : -EFAULT;
> }
[Severity: High]
This is also a pre-existing issue, but does kvm_s390_set_cmma_bits() have
the same quadratic restart problem?
Similar to the skeys function above, if dat_set_cmma_bits() returns -ENOMEM,
the loop tops up the cache and restarts processing from args->start_gfn:
do {
r = kvm_s390_mmu_cache_topup(mc);
if (r)
return r;
scoped_guard(read_lock, &kvm->mmu_lock) {
r = dat_set_cmma_bits(mc, kvm->arch.gmap->asce, args->start_gfn,
args->count, args->mask, bits);
}
} while (r == -ENOMEM);
Should these loops advance the starting address/index so they resume where
they left off instead of starting over?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 06/13] KVM: s390: Fix race in __do_essa()
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (4 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 05/13] KVM: s390: Fix leaking of PGM_ADDRESSING to userspace Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 17:22 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot Claudio Imbrenda
` (6 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
An unlikely race between __do_essa() and kvm_s390_vm_start_migration(),
kvm_s390_vm_stop_migration(), or dat_get_cmma() was possible.
Fix by locking kvm->slots_arch_lock. Since this is not a hot path, the
overhead of an additional mutex is negligible.
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 20 ++++++++++----------
arch/s390/kvm/priv.c | 5 +++--
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 39626649901d..80036b8a1f31 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1219,8 +1219,8 @@ static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req)
/*
* Must be called with kvm->srcu held to avoid races on memslots, and with
- * kvm->slots_lock to avoid races with ourselves, kvm_s390_vm_stop_migration(),
- * and kvm_s390_get_cmma_bits().
+ * kvm->slots_arch_lock to avoid races with ourselves,
+ * kvm_s390_vm_stop_migration(), and kvm_s390_get_cmma_bits().
*/
static int kvm_s390_vm_start_migration(struct kvm *kvm)
{
@@ -1265,7 +1265,7 @@ static int kvm_s390_vm_start_migration(struct kvm *kvm)
}
/*
- * Must be called with kvm->slots_lock to avoid races with ourselves,
+ * Must be called with kvm->slots_arch_lock to avoid races with ourselves,
* kvm_s390_vm_start_migration() and kvm_s390_get_cmma_bits().
*/
static int kvm_s390_vm_stop_migration(struct kvm *kvm)
@@ -1300,7 +1300,9 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
{
int res = -ENXIO;
- mutex_lock(&kvm->slots_lock);
+ guard(srcu)(&kvm->srcu);
+ guard(mutex)(&kvm->slots_arch_lock);
+
switch (attr->attr) {
case KVM_S390_VM_MIGRATION_START:
res = kvm_s390_vm_start_migration(kvm);
@@ -1311,7 +1313,6 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
default:
break;
}
- mutex_unlock(&kvm->slots_lock);
return res;
}
@@ -2998,9 +2999,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
r = -EFAULT;
if (copy_from_user(&args, argp, sizeof(args)))
break;
- mutex_lock(&kvm->slots_lock);
- r = kvm_s390_get_cmma_bits(kvm, &args);
- mutex_unlock(&kvm->slots_lock);
+ scoped_guard(mutex, &kvm->slots_arch_lock)
+ r = kvm_s390_get_cmma_bits(kvm, &args);
if (!r) {
r = copy_to_user(argp, &args, sizeof(args));
if (r)
@@ -3014,9 +3014,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
r = -EFAULT;
if (copy_from_user(&args, argp, sizeof(args)))
break;
- mutex_lock(&kvm->slots_lock);
+ mutex_lock(&kvm->slots_arch_lock);
r = kvm_s390_set_cmma_bits(kvm, &args);
- mutex_unlock(&kvm->slots_lock);
+ mutex_unlock(&kvm->slots_arch_lock);
break;
}
case KVM_S390_PV_COMMAND: {
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index ea5a99537346..b1ba24c346ef 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -1261,8 +1261,9 @@ static int handle_essa(struct kvm_vcpu *vcpu)
/* Retry the ESSA instruction */
kvm_s390_retry_instr(vcpu);
} else {
- scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
- i = __do_essa(vcpu, orc);
+ scoped_guard(mutex, &vcpu->kvm->slots_arch_lock)
+ scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
+ i = __do_essa(vcpu, orc);
if (i < 0)
return i;
/* Account for the possible extra cbrl entry */
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (5 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 06/13] KVM: s390: Fix race in __do_essa() Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 17:38 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child() Claudio Imbrenda
` (5 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
When a memslot is removed, all ptes that mapped the slot are cleared or
even deallocated. If this happens while the system is in migration
mode, and if cmma-dirty pages are removed, the cmma-dirty counter will
not reflect reality.
Fix by appropriately decrementing the cmma-dirty counter when removing
a memslot.
Opportunistically improve kvm_arch_commit_memory_region() to use
__free() for the struct kvm_s390_mmu_cache.
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/dat.c | 7 ++++++-
arch/s390/kvm/kvm-s390.c | 25 +++++++++++++++++++++++--
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c
index 171b61959908..3f2d6e8902d7 100644
--- a/arch/s390/kvm/dat.c
+++ b/arch/s390/kvm/dat.c
@@ -850,6 +850,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
struct slot_priv *p = walk->priv;
union crste dummy = { .val = p->token };
union pte new_pte, pte = READ_ONCE(*ptep);
+ union pgste pgste;
new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
@@ -857,7 +858,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
if (pte.val == new_pte.val)
return 0;
- dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
+ pgste = pgste_get_lock(ptep);
+ pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false);
+ pgste.cmma_d = 0;
+ pgste_set_unlock(ptep, pgste);
+
return 0;
}
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 80036b8a1f31..3e7e5cfea181 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -5806,14 +5806,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
return 0;
}
+static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
+{
+ union pgste pgste;
+
+ pgste = pgste_get_lock(ptep);
+ if (pgste.cmma_d) {
+ pgste.cmma_d = 0;
+ atomic64_dec(walk->priv);
+ }
+ pgste_set_unlock(ptep, pgste);
+ return 0;
+}
+
void kvm_arch_commit_memory_region(struct kvm *kvm,
struct kvm_memory_slot *old,
const struct kvm_memory_slot *new,
enum kvm_mr_change change)
{
- struct kvm_s390_mmu_cache *mc = NULL;
+ const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
+ struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
int rc = 0;
+ guard(mutex)(&kvm->slots_arch_lock);
+
if (change == KVM_MR_FLAGS_ONLY)
return;
@@ -5824,6 +5840,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
}
scoped_guard(write_lock, &kvm->mmu_lock) {
+ if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
+ _dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
+ kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES,
+ &kvm->arch.cmma_dirty_pages);
+ }
+
switch (change) {
case KVM_MR_DELETE:
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
@@ -5845,7 +5867,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
out:
if (rc)
pr_warn("failed to commit memory region\n");
- kvm_s390_free_mmu_cache(mc);
return;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot
2026-07-29 15:29 ` [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot Claudio Imbrenda
@ 2026-07-29 17:38 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 17:38 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: kvm, Heiko Carstens, Vasily Gorbik, Alexander Gordeev, linux-s390
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Missing `DAT_WALK_ANY` flag skips swapped-out pages, leaking the CMMA dirty counter.
Pre-existing issues:
- [High] Silent failure in `kvm_arch_commit_memory_region` leaves KVM memslots and s390 DAT tables out of sync.
--
--- Patch [7]: [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot ---
commit 209a02966d18973e5f9b2678eee5bc92d7952603
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: cmma: Fix dirty tracking when removing memslot
When a memslot is removed, all ptes that mapped the slot are cleared or
even deallocated. If this happens while the system is in migration
mode, and if cmma-dirty pages are removed, the cmma-dirty counter will
not reflect reality.
Fix by appropriately decrementing the cmma-dirty counter when removing
a memslot.
Opportunistically improve kvm_arch_commit_memory_region() to use
__free() for the struct kvm_s390_mmu_cache.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index a72ffd515c866..2ae431030621e 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
[ ... ]
> @@ -5804,14 +5804,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
> return 0;
> }
>
> +static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
[ ... ]
> void kvm_arch_commit_memory_region(struct kvm *kvm,
> struct kvm_memory_slot *old,
> const struct kvm_memory_slot *new,
> enum kvm_mr_change change)
> {
> - struct kvm_s390_mmu_cache *mc = NULL;
> + const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
> + struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
[Severity: High]
This is a pre-existing issue, but if kvm_s390_new_mmu_cache() fails to
allocate memory and returns NULL later in this function, doesn't it
silently skip updating the DAT tables?
Since kvm_arch_commit_memory_region() returns void, KVM assumes the changes
are applied infallibly. Could this leave KVM memslots and the s390 hardware
DAT tables fatally out of sync under host memory pressure?
> int rc = 0;
>
> + guard(mutex)(&kvm->slots_arch_lock);
> +
> if (change == KVM_MR_FLAGS_ONLY)
> return;
>
> @@ -5822,6 +5838,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
> }
>
> scoped_guard(write_lock, &kvm->mmu_lock) {
> + if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
> + _dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
> + kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES,
> + &kvm->arch.cmma_dirty_pages);
> + }
[Severity: High]
Does this walk miss swapped-out pages by omitting the DAT_WALK_ANY flag?
If DAT_WALK_ANY is not passed to _dat_walk_gfn_range(), holes are skipped
and cmma_d_count_pte() is never called for swapped-out pages, leaking the
cmma_dirty_pages count.
However, the subsequent dat_delete_slot() will process those same holes and
wipe their dirty status.
Will this cause an infinite loop in userspace migration wait loops if the
dirty count never drops to zero?
> +
> switch (change) {
> case KVM_MR_DELETE:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=7
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child()
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (6 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 07/13] KVM: s390: cmma: Fix dirty tracking when removing memslot Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 17:50 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails Claudio Imbrenda
` (4 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
gmap_remove_child() needs to be called while holding the children_lock
of the parent gmap. This was not the case in the error handling path of
kvm_arch_vcpu_create() for UCONTROL guests.
Fix by adding the missing lock.
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 3e7e5cfea181..a61a2140a60c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3871,7 +3871,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
out_ucontrol_uninit:
if (kvm_is_ucontrol(vcpu->kvm)) {
+ spin_lock(&vcpu->kvm->arch.gmap->children_lock);
gmap_remove_child(vcpu->arch.gmap);
+ spin_unlock(&vcpu->kvm->arch.gmap->children_lock);
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
}
out_free_sie_block:
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child()
2026-07-29 15:29 ` [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child() Claudio Imbrenda
@ 2026-07-29 17:50 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 17:50 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: kvm, linux-s390, Alexander Gordeev, Heiko Carstens, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The struct kvm_s390_mmu_cache (vcpu->arch.mc) is permanently leaked on error paths in kvm_arch_vcpu_create().
--
--- Patch [8]: [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child() ---
commit 1255c78b1a63e5de1172d37ceb311f1fd31eb54f
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: ucontrol: Add missing locking around gmap_remove_child()
gmap_remove_child() needs to be called while holding the children_lock
of the parent gmap. This was not the case in the error handling path of
kvm_arch_vcpu_create() for UCONTROL guests.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index 2ae431030621e..c04b7e84c4346 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
> @@ -3869,7 +3869,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
>
> out_ucontrol_uninit:
> if (kvm_is_ucontrol(vcpu->kvm)) {
> + spin_lock(&vcpu->kvm->arch.gmap->children_lock);
> gmap_remove_child(vcpu->arch.gmap);
> + spin_unlock(&vcpu->kvm->arch.gmap->children_lock);
> vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
> }
> out_free_sie_block:
[Severity: High]
This isn't a bug introduced by this patch, but does this error path
permanently leak the struct kvm_s390_mmu_cache (vcpu->arch.mc)?
Looking at kvm_arch_vcpu_create() in arch/s390/kvm/kvm-s390.c, vcpu->arch.mc
is dynamically allocated earlier in the function via kvm_s390_new_mmu_cache().
If a subsequent setup step fails (such as gmap_new_child or
kvm_s390_vcpu_setup) and execution jumps to out_ucontrol_uninit or
out_free_sie_block, the function returns without calling
kvm_s390_free_mmu_cache(vcpu->arch.mc).
Since the KVM core bypasses kvm_arch_vcpu_destroy() when vCPU creation fails,
could this lead to a permanent memory leak if a user repeatedly creates VMs
and artificially triggers failures?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=8
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (7 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 08/13] KVM: s390: ucontrol: Add missing locking around gmap_remove_child() Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 18:02 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 10/13] KVM: s390: Return -EINTR if a signal was pending while faulting-in Claudio Imbrenda
` (3 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
The mmu cache is the first thing that is allocated in
kvm_arch_vcpu_create(), but in case of failure it was not freed.
Fix by freeing the mmu cache in case of failure.
Refactor kvm_arch_vcpu_create() to use scope-based cleanup instead of
gotos.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
---
arch/s390/kvm/kvm-s390.c | 40 ++++++++++++++++++----------------------
1 file changed, 18 insertions(+), 22 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index a61a2140a60c..fbe9abf365df 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3796,21 +3796,21 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
return 0;
}
+DEFINE_FREE(sie_page, struct sie_page *, if (_T) free_page((unsigned long)(_T)))
+
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
{
- struct sie_page *sie_page;
+ struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
+ struct sie_page *sie_page __free(sie_page) = NULL;
int rc;
BUILD_BUG_ON(sizeof(struct sie_page) != 4096);
- vcpu->arch.mc = kvm_s390_new_mmu_cache();
- if (!vcpu->arch.mc)
+ mc = kvm_s390_new_mmu_cache();
+ if (!mc)
return -ENOMEM;
sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL_ACCOUNT);
- if (!sie_page) {
- kvm_s390_free_mmu_cache(vcpu->arch.mc);
- vcpu->arch.mc = NULL;
+ if (!sie_page)
return -ENOMEM;
- }
vcpu->arch.sie_block = &sie_page->sie_block;
vcpu->arch.sie_block->itdba = virt_to_phys(&sie_page->itdb);
@@ -3852,10 +3852,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS;
if (kvm_is_ucontrol(vcpu->kvm)) {
- rc = -ENOMEM;
vcpu->arch.gmap = gmap_new_child(vcpu->kvm->arch.gmap, -1UL);
if (!vcpu->arch.gmap)
- goto out_free_sie_block;
+ return -ENOMEM;
}
VM_EVENT(vcpu->kvm, 3, "create cpu %d at 0x%p, sie block at 0x%p",
@@ -3863,22 +3862,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
trace_kvm_s390_create_vcpu(vcpu->vcpu_id, vcpu, vcpu->arch.sie_block);
rc = kvm_s390_vcpu_setup(vcpu);
- if (rc)
- goto out_ucontrol_uninit;
+ if (rc) {
+ if (kvm_is_ucontrol(vcpu->kvm)) {
+ scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock)
+ gmap_remove_child(vcpu->arch.gmap);
+ vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
+ }
+ return rc;
+ }
+ vcpu->arch.mc = no_free_ptr(mc);
+ sie_page = NULL;
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
return 0;
-
-out_ucontrol_uninit:
- if (kvm_is_ucontrol(vcpu->kvm)) {
- spin_lock(&vcpu->kvm->arch.gmap->children_lock);
- gmap_remove_child(vcpu->arch.gmap);
- spin_unlock(&vcpu->kvm->arch.gmap->children_lock);
- vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
- }
-out_free_sie_block:
- free_page((unsigned long)(vcpu->arch.sie_block));
- return rc;
}
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails
2026-07-29 15:29 ` [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails Claudio Imbrenda
@ 2026-07-29 18:02 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 18:02 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: linux-s390, kvm, Heiko Carstens, Alexander Gordeev, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Memory leak of `vcpu->arch.pv.stor_base` and `sida_addr` in `kvm_s390_pv_create_cpu` error path.
--
--- Patch [9]: [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails ---
commit b7924aec9fd280e9673810b13295e30b7a659f81
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails
The mmu cache is the first thing that is allocated in
kvm_arch_vcpu_create(), but in case of failure it was not freed.
This patch refactors the function to use scope-based cleanup to ensure
proper cleanup on failure.
> diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
> index c04b7e84c4346..40f33cdcdf84d 100644
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
[ ... ]
> @@ -3861,22 +3860,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
> trace_kvm_s390_create_vcpu(vcpu->vcpu_id, vcpu, vcpu->arch.sie_block);
>
> rc = kvm_s390_vcpu_setup(vcpu);
> - if (rc)
> - goto out_ucontrol_uninit;
> + if (rc) {
[Severity: High]
This isn't a bug introduced by this patch, but does the error path in
kvm_s390_pv_create_cpu() leak vcpu->arch.pv.stor_base and sida_addr when
called from kvm_s390_vcpu_setup() here?
If uv_call() fails during kvm_s390_pv_create_cpu(), it attempts cleanup
by calling kvm_s390_pv_destroy_cpu():
arch/s390/kvm/pv.c:kvm_s390_pv_create_cpu() {
...
if (cc) {
u16 dummy;
kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy);
return -EIO;
}
...
}
However, kvm_s390_pv_destroy_cpu() returns early because the PV handle
is not yet set:
arch/s390/kvm/pv.c:kvm_s390_pv_destroy_cpu() {
...
if (!kvm_s390_pv_cpu_get_handle(vcpu))
return 0;
...
}
Since vcpu->arch.pv.handle is assigned only after a successful uv_call(),
it is 0 during this error path. As a result, kvm_s390_pv_destroy_cpu()
does not free the previously allocated stor_base and sida_addr.
The caller then propagates the error back here, and generic KVM code skips
kvm_arch_vcpu_destroy(), making the memory leak permanent.
> + if (kvm_is_ucontrol(vcpu->kvm)) {
> + scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock)
> + gmap_remove_child(vcpu->arch.gmap);
> + vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
> + }
> + return rc;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=9
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 10/13] KVM: s390: Return -EINTR if a signal was pending while faulting-in
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (8 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 09/13] KVM: s390: Free the mmu cache when kvm_arch_vcpu_create() fails Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 18:12 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 11/13] KVM: s390: Fix ordering when adding to SCA Claudio Imbrenda
` (2 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
If a fatal signal is pending while trying to fault-in a page, return
-EINTR instead of -EAGAIN.
Also fix unpack_one() to handle -EINTR properly.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling")
---
arch/s390/kvm/faultin.c | 6 +++---
arch/s390/kvm/pv.c | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c
index fee80047bd94..3cc45f7f5b2d 100644
--- a/arch/s390/kvm/faultin.c
+++ b/arch/s390/kvm/faultin.c
@@ -91,9 +91,9 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
/* Access outside memory, addressing exception. */
if (is_noslot_pfn(f->pfn))
return PGM_ADDRESSING;
- /* Signal pending: try again. */
- if (f->pfn == KVM_PFN_ERR_SIGPENDING)
- return -EAGAIN;
+ /* Fatal signal pending: bail out. */
+ if (is_sigpending_pfn(f->pfn))
+ return -EINTR;
/* Check if it's read-only memory; don't try to actually handle that case. */
if (f->pfn == KVM_PFN_ERR_RO_FAULT)
return -EOPNOTSUPP;
diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c
index 1beacc841ca8..dc204b521052 100644
--- a/arch/s390/kvm/pv.c
+++ b/arch/s390/kvm/pv.c
@@ -809,7 +809,7 @@ static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak,
return -EAGAIN;
}
- if (ret && ret != -EAGAIN)
+ if (ret && ret != -EAGAIN && ret != -EINTR)
KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x",
uvcb.gaddr, *rc, *rrc);
return ret;
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v5 11/13] KVM: s390: Fix ordering when adding to SCA
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (9 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 10/13] KVM: s390: Return -EINTR if a signal was pending while faulting-in Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 18:33 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 12/13] KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu() Claudio Imbrenda
2026-07-29 15:29 ` [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory Claudio Imbrenda
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
When adding a new vCPU to the SCA area, the validity bit in the MCN was
set before the pointer to the state description, potentially allowing
for a race.
Fix by setting the pointer before setting the bit.
Fixes: 14542a0a54c5 ("KVM: S390: Remove sca_lock")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index fbe9abf365df..cdda7a00a0ff 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -3481,8 +3481,8 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu)
if (!kvm_s390_use_sca_entries())
return;
+ WRITE_ONCE(sca->cpu[vcpu->vcpu_id].sda, virt_to_phys(vcpu->arch.sie_block));
set_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
- sca->cpu[vcpu->vcpu_id].sda = virt_to_phys(vcpu->arch.sie_block);
}
static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id)
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* [PATCH v5 12/13] KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu()
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (10 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 11/13] KVM: s390: Fix ordering when adding to SCA Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 18:39 ` sashiko-bot
2026-07-29 15:29 ` [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory Claudio Imbrenda
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
If creating a protected vCPU in kvm_s390_pv_create_cpu() fails,
kvm_s390_pv_destroy_cpu() was called, which checks whether the vCPU has
a PV handle and exits doing nothing otherwise. At that point, due to
not having created the protected vCPU, the PV handle will not be set,
and kvm_s390_pv_destroy_cpu() will do nothing, thus leaking the
allocated memory.
Fix by factoring out the code to free and reset a PV vCPU; call it from
kvm_s390_pv_destroy_cpu() and kvm_s390_pv_create_cpu().
Opportunistically fix the return value of kvm_s390_pv_destroy_cpu() in
case of errors: return -EIO instead if EIO.
Fixes: d4074324b07a ("KVM: s390: pv: avoid double free of sida page")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
---
arch/s390/kvm/pv.c | 41 +++++++++++++++++++++--------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c
index dc204b521052..b02e0159d3cd 100644
--- a/arch/s390/kvm/pv.c
+++ b/arch/s390/kvm/pv.c
@@ -244,6 +244,24 @@ static void kvm_s390_clear_pv_state(struct kvm *kvm)
kvm->arch.pv.stor_var = NULL;
}
+static void kvm_s390_pv_dispose_cpu(struct kvm_vcpu *vcpu, bool free_stor_base)
+{
+ if (free_stor_base)
+ free_pages(vcpu->arch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len));
+ free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
+ vcpu->arch.sie_block->pv_handle_cpu = 0;
+ vcpu->arch.sie_block->pv_handle_config = 0;
+ memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
+ vcpu->arch.sie_block->sdf = 0;
+ /*
+ * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
+ * Use the reset value of gbea to avoid leaking the kernel pointer of
+ * the just freed sida.
+ */
+ vcpu->arch.sie_block->gbea = 1;
+ kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
+}
+
int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
{
int cc;
@@ -258,24 +276,9 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
WARN_ONCE(cc, "protvirt destroy cpu failed rc %x rrc %x", *rc, *rrc);
/* Intended memory leak for something that should never happen. */
- if (!cc)
- free_pages(vcpu->arch.pv.stor_base,
- get_order(uv_info.guest_cpu_stor_len));
-
- free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
- vcpu->arch.sie_block->pv_handle_cpu = 0;
- vcpu->arch.sie_block->pv_handle_config = 0;
- memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
- vcpu->arch.sie_block->sdf = 0;
- /*
- * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
- * Use the reset value of gbea to avoid leaking the kernel pointer of
- * the just freed sida.
- */
- vcpu->arch.sie_block->gbea = 1;
- kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
+ kvm_s390_pv_dispose_cpu(vcpu, !cc);
- return cc ? EIO : 0;
+ return cc ? -EIO : 0;
}
int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
@@ -319,9 +322,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
uvcb.header.rrc);
if (cc) {
- u16 dummy;
-
- kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy);
+ kvm_s390_pv_dispose_cpu(vcpu, true);
return -EIO;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 12/13] KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu()
2026-07-29 15:29 ` [PATCH v5 12/13] KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu() Claudio Imbrenda
@ 2026-07-29 18:39 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 18:39 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: Vasily Gorbik, linux-s390, kvm, Alexander Gordeev, Heiko Carstens
> If creating a protected vCPU in kvm_s390_pv_create_cpu() fails,
> kvm_s390_pv_destroy_cpu() was called, which checks whether the vCPU has
> a PV handle and exits doing nothing otherwise. At that point, due to
> not having created the protected vCPU, the PV handle will not be set,
> and kvm_s390_pv_destroy_cpu() will do nothing, thus leaking the
> allocated memory.
>
> Fix by factoring out the code to free and reset a PV vCPU; call it from
> kvm_s390_pv_destroy_cpu() and kvm_s390_pv_create_cpu().
>
> Opportunistically fix the return value of kvm_s390_pv_destroy_cpu() in
> case of errors: return -EIO instead if EIO.
>
> Fixes: d4074324b07a ("KVM: s390: pv: avoid double free of sida page")
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=12
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory
2026-07-29 15:29 [PATCH v5 00/13] KVM: s390: Misc fixes Claudio Imbrenda
` (11 preceding siblings ...)
2026-07-29 15:29 ` [PATCH v5 12/13] KVM: s390: Fix cleanup in kvm_s390_pv_create_cpu() Claudio Imbrenda
@ 2026-07-29 15:29 ` Claudio Imbrenda
2026-07-29 18:53 ` sashiko-bot
12 siblings, 1 reply; 27+ messages in thread
From: Claudio Imbrenda @ 2026-07-29 15:29 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
When low on memory, kvm_arch_commit_memory_region() might not perform
the required action completely, and instead just print a warning and
return. This allows the VM to continue running with an inconsistent
state.
Fix by refilling the struct kvm_s390_mmu_cache and trying again. A true
failure only happens if the refill fails, or if the cache cannot be
allocated at all; in such case print a warning and kill the VM. Such a
failure cannot currently happen.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
---
arch/s390/kvm/kvm-s390.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index cdda7a00a0ff..b29e5460f5ef 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -5824,7 +5824,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
{
const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
- int rc = 0;
+ int rc = -ENOMEM;
guard(mutex)(&kvm->slots_arch_lock);
@@ -5832,11 +5832,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
return;
mc = kvm_s390_new_mmu_cache();
- if (!mc) {
- rc = -ENOMEM;
+ if (!mc)
goto out;
- }
-
+retry:
scoped_guard(write_lock, &kvm->mmu_lock) {
if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
_dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
@@ -5859,11 +5857,17 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
case KVM_MR_FLAGS_ONLY:
break;
default:
+ rc = 0;
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
}
}
+ if (rc == -ENOMEM) {
+ rc = kvm_s390_mmu_cache_topup(mc);
+ if (!rc)
+ goto retry;
+ }
out:
- if (rc)
+ if (KVM_BUG_ON(rc, kvm))
pr_warn("failed to commit memory region\n");
return;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory
2026-07-29 15:29 ` [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory Claudio Imbrenda
@ 2026-07-29 18:53 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-07-29 18:53 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: Alexander Gordeev, kvm, linux-s390, Vasily Gorbik, Heiko Carstens
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Dropping `kvm->mmu_lock` and retrying allows guest page faults to populate page tables that are subsequently freed by `dat_create_slot`, destroying guest ESSA state and leaking `cmma_dirty_pages`.
- [High] The patch introduces a host-userspace-reachable KVM_BUG_ON() which triggers a WARN_ON_ONCE, allowing unprivileged users to spam the kernel log or crash the host if panic_on_warn is set.
--
--- Patch [13]: [PATCH v5 13/13] KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory ---
commit 4801c2be100c4b8f582c02a4637cf4703c366817
Author: Claudio Imbrenda <imbrenda@linux.ibm.com>
KVM: s390: Fix kvm_arch_commit_memory_region() when low on memory
This commit updates the out-of-memory handling in
kvm_arch_commit_memory_region(). It refills the kvm_s390_mmu_cache and
retries the operation, and warns and kills the VM if the refill fails.
> --- a/arch/s390/kvm/kvm-s390.c
> +++ b/arch/s390/kvm/kvm-s390.c
[ ... ]
> @@ -5822,7 +5822,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
[ ... ]
> mc = kvm_s390_new_mmu_cache();
> - if (!mc) {
> - rc = -ENOMEM;
> + if (!mc)
> goto out;
> - }
> -
> +retry:
> scoped_guard(write_lock, &kvm->mmu_lock) {
[ ... ]
> default:
> + rc = 0;
> WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
> }
> }
> + if (rc == -ENOMEM) {
> + rc = kvm_s390_mmu_cache_topup(mc);
> + if (!rc)
> + goto retry;
[Severity: Critical]
Does dropping kvm->mmu_lock between the scoped_guard exit and the goto
retry allow concurrent guest page faults?
If a guest vCPU faults on an address in the new memslot during this
window, it could populate a page table and execute ESSA instructions.
When kvm_arch_commit_memory_region() restarts dat_create_slot() after
re-acquiring the lock, could it blindly replace the now-populated CRSTE
and free the new page table without cleaning up the atomic counters?
Looking at dat_create_slot(), it appears that it replaces the CRSTE and
frees the lower-level page table:
arch/s390/kvm/dat.c:_dat_slot_crste() {
...
/* A lower level table was present, needs to be freed. */
if (!crste.h.fc && !crste.h.i) {
if (is_pmd(crste))
dat_free_pt(dereference_pmd(crste.pmd));
...
}
Could this regression result in the loss of guest state and leak
cmma_dirty_pages?
> + }
> out:
> - if (rc)
> + if (KVM_BUG_ON(rc, kvm))
[Severity: High]
Can this KVM_BUG_ON be triggered by an unprivileged host userspace
process?
If a process in a restrictive memory cgroup invokes the memslot ioctl,
kvm_s390_mmu_cache_topup() could fail with -ENOMEM, passing the error to
this path. Since KVM_BUG_ON internally executes WARN_ON_ONCE, could this
allow a malicious host-userspace process to spam the kernel log or
trigger a crash if panic_on_warn is set?
> pr_warn("failed to commit memory region\n");
> return;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729152938.782383-1-imbrenda@linux.ibm.com?part=13
^ permalink raw reply [flat|nested] 27+ messages in thread