AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/03 17:09 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_arch_vcpu_create",
    "kvm_arch_vcpu_ioctl_run",
    "kvm_arch_vcpu_ioctl",
    "kvm_arch_vcpu_unlocked_ioctl",
    "kvm_s390_vm_set_migration",
    "kvm_arch_commit_memory_region",
    "kvm_s390_pv_destroy_cpu",
    "kvm_s390_pv_create_cpu",
    "handle_rrbe",
    "handle_essa",
    "kvm_s390_keyop",
    "dat_reset_reference_bit"
  ],
  "Reasoning": "The patch makes functional changes to the s390 KVM implementation, including fixing race conditions during vCPU creation/initialization, fixing memory leaks, and updating locking mechanisms (using slots_arch_lock). These changes alter the execution flow of KVM ioctls and memory management, making them worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/08/03 17:09 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 94970647c4b3c92456de3cdf0036ff87bddb23c5\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 3 17:09:48 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h\nindex eaa34c5bd3c11..edf75b6ad20ce 100644\n--- a/arch/s390/include/asm/kvm_host.h\n+++ b/arch/s390/include/asm/kvm_host.h\n@@ -440,6 +440,7 @@ struct kvm_vcpu_arch {\n \tbool skey_enabled;\n \t/* Indicator if the access registers have been loaded from guest */\n \tbool acrs_loaded;\n+\tbool initialized;\n \tstruct kvm_s390_pv_vcpu pv;\n \tunion diag318_info diag318_info;\n \tstruct kvm_s390_mmu_cache *mc;\ndiff --git a/arch/s390/kvm/dat.c b/arch/s390/kvm/dat.c\nindex ed4259d176295..3f2d6e8902d76 100644\n--- a/arch/s390/kvm/dat.c\n+++ b/arch/s390/kvm/dat.c\n@@ -755,13 +755,15 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf\n \treturn rc;\n }\n \n-int dat_reset_reference_bit(union asce asce, gfn_t gfn)\n+int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)\n {\n \tunion pgste pgste, old;\n \tunion crste *crstep;\n \tunion pte *ptep;\n \tint rc;\n \n+\tskey-\u003eskey = 0;\n+\n \trc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, \u0026crstep, \u0026ptep);\n \tif (rc)\n \t\treturn rc;\n@@ -771,21 +773,23 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn)\n \n \t\tif (!crste.h.fc || !crste.s.fc1.pr)\n \t\t\treturn 0;\n-\t\treturn page_reset_referenced(large_crste_to_phys(*crstep, gfn));\n+\t\tskey-\u003eskey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) \u003c\u003c 1;\n+\t\treturn 0;\n \t}\n \told = pgste_get_lock(ptep);\n \tpgste = old;\n \n \tif (!ptep-\u003eh.i) {\n-\t\trc = page_reset_referenced(pte_origin(*ptep));\n-\t\tpgste.hr = rc \u003e\u003e 1;\n+\t\tskey-\u003eskey = page_reset_referenced(pte_origin(*ptep)) \u003c\u003c 1;\n+\t\tpgste.hr = skey-\u003er;\n \t}\n-\trc |= (pgste.gr \u003c\u003c 1) | pgste.gc;\n+\tskey-\u003er |= pgste.gr;\n+\tskey-\u003ec |= pgste.gc;\n \tpgste.gr = 0;\n \n \tdat_update_ptep_sd(old, pgste, ptep);\n \tpgste_set_unlock(ptep, pgste);\n-\treturn rc;\n+\treturn 0;\n }\n \n static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)\n@@ -846,6 +850,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal\n \tstruct slot_priv *p = walk-\u003epriv;\n \tunion crste dummy = { .val = p-\u003etoken };\n \tunion pte new_pte, pte = READ_ONCE(*ptep);\n+\tunion pgste pgste;\n \n \tnew_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);\n \n@@ -853,7 +858,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal\n \tif (pte.val == new_pte.val)\n \t\treturn 0;\n \n-\tdat_ptep_xchg(ptep, new_pte, gfn, walk-\u003easce, false);\n+\tpgste = pgste_get_lock(ptep);\n+\tpgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk-\u003easce, false);\n+\tpgste.cmma_d = 0;\n+\tpgste_set_unlock(ptep, pgste);\n+\n \treturn 0;\n }\n \ndiff --git a/arch/s390/kvm/dat.h b/arch/s390/kvm/dat.h\nindex fad605305e05c..141ee7b9f0191 100644\n--- a/arch/s390/kvm/dat.h\n+++ b/arch/s390/kvm/dat.h\n@@ -537,7 +537,7 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf\n \t\t\tunion skey skey, bool nq);\n int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,\n \t\t\t     union skey skey, union skey *oldkey, bool nq, bool mr, bool mc);\n-int dat_reset_reference_bit(union asce asce, gfn_t gfn);\n+int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey);\n long dat_reset_skeys(union asce asce, gfn_t start);\n \n unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);\ndiff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c\nindex fee80047bd94f..3cc45f7f5b2d7 100644\n--- a/arch/s390/kvm/faultin.c\n+++ b/arch/s390/kvm/faultin.c\n@@ -91,9 +91,9 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa\n \t\t/* Access outside memory, addressing exception. */\n \t\tif (is_noslot_pfn(f-\u003epfn))\n \t\t\treturn PGM_ADDRESSING;\n-\t\t/* Signal pending: try again. */\n-\t\tif (f-\u003epfn == KVM_PFN_ERR_SIGPENDING)\n-\t\t\treturn -EAGAIN;\n+\t\t/* Fatal signal pending: bail out. */\n+\t\tif (is_sigpending_pfn(f-\u003epfn))\n+\t\t\treturn -EINTR;\n \t\t/* Check if it's read-only memory; don't try to actually handle that case. */\n \t\tif (f-\u003epfn == KVM_PFN_ERR_RO_FAULT)\n \t\t\treturn -EOPNOTSUPP;\ndiff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c\nindex 9e3e6b0d72ad6..6b3f97a7513b3 100644\n--- a/arch/s390/kvm/interrupt.c\n+++ b/arch/s390/kvm/interrupt.c\n@@ -45,13 +45,16 @@ static struct kvm_s390_gib *gib;\n static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)\n {\n \tstruct esca_block *sca = vcpu-\u003ekvm-\u003earch.sca;\n-\tunion esca_sigp_ctrl sigp_ctrl = sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n+\tunion esca_sigp_ctrl sigp_ctrl;\n \n \tif (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))\n \t\treturn 0;\n+\tif (kvm_is_ucontrol(vcpu-\u003ekvm))\n+\t\treturn 0;\n \n \tBUG_ON(!kvm_s390_use_sca_entries());\n \n+\tsigp_ctrl = sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n \tif (src_id)\n \t\t*src_id = sigp_ctrl.scn;\n \n@@ -60,13 +63,16 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)\n \n static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)\n {\n-\tstruct esca_block *sca = vcpu-\u003ekvm-\u003earch.sca;\n-\tunion esca_sigp_ctrl *sigp_ctrl = \u0026sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n \tunion esca_sigp_ctrl old_val, new_val = {.scn = src_id, .c = 1};\n+\tstruct esca_block *sca = vcpu-\u003ekvm-\u003earch.sca;\n+\tunion esca_sigp_ctrl *sigp_ctrl;\n \tint expect, rc;\n \n \tBUG_ON(!kvm_s390_use_sca_entries());\n+\tif (kvm_is_ucontrol(vcpu-\u003ekvm))\n+\t\treturn -EINVAL;\n \n+\tsigp_ctrl = \u0026sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n \told_val = READ_ONCE(*sigp_ctrl);\n \told_val.c = 0;\n \n@@ -84,10 +90,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)\n static void sca_clear_ext_call(struct kvm_vcpu *vcpu)\n {\n \tstruct esca_block *sca = vcpu-\u003ekvm-\u003earch.sca;\n-\tunion esca_sigp_ctrl *sigp_ctrl = \u0026sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n+\tunion esca_sigp_ctrl *sigp_ctrl;\n \n-\tif (!kvm_s390_use_sca_entries())\n+\tif (!kvm_s390_use_sca_entries() || !vcpu-\u003earch.initialized || kvm_is_ucontrol(vcpu-\u003ekvm))\n \t\treturn;\n+\n+\t/* Initialize after the above check, to prevent going out of bounds */\n+\tsigp_ctrl = \u0026sca-\u003ecpu[vcpu-\u003evcpu_id].sigp_ctrl;\n \tkvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);\n \n \tWRITE_ONCE(sigp_ctrl-\u003evalue, 0);\ndiff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c\nindex 150b5dd2170e2..518a69c55e85c 100644\n--- a/arch/s390/kvm/kvm-s390.c\n+++ b/arch/s390/kvm/kvm-s390.c\n@@ -571,7 +571,7 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op\n \tswitch (op) {\n \tcase KVM_S390_KEYOP_SSKE:\n \t\tr = dat_cond_set_storage_key(mc, asce, gfn, skey, \u0026skey, 0, 0, 0);\n-\t\tif (r \u003e= 0)\n+\t\tif (r == 0 || r == 1)\n \t\t\treturn skey.skey;\n \t\tbreak;\n \tcase KVM_S390_KEYOP_ISKE:\n@@ -580,14 +580,14 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op\n \t\t\treturn skey.skey;\n \t\tbreak;\n \tcase KVM_S390_KEYOP_RRBE:\n-\t\tr = dat_reset_reference_bit(asce, gfn);\n-\t\tif (r \u003e 0)\n-\t\t\treturn r \u003c\u003c 1;\n+\t\tr = dat_reset_reference_bit(asce, gfn, \u0026skey);\n+\t\tif (!r)\n+\t\t\treturn skey.skey;\n \t\tbreak;\n \tdefault:\n \t\treturn -EINVAL;\n \t}\n-\treturn r;\n+\treturn r \u003e 0 ? -EFAULT : r;\n }\n \n /* Section: device related */\n@@ -1219,8 +1219,8 @@ static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req)\n \n /*\n  * Must be called with kvm-\u003esrcu held to avoid races on memslots, and with\n- * kvm-\u003eslots_lock to avoid races with ourselves, kvm_s390_vm_stop_migration(),\n- * and kvm_s390_get_cmma_bits().\n+ * kvm-\u003eslots_arch_lock to avoid races with ourselves,\n+ * kvm_s390_vm_stop_migration(), and kvm_s390_get_cmma_bits().\n  */\n static int kvm_s390_vm_start_migration(struct kvm *kvm)\n {\n@@ -1265,7 +1265,7 @@ static int kvm_s390_vm_start_migration(struct kvm *kvm)\n }\n \n /*\n- * Must be called with kvm-\u003eslots_lock to avoid races with ourselves,\n+ * Must be called with kvm-\u003eslots_arch_lock to avoid races with ourselves,\n  * kvm_s390_vm_start_migration() and kvm_s390_get_cmma_bits().\n  */\n static int kvm_s390_vm_stop_migration(struct kvm *kvm)\n@@ -1300,7 +1300,9 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,\n {\n \tint res = -ENXIO;\n \n-\tmutex_lock(\u0026kvm-\u003eslots_lock);\n+\tguard(srcu)(\u0026kvm-\u003esrcu);\n+\tguard(mutex)(\u0026kvm-\u003eslots_arch_lock);\n+\n \tswitch (attr-\u003eattr) {\n \tcase KVM_S390_VM_MIGRATION_START:\n \t\tres = kvm_s390_vm_start_migration(kvm);\n@@ -1311,7 +1313,6 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,\n \tdefault:\n \t\tbreak;\n \t}\n-\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n \n \treturn res;\n }\n@@ -2214,7 +2215,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)\n \t}\n \n \tkvfree(keys);\n-\treturn r;\n+\treturn r \u003c= 0 ? r : -EFAULT;\n }\n \n static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)\n@@ -2276,7 +2277,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)\n \tkvm_s390_free_mmu_cache(mc);\n out:\n \tkvfree(keys);\n-\treturn r;\n+\treturn r \u003c= 0 ? r : -EFAULT;\n }\n \n /*\n@@ -2386,7 +2387,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,\n \n \tset_bit(GMAP_FLAG_USES_CMM, \u0026kvm-\u003earch.gmap-\u003eflags);\n \n-\treturn r;\n+\treturn r \u003c= 0 ? r : -EFAULT;\n }\n \n /**\n@@ -2934,6 +2935,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)\n \tcase KVM_S390_INTERRUPT: {\n \t\tstruct kvm_s390_interrupt s390int;\n \n+\t\tr = -EINVAL;\n+\t\tif (kvm_is_ucontrol(kvm))\n+\t\t\tbreak;\n \t\tr = -EFAULT;\n \t\tif (copy_from_user(\u0026s390int, argp, sizeof(s390int)))\n \t\t\tbreak;\n@@ -2998,9 +3002,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)\n \t\tr = -EFAULT;\n \t\tif (copy_from_user(\u0026args, argp, sizeof(args)))\n \t\t\tbreak;\n-\t\tmutex_lock(\u0026kvm-\u003eslots_lock);\n-\t\tr = kvm_s390_get_cmma_bits(kvm, \u0026args);\n-\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n+\t\tscoped_guard(mutex, \u0026kvm-\u003eslots_arch_lock)\n+\t\t\tr = kvm_s390_get_cmma_bits(kvm, \u0026args);\n \t\tif (!r) {\n \t\t\tr = copy_to_user(argp, \u0026args, sizeof(args));\n \t\t\tif (r)\n@@ -3014,9 +3017,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)\n \t\tr = -EFAULT;\n \t\tif (copy_from_user(\u0026args, argp, sizeof(args)))\n \t\t\tbreak;\n-\t\tmutex_lock(\u0026kvm-\u003eslots_lock);\n+\t\tmutex_lock(\u0026kvm-\u003eslots_arch_lock);\n \t\tr = kvm_s390_set_cmma_bits(kvm, \u0026args);\n-\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n+\t\tmutex_unlock(\u0026kvm-\u003eslots_arch_lock);\n \t\tbreak;\n \t}\n \tcase KVM_S390_PV_COMMAND: {\n@@ -3247,7 +3250,8 @@ static void kvm_s390_crypto_init(struct kvm *kvm)\n \n static void sca_dispose(struct kvm *kvm)\n {\n-\tfree_pages_exact(kvm-\u003earch.sca, sizeof(*kvm-\u003earch.sca));\n+\tif (kvm-\u003earch.sca)\n+\t\tfree_pages_exact(kvm-\u003earch.sca, sizeof(*kvm-\u003earch.sca));\n \tkvm-\u003earch.sca = NULL;\n }\n \n@@ -3461,7 +3465,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu)\n {\n \tstruct esca_block *sca = vcpu-\u003ekvm-\u003earch.sca;\n \n-\tif (!kvm_s390_use_sca_entries())\n+\tif (!kvm_s390_use_sca_entries() || !vcpu-\u003earch.initialized)\n \t\treturn;\n \n \tclear_bit_inv(vcpu-\u003evcpu_id, (unsigned long *)sca-\u003emcn);\n@@ -3481,8 +3485,8 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu)\n \tif (!kvm_s390_use_sca_entries())\n \t\treturn;\n \n+\tWRITE_ONCE(sca-\u003ecpu[vcpu-\u003evcpu_id].sda, virt_to_phys(vcpu-\u003earch.sie_block));\n \tset_bit_inv(vcpu-\u003evcpu_id, (unsigned long *)sca-\u003emcn);\n-\tsca-\u003ecpu[vcpu-\u003evcpu_id].sda = virt_to_phys(vcpu-\u003earch.sie_block);\n }\n \n static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id)\n@@ -3613,6 +3617,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)\n \tif (test_kvm_facility(vcpu-\u003ekvm, 74) || vcpu-\u003ekvm-\u003earch.user_instr0 ||\n \t    vcpu-\u003ekvm-\u003earch.user_operexec)\n \t\tvcpu-\u003earch.sie_block-\u003eictl |= ICTL_OPEREXC;\n+\n+\t/* Pairs with smp_load_acquire() in kvm_arch_vcpu_ioctl_run() and kvm_arch_vcpu_ioctl() */\n+\tsmp_store_release(\u0026vcpu-\u003earch.initialized, true);\n }\n \n static bool kvm_has_pckmo_subfunc(struct kvm *kvm, unsigned long nr)\n@@ -3674,7 +3681,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)\n \n void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)\n {\n-\tfree_page((unsigned long)phys_to_virt(vcpu-\u003earch.sie_block-\u003ecbrlo));\n+\tif (vcpu-\u003earch.sie_block-\u003ecbrlo)\n+\t\tfree_page((unsigned long)phys_to_virt(vcpu-\u003earch.sie_block-\u003ecbrlo));\n \tvcpu-\u003earch.sie_block-\u003ecbrlo = 0;\n }\n \n@@ -3792,21 +3800,21 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)\n \treturn 0;\n }\n \n+DEFINE_FREE(sie_page, struct sie_page *, if (_T) free_page((unsigned long)(_T)))\n+\n int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n {\n-\tstruct sie_page *sie_page;\n+\tstruct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;\n+\tstruct sie_page *sie_page __free(sie_page) = NULL;\n \tint rc;\n \n \tBUILD_BUG_ON(sizeof(struct sie_page) != 4096);\n-\tvcpu-\u003earch.mc = kvm_s390_new_mmu_cache();\n-\tif (!vcpu-\u003earch.mc)\n+\tmc = kvm_s390_new_mmu_cache();\n+\tif (!mc)\n \t\treturn -ENOMEM;\n \tsie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL_ACCOUNT);\n-\tif (!sie_page) {\n-\t\tkvm_s390_free_mmu_cache(vcpu-\u003earch.mc);\n-\t\tvcpu-\u003earch.mc = NULL;\n+\tif (!sie_page)\n \t\treturn -ENOMEM;\n-\t}\n \n \tvcpu-\u003earch.sie_block = \u0026sie_page-\u003esie_block;\n \tvcpu-\u003earch.sie_block-\u003eitdba = virt_to_phys(\u0026sie_page-\u003eitdb);\n@@ -3848,10 +3856,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n \t\tvcpu-\u003erun-\u003ekvm_valid_regs |= KVM_SYNC_FPRS;\n \n \tif (kvm_is_ucontrol(vcpu-\u003ekvm)) {\n-\t\trc = -ENOMEM;\n \t\tvcpu-\u003earch.gmap = gmap_new_child(vcpu-\u003ekvm-\u003earch.gmap, -1UL);\n \t\tif (!vcpu-\u003earch.gmap)\n-\t\t\tgoto out_free_sie_block;\n+\t\t\treturn -ENOMEM;\n \t}\n \n \tVM_EVENT(vcpu-\u003ekvm, 3, \"create cpu %d at 0x%p, sie block at 0x%p\",\n@@ -3859,20 +3866,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)\n \ttrace_kvm_s390_create_vcpu(vcpu-\u003evcpu_id, vcpu, vcpu-\u003earch.sie_block);\n \n \trc = kvm_s390_vcpu_setup(vcpu);\n-\tif (rc)\n-\t\tgoto out_ucontrol_uninit;\n+\tif (rc) {\n+\t\tif (kvm_is_ucontrol(vcpu-\u003ekvm)) {\n+\t\t\tscoped_guard(spinlock, \u0026vcpu-\u003ekvm-\u003earch.gmap-\u003echildren_lock)\n+\t\t\t\tgmap_remove_child(vcpu-\u003earch.gmap);\n+\t\t\tvcpu-\u003earch.gmap = gmap_put(vcpu-\u003earch.gmap);\n+\t\t}\n+\t\treturn rc;\n+\t}\n \n+\tvcpu-\u003earch.mc = no_free_ptr(mc);\n+\tsie_page = NULL;\n \tkvm_s390_update_topology_change_report(vcpu-\u003ekvm, 1);\n \treturn 0;\n-\n-out_ucontrol_uninit:\n-\tif (kvm_is_ucontrol(vcpu-\u003ekvm)) {\n-\t\tgmap_remove_child(vcpu-\u003earch.gmap);\n-\t\tvcpu-\u003earch.gmap = gmap_put(vcpu-\u003earch.gmap);\n-\t}\n-out_free_sie_block:\n-\tfree_page((unsigned long)(vcpu-\u003earch.sie_block));\n-\treturn rc;\n }\n \n int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)\n@@ -5039,6 +5045,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)\n \t    kvm_run-\u003ekvm_dirty_regs \u0026 ~KVM_SYNC_S390_VALID_FIELDS)\n \t\treturn -EINVAL;\n \n+\t/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */\n+\tif (!smp_load_acquire(\u0026vcpu-\u003earch.initialized))\n+\t\treturn -EINVAL;\n+\n \tvcpu_load(vcpu);\n \n \tif (guestdbg_exit_pending(vcpu)) {\n@@ -5447,6 +5457,8 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,\n \t\tstruct kvm_s390_interrupt s390int;\n \t\tstruct kvm_s390_irq s390irq = {};\n \n+\t\tif (kvm_is_ucontrol(vcpu-\u003ekvm))\n+\t\t\treturn -EINVAL;\n \t\tif (copy_from_user(\u0026s390int, argp, sizeof(s390int)))\n \t\t\treturn -EFAULT;\n \t\tif (s390int_to_s390irq(\u0026s390int, \u0026s390irq))\n@@ -5523,6 +5535,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,\n \tlong r;\n \tu16 rc, rrc;\n \n+\t/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */\n+\tif (!smp_load_acquire(\u0026vcpu-\u003earch.initialized))\n+\t\treturn -EINVAL;\n+\n \tvcpu_load(vcpu);\n \n \tswitch (ioctl) {\n@@ -5794,14 +5810,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,\n \treturn 0;\n }\n \n+static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)\n+{\n+\tunion pgste pgste;\n+\n+\tpgste = pgste_get_lock(ptep);\n+\tif (pgste.cmma_d) {\n+\t\tpgste.cmma_d = 0;\n+\t\tatomic64_dec(walk-\u003epriv);\n+\t}\n+\tpgste_set_unlock(ptep, pgste);\n+\treturn 0;\n+}\n+\n void kvm_arch_commit_memory_region(struct kvm *kvm,\n \t\t\t\tstruct kvm_memory_slot *old,\n \t\t\t\tconst struct kvm_memory_slot *new,\n \t\t\t\tenum kvm_mr_change change)\n {\n-\tstruct kvm_s390_mmu_cache *mc = NULL;\n+\tconst struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };\n+\tstruct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;\n \tint rc = 0;\n \n+\tguard(mutex)(\u0026kvm-\u003eslots_arch_lock);\n+\n \tif (change == KVM_MR_FLAGS_ONLY)\n \t\treturn;\n \n@@ -5812,6 +5844,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,\n \t}\n \n \tscoped_guard(write_lock, \u0026kvm-\u003emmu_lock) {\n+\t\tif (kvm-\u003earch.migration_mode \u0026\u0026 kvm-\u003earch.use_cmma \u0026\u0026 old) {\n+\t\t\t_dat_walk_gfn_range(old-\u003ebase_gfn, old-\u003ebase_gfn + old-\u003enpages,\n+\t\t\t\t\t    kvm-\u003earch.gmap-\u003easce, \u0026ops, DAT_WALK_IGN_HOLES,\n+\t\t\t\t\t    \u0026kvm-\u003earch.cmma_dirty_pages);\n+\t\t}\n+\n \t\tswitch (change) {\n \t\tcase KVM_MR_DELETE:\n \t\t\trc = dat_delete_slot(mc, kvm-\u003earch.gmap-\u003easce, old-\u003ebase_gfn, old-\u003enpages);\n@@ -5833,7 +5871,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,\n out:\n \tif (rc)\n \t\tpr_warn(\"failed to commit memory region\\n\");\n-\tkvm_s390_free_mmu_cache(mc);\n \treturn;\n }\n \ndiff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c\nindex ad0ddc433a73c..b1ba24c346efa 100644\n--- a/arch/s390/kvm/priv.c\n+++ b/arch/s390/kvm/priv.c\n@@ -289,6 +289,7 @@ static int handle_iske(struct kvm_vcpu *vcpu)\n static int handle_rrbe(struct kvm_vcpu *vcpu)\n {\n \tunsigned long gaddr;\n+\tunion skey skey;\n \tint reg1, reg2;\n \tint rc;\n \n@@ -307,12 +308,12 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)\n \tgaddr = kvm_s390_logical_to_effective(vcpu, gaddr);\n \tgaddr = kvm_s390_real_to_abs(vcpu, gaddr);\n \tscoped_guard(read_lock, \u0026vcpu-\u003ekvm-\u003emmu_lock)\n-\t\trc = dat_reset_reference_bit(vcpu-\u003earch.gmap-\u003easce, gpa_to_gfn(gaddr));\n+\t\trc = dat_reset_reference_bit(vcpu-\u003earch.gmap-\u003easce, gpa_to_gfn(gaddr), \u0026skey);\n \tif (rc \u003e 0)\n \t\treturn kvm_s390_inject_program_int(vcpu, rc);\n \tif (rc \u003c 0)\n \t\treturn rc;\n-\tkvm_s390_set_psw_cc(vcpu, rc);\n+\tkvm_s390_set_psw_cc(vcpu, (skey.skey \u003e\u003e 1) \u0026 3);\n \treturn 0;\n }\n \n@@ -1260,8 +1261,9 @@ static int handle_essa(struct kvm_vcpu *vcpu)\n \t\t/* Retry the ESSA instruction */\n \t\tkvm_s390_retry_instr(vcpu);\n \t} else {\n-\t\tscoped_guard(read_lock, \u0026vcpu-\u003ekvm-\u003emmu_lock)\n-\t\t\ti = __do_essa(vcpu, orc);\n+\t\tscoped_guard(mutex, \u0026vcpu-\u003ekvm-\u003eslots_arch_lock)\n+\t\t\tscoped_guard(read_lock, \u0026vcpu-\u003ekvm-\u003emmu_lock)\n+\t\t\t\ti = __do_essa(vcpu, orc);\n \t\tif (i \u003c 0)\n \t\t\treturn i;\n \t\t/* Account for the possible extra cbrl entry */\ndiff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c\nindex 1beacc841ca85..b02e0159d3cd2 100644\n--- a/arch/s390/kvm/pv.c\n+++ b/arch/s390/kvm/pv.c\n@@ -244,6 +244,24 @@ static void kvm_s390_clear_pv_state(struct kvm *kvm)\n \tkvm-\u003earch.pv.stor_var = NULL;\n }\n \n+static void kvm_s390_pv_dispose_cpu(struct kvm_vcpu *vcpu, bool free_stor_base)\n+{\n+\tif (free_stor_base)\n+\t\tfree_pages(vcpu-\u003earch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len));\n+\tfree_page((unsigned long)sida_addr(vcpu-\u003earch.sie_block));\n+\tvcpu-\u003earch.sie_block-\u003epv_handle_cpu = 0;\n+\tvcpu-\u003earch.sie_block-\u003epv_handle_config = 0;\n+\tmemset(\u0026vcpu-\u003earch.pv, 0, sizeof(vcpu-\u003earch.pv));\n+\tvcpu-\u003earch.sie_block-\u003esdf = 0;\n+\t/*\n+\t * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).\n+\t * Use the reset value of gbea to avoid leaking the kernel pointer of\n+\t * the just freed sida.\n+\t */\n+\tvcpu-\u003earch.sie_block-\u003egbea = 1;\n+\tkvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);\n+}\n+\n int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)\n {\n \tint cc;\n@@ -258,24 +276,9 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)\n \tWARN_ONCE(cc, \"protvirt destroy cpu failed rc %x rrc %x\", *rc, *rrc);\n \n \t/* Intended memory leak for something that should never happen. */\n-\tif (!cc)\n-\t\tfree_pages(vcpu-\u003earch.pv.stor_base,\n-\t\t\t   get_order(uv_info.guest_cpu_stor_len));\n-\n-\tfree_page((unsigned long)sida_addr(vcpu-\u003earch.sie_block));\n-\tvcpu-\u003earch.sie_block-\u003epv_handle_cpu = 0;\n-\tvcpu-\u003earch.sie_block-\u003epv_handle_config = 0;\n-\tmemset(\u0026vcpu-\u003earch.pv, 0, sizeof(vcpu-\u003earch.pv));\n-\tvcpu-\u003earch.sie_block-\u003esdf = 0;\n-\t/*\n-\t * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).\n-\t * Use the reset value of gbea to avoid leaking the kernel pointer of\n-\t * the just freed sida.\n-\t */\n-\tvcpu-\u003earch.sie_block-\u003egbea = 1;\n-\tkvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);\n+\tkvm_s390_pv_dispose_cpu(vcpu, !cc);\n \n-\treturn cc ? EIO : 0;\n+\treturn cc ? -EIO : 0;\n }\n \n int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)\n@@ -319,9 +322,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)\n \t\t     uvcb.header.rrc);\n \n \tif (cc) {\n-\t\tu16 dummy;\n-\n-\t\tkvm_s390_pv_destroy_cpu(vcpu, \u0026dummy, \u0026dummy);\n+\t\tkvm_s390_pv_dispose_cpu(vcpu, true);\n \t\treturn -EIO;\n \t}\n \n@@ -809,7 +810,7 @@ static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak,\n \t\t\treturn -EAGAIN;\n \t}\n \n-\tif (ret \u0026\u0026 ret != -EAGAIN)\n+\tif (ret \u0026\u0026 ret != -EAGAIN \u0026\u0026 ret != -EINTR)\n \t\tKVM_UV_EVENT(kvm, 3, \"PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x\",\n \t\t\t     uvcb.gaddr, *rc, *rrc);\n \treturn ret;\n"
}

2/1 2026/08/03 17:09 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_arch_vcpu_create",
    "kvm_arch_vcpu_ioctl_run",
    "kvm_arch_vcpu_ioctl",
    "kvm_arch_vcpu_unlocked_ioctl",
    "kvm_s390_vm_set_migration",
    "kvm_arch_commit_memory_region",
    "kvm_s390_pv_destroy_cpu",
    "kvm_s390_pv_create_cpu",
    "handle_rrbe",
    "handle_essa",
    "kvm_s390_keyop",
    "dat_reset_reference_bit"
  ],
  "Reasoning": "The patch makes functional changes to the s390 KVM implementation, including fixing race conditions during vCPU creation/initialization, fixing memory leaks, and updating locking mechanisms (using slots_arch_lock). These changes alter the execution flow of KVM ioctls and memory management, making them worth fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 94970647c4b3c92456de3cdf0036ff87bddb23c5
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 3 17:09:48 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index eaa34c5bd3c11..edf75b6ad20ce 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/dat.c b/arch/s390/kvm/dat.c
index ed4259d176295..3f2d6e8902d76 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)
@@ -846,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);
 
@@ -853,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/dat.h b/arch/s390/kvm/dat.h
index fad605305e05c..141ee7b9f0191 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/faultin.c b/arch/s390/kvm/faultin.c
index fee80047bd94f..3cc45f7f5b2d7 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/interrupt.c b/arch/s390/kvm/interrupt.c
index 9e3e6b0d72ad6..6b3f97a7513b3 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -45,13 +45,16 @@ static struct kvm_s390_gib *gib;
 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;
+	union esca_sigp_ctrl sigp_ctrl;
 
 	if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
 		return 0;
+	if (kvm_is_ucontrol(vcpu->kvm))
+		return 0;
 
 	BUG_ON(!kvm_s390_use_sca_entries());
 
+	sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
 	if (src_id)
 		*src_id = sigp_ctrl.scn;
 
@@ -60,13 +63,16 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
 
 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;
 	union esca_sigp_ctrl old_val, new_val = {.scn = src_id, .c = 1};
+	struct esca_block *sca = vcpu->kvm->arch.sca;
+	union esca_sigp_ctrl *sigp_ctrl;
 	int expect, rc;
 
 	BUG_ON(!kvm_s390_use_sca_entries());
+	if (kvm_is_ucontrol(vcpu->kvm))
+		return -EINVAL;
 
+	sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
 	old_val = READ_ONCE(*sigp_ctrl);
 	old_val.c = 0;
 
@@ -84,10 +90,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())
+	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);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 150b5dd2170e2..518a69c55e85c 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 */
@@ -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;
 }
@@ -2214,7 +2215,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 +2277,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 +2387,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;
 }
 
 /**
@@ -2934,6 +2935,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
 	case KVM_S390_INTERRUPT: {
 		struct kvm_s390_interrupt s390int;
 
+		r = -EINVAL;
+		if (kvm_is_ucontrol(kvm))
+			break;
 		r = -EFAULT;
 		if (copy_from_user(&s390int, argp, sizeof(s390int)))
 			break;
@@ -2998,9 +3002,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 +3017,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: {
@@ -3247,7 +3250,8 @@ static void kvm_s390_crypto_init(struct kvm *kvm)
 
 static void sca_dispose(struct kvm *kvm)
 {
-	free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
+	if (kvm->arch.sca)
+		free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
 	kvm->arch.sca = NULL;
 }
 
@@ -3461,7 +3465,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);
@@ -3481,8 +3485,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)
@@ -3613,6 +3617,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)
@@ -3674,7 +3681,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;
 }
 
@@ -3792,21 +3800,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);
@@ -3848,10 +3856,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",
@@ -3859,20 +3866,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)) {
-		gmap_remove_child(vcpu->arch.gmap);
-		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)
@@ -5039,6 +5045,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)) {
@@ -5447,6 +5457,8 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
 		struct kvm_s390_interrupt s390int;
 		struct kvm_s390_irq s390irq = {};
 
+		if (kvm_is_ucontrol(vcpu->kvm))
+			return -EINVAL;
 		if (copy_from_user(&s390int, argp, sizeof(s390int)))
 			return -EFAULT;
 		if (s390int_to_s390irq(&s390int, &s390irq))
@@ -5523,6 +5535,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) {
@@ -5794,14 +5810,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;
 
@@ -5812,6 +5844,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);
@@ -5833,7 +5871,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;
 }
 
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index ad0ddc433a73c..b1ba24c346efa 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;
 }
 
@@ -1260,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 */
diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c
index 1beacc841ca85..b02e0159d3cd2 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;
 	}
 
@@ -809,7 +810,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;

3/2 2026/08/03 17:09 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11817
							output: 35
							thoughts: 970
						

Thoughts:
**Analyzing an s390 KVM Patch**

Okay, so I've got this patch for s390 KVM, and I need to get a handle on it quickly. Let's see... it's touching several core areas: `arch/s390/kvm/`.  The key changes jump out right away. They've added a `bool initialized;` to `struct kvm_vcpu_arch` in `kvm_host.h`. That's interesting, suggesting they're tracking VCPU initialization state, and probably fixing some race conditions.

Then, there's a modification to `dat_reset_reference_bit` in `dat.c`. The signature changed, returning the `skey` via a pointer.  That's a potential area for subtle bugs if not handled correctly by all callers. The `faultin.c` change, handling `-EINTR` from `is_sigpending_pfn()`, is a good defensive measure; addressing signal handling in faulting paths is always a win.

Moving to `interrupt.c`, the checks for `kvm_is_ucontrol(vcpu->kvm)` in interrupt functions look like a good security measure, only allowing privileged operations from appropriate contexts.

`kvm-s390.c` is where things get really interesting. There are updates to `kvm_s390_keyop` to accommodate the `dat_reset_reference_bit` change, which is expected. The use of `kvm->slots_arch_lock` instead of `kvm->slots_lock` in `kvm_s390_vm_set_migration` and `kvm_arch_vm_ioctl` is a good sign that they're addressing some locking issues, especially with the CMMA bits, which would have had to do with the guest physical to host physical translation. The addition of checks for the `kvm->arch.sca` before freeing and the VCPU's initialization state (`vcpu->arch.initialized`) in several spots suggests a focus on resource management and avoiding use-after-free conditions. The use of `smp_store_release` and `smp_load_acquire` with the initialized flag is absolutely necessary for correct synchronization on a multi-core machine, and I'm glad to see that. Fixing those memory leaks and error handling issues in `kvm_arch_vcpu_create` is important. I need to note that function and analyze it.

The use of `slots_arch_lock` in `kvm_arch_commit_memory_region` also points to locking improvements, and CMMA dirty page updates are a key part of this. The `priv.c` and `pv.c` changes, with the updated `dat_reset_reference_bit` calls and the `slots_arch_lock` usage in `handle_essa` are standard adaptations to the changes in the rest of the code. The splitting of `kvm_s390_pv_dispose_cpu` with the respective changes in `kvm_s390_pv_destroy_cpu` and `kvm_s390_pv_create_cpu` is a good step towards simplifying the CPU resource management.

So, overall, this looks like a functional patch series. Definitely worth a thorough look. It addresses a few bugs, including race conditions related to VCPU initialization, locking problems, and some memory leaks. That's good stuff.

Okay, let me list the focus symbols:

*   `kvm_arch_vcpu_create` - That one needs scrutiny.
*   `kvm_arch_vcpu_ioctl_run`
*   `kvm_arch_vcpu_ioctl`
*   `kvm_arch_vcpu_unlocked_ioctl`
*   `kvm_s390_vm_set_migration`
*   `kvm_arch_commit_memory_region`
*   `kvm_s390_pv_destroy_cpu`
*   `kvm_s390_pv_create_cpu`
*   `handle_rrbe`
*   `handle_essa`
*   `kvm_s390_keyop`
*   `dat_reset_reference_bit` - Keep an eye on the `dat_reset_reference_bit` signature change, as well as every location the function is called.

Now, let's make sure the Kconfig is set correctly. s390 KVM depends on `CONFIG_KVM`, obviously.  Let's use `grepper` in the `arch/s390/kvm/Kconfig` to see


4/2 2026/08/03 17:10 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/s390/kvm/Kconfig"
}

Results:
{
  "Output": "arch/s390/kvm/Kconfig=17=if VIRTUALIZATION\narch/s390/kvm/Kconfig-18-\narch/s390/kvm/Kconfig:19:config KVM\narch/s390/kvm/Kconfig-20-\tdef_tristate y\n--\narch/s390/kvm/Kconfig-46-\narch/s390/kvm/Kconfig:47:config KVM_S390_UCONTROL\narch/s390/kvm/Kconfig-48-\tbool \"Userspace controlled virtual machines\"\n"
}

5/2 2026/08/03 17:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14015
							output: 43
							thoughts: 71
						

Thoughts:
**Investigating the KVM S390 UControl Configuration**

Okay, so I'm looking at this patch and it references `kvm_is_ucontrol(vcpu->kvm)`. That function call, based on my understanding of the KVM codebase, strongly implies that the `CONFIG_KVM_S390_UCONTROL` configuration option *should* be enabled for the target platform. Given this, I need to verify that this configuration option is indeed set as expected.

My next step is to drill down into the architecture-specific configuration files. I'll need to confirm the presence and status of `KVM_S390_UCONTROL`. I'll go check `arch/s390/kvm/Kconfig` to inspect the Kconfig file for the relevant option. I need to make sure this configuration element is set correctly, and the build is picking this option up or configured correctly.



6/2 2026/08/03 17:10 tool grepper
0m Args:
{
  "Expression": "config KVM_S390_UCONTROL",
  "PathPrefix": "arch/s390/kvm/Kconfig"
}

Results:
{
  "Output": "arch/s390/kvm/Kconfig=19=config KVM\n--\narch/s390/kvm/Kconfig-46-\narch/s390/kvm/Kconfig:47:config KVM_S390_UCONTROL\narch/s390/kvm/Kconfig-48-\tbool \"Userspace controlled virtual machines\"\n"
}

7/2 2026/08/03 17:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14433
							output: 262
							thoughts: 195
						

Thoughts:
Okay, here's my take on those focus symbols, thinking through this analysis:

**Focus Symbol Review & KVM Integration**

Alright, let's get down to brass tacks. I'm taking a look at a set of key symbols related to the `KVM` infrastructure – definitely central to any deep dive into virtualization on Linux. The objective here is to get a solid grasp of where the most critical interactions and processing occur within the `KVM` layer.

I've got a comprehensive list of functions to consider. These functions represent the backbone of `KVM`'s interactions with the architecture and I/O. I'm noting the initial list:

*   `kvm_arch_vcpu_create`: This is crucial – the creation of a virtual CPU context. Everything spins out from here.
*   `kvm_arch_vcpu_ioctl_run`: This is the heart of the VCPU's execution loop, the glue between userspace and the guest.
*   `kvm_arch_vcpu_ioctl`: The general purpose `ioctl` interface for VCPUs.
*   `kvm_arch_vcpu_unlocked_ioctl`: Variant of the above, often used when critical sections don't need to be locked.
*   `kvm_s390_vm_set_migration`: This one is very interesting, since it relates to VM migration, so it's a good inclusion.
*   `kvm_arch_commit_memory_region`: This is related to memory management, which is something I need to evaluate.
*   `kvm_s390_pv_destroy_cpu`: And its counterpart: the destruction of the virtual CPU.
*   `kvm_s390_pv_create_cpu`: Creating a virtual CPU, but for a specific architecture.
*   `handle_rrbe`: Looks like a crucial handler for a certain type of instruction or hardware event.
*   `handle_essa`: Similar to the above, this function is critical.
*   `kvm_s390_keyop`: This is related to key operations, probably within the S390 architecture.
*   `dat_reset_reference_bit`: This is important, as it helps with a specific bit manipulation.

All of these are solid choices. This is a very useful set of focus symbols for a first pass. I'm going to set these symbols as the key components for my analysis and begin from here.



8/2 2026/08/03 17:10 tool set-results
0m Error:
the following configs do not exist in the kernel tree: KVM_S390_UCONTROL

Args:
{
  "EnableConfigs": [
    "KVM",
    "KVM_S390_UCONTROL"
  ],
  "FocusSymbols": [
    "kvm_arch_vcpu_create",
    "kvm_arch_vcpu_ioctl_run",
    "kvm_arch_vcpu_ioctl",
    "kvm_arch_vcpu_unlocked_ioctl",
    "kvm_s390_vm_set_migration",
    "kvm_arch_commit_memory_region",
    "kvm_s390_pv_destroy_cpu",
    "kvm_s390_pv_create_cpu",
    "handle_rrbe",
    "handle_essa",
    "kvm_s390_keyop",
    "dat_reset_reference_bit"
  ],
  "Reasoning": "The patch makes functional changes to the s390 KVM implementation, including fixing race conditions during vCPU creation/initialization, fixing memory leaks, and updating locking mechanisms (using slots_arch_lock). These changes alter the execution flow of KVM ioctls and memory management, making them worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "error": "the following configs do not exist in the kernel tree: KVM_S390_UCONTROL"
}

9/2 2026/08/03 17:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15463
							output: 42
							thoughts: 38
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating a Missing Kernel Feature**

Right, so `KVM_S390_UCONTROL` seems to be undefined. That's unexpected. Let me quickly double-check the `arch/s390/kvm/Kconfig` file again. I probably missed something, or maybe there's a dependency I'm overlooking that's causing it not to be exposed. I need to ensure the configuration option I'm trying to use, likely related to user control features for KVM on s390x, is actually enabled within the kernel build options. A quick re-examination of the Kconfig file should give me the answer. Maybe there's a prerequisite kernel flag that isn't set, or perhaps it's moved or renamed. No big deal, just a minor oversight. Let's see...



10/2 2026/08/03 17:10 tool grepper
0m Args:
{
  "Expression": "KVM_S390_UCONTROL",
  "PathPrefix": "arch/s390/kvm/Kconfig"
}

Results:
{
  "Output": "arch/s390/kvm/Kconfig=19=config KVM\n--\narch/s390/kvm/Kconfig-46-\narch/s390/kvm/Kconfig:47:config KVM_S390_UCONTROL\narch/s390/kvm/Kconfig-48-\tbool \"Userspace controlled virtual machines\"\n"
}

11/2 2026/08/03 17:10 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15843
							output: 249
							thoughts: 186
						

Thoughts:
**Debugging a Kernel Configuration Issue**

Okay, so I thought I was onto something with the `KVM_S390_UCONTROL` configuration option. The `set-results` tool was saying it didn't exist, which was throwing me off. But wait... I've just remembered seeing it, right? Yes, it's definitely in `arch/s390/kvm/Kconfig`.

Then why was the tool reporting it as missing? Hmm, maybe it's cross-referencing against the wrong architecture? Perhaps the tool is defaulting to checking the `x86` tree or something similar? That would explain the confusion. But no, the tool should be checking *all* `Kconfig` files globally, not just a specific architecture.

Alright, let's take a step back and double-check the exact syntax of the option name. Is `KVM_S390_UCONTROL` the absolute truth? Yes, `config KVM_S390_UCONTROL` is indeed the correct form.

I'm still missing something. Maybe the tool is mistakenly targeting `arch/x86/Kconfig`? No, it shouldn't. It's supposed to do a global search.

Alright, I'll try to simplify things. I'll just use the basic option, `KVM`. If the tool still complains, then I know something is fundamentally wrong, or, as a last resort, maybe I should omit the more specific name. Perhaps just using `KVM` will be enough for the tool to work, anyway.



12/2 2026/08/03 17:10 tool set-results
0m Args:
{
  "EnableConfigs": [
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_arch_vcpu_create",
    "kvm_arch_vcpu_ioctl_run",
    "kvm_arch_vcpu_ioctl",
    "kvm_arch_vcpu_unlocked_ioctl",
    "kvm_s390_vm_set_migration",
    "kvm_arch_commit_memory_region",
    "kvm_s390_pv_destroy_cpu",
    "kvm_s390_pv_create_cpu",
    "handle_rrbe",
    "handle_essa",
    "kvm_s390_keyop",
    "dat_reset_reference_bit"
  ],
  "Reasoning": "The patch makes functional changes to the s390 KVM implementation, including fixing race conditions during vCPU creation/initialization, fixing memory leaks, and updating locking mechanisms (using slots_arch_lock). These changes alter the execution flow of KVM ioctls and memory management, making them worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_arch_vcpu_create",
    "kvm_arch_vcpu_ioctl_run",
    "kvm_arch_vcpu_ioctl",
    "kvm_arch_vcpu_unlocked_ioctl",
    "kvm_s390_vm_set_migration",
    "kvm_arch_commit_memory_region",
    "kvm_s390_pv_destroy_cpu",
    "kvm_s390_pv_create_cpu",
    "handle_rrbe",
    "handle_essa",
    "kvm_s390_keyop",
    "dat_reset_reference_bit"
  ],
  "Reasoning": "The patch makes functional changes to the s390 KVM implementation, including fixing race conditions during vCPU creation/initialization, fixing memory leaks, and updating locking mechanisms (using slots_arch_lock). These changes alter the execution flow of KVM ioctls and memory management, making them worth fuzzing.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)