AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/05 18:22 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies the s390 VFIO AP (Adjunct Processor) driver in drivers/s390/crypto/, which is exclusive to the s390 architecture (CONFIG_VFIO_AP) and is neither compiled nor reachable on the target architecture (amd64).",
  "WorthFuzzing": false
}

1/1 2026/09/05 18:22 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit f3c869c7fcd3e292fb2f6d226424aa4a2174decb\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Sep 5 18:22:06 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c\nindex 940c0ff668bee..e054fd4a94970 100644\n--- a/drivers/s390/crypto/vfio_ap_ops.c\n+++ b/drivers/s390/crypto/vfio_ap_ops.c\n@@ -31,6 +31,7 @@\n #define AP_QUEUE_IN_USE \"in use\"\n \n #define AP_RESET_INTERVAL\t\t20\t/* Reset sleep interval (20ms)\t\t*/\n+#define AP_RESET_MAX_WAIT\t\t2000\t/* Maximum wait for reset (2000ms)\t*/\n \n static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev);\n static int vfio_ap_mdev_reset_qlist(struct list_head *qlist);\n@@ -226,16 +227,27 @@ static struct vfio_ap_queue *vfio_ap_mdev_get_queue(\n }\n \n /**\n- * vfio_ap_wait_for_irqclear - clears the IR bit or gives up after 5 tries\n- * @apqn: The AP Queue number\n- *\n- * Checks the IRQ bit for the status of this APQN using ap_tapq.\n- * Returns if the ap_tapq function succeeded and the bit is clear.\n- * Returns if ap_tapq function failed with invalid, deconfigured or\n- * checkstopped AP.\n- * Otherwise retries up to 5 times after waiting 20ms.\n+ * vfio_ap_wait_for_irqclear - wait for the IR bit to clear after a disable\n+ *\n+ * @apqn: the APQN of the queue\n+ *\n+ * Repeatedly polls the AP queue status via PQAP(TAPQ) every 20ms until the IR\n+ * bit is clear, the queue becomes non-operational, or 5 retries are exhausted.\n+ *\n+ * Because PQAP(AQIC) disable initiates an asynchronous process, a\n+ * condition-code 0 completion does not guarantee the IR bit has been cleared.\n+ * The host must confirm IR=0 before unpinning the NIB page to avoid a wild\n+ * DMA write to a freed page.\n+ *\n+ * Return:\n+ * - 0\t\tif the IR bit is clear (i.e., interrupts are disabled).\n+ *\n+ * - -ENODEV\tif the PQAP-TAPQ response code indicates the queue is not available,\n+ *\t\tis deconfigured, or is checkstopped (i.e., not operational).\n+ *\n+ * - -ETIMEDOUT\tthe function timed out before the IR bit was cleared.\n  */\n-static void vfio_ap_wait_for_irqclear(int apqn)\n+static int vfio_ap_wait_for_irqclear(int apqn)\n {\n \tstruct ap_queue_status status;\n \tint retry = 5;\n@@ -246,7 +258,7 @@ static void vfio_ap_wait_for_irqclear(int apqn)\n \t\tcase AP_RESPONSE_NORMAL:\n \t\tcase AP_RESPONSE_RESET_IN_PROGRESS:\n \t\t\tif (!status.irq_enabled)\n-\t\t\t\treturn;\n+\t\t\t\treturn 0;\n \t\t\tfallthrough;\n \t\tcase AP_RESPONSE_BUSY:\n \t\t\tmsleep(20);\n@@ -257,12 +269,15 @@ static void vfio_ap_wait_for_irqclear(int apqn)\n \t\tdefault:\n \t\t\tWARN_ONCE(1, \"%s: tapq rc %02x: %04x\\n\", __func__,\n \t\t\t\t  status.response_code, apqn);\n-\t\t\treturn;\n+\t\t\treturn -ENODEV;\n \t\t}\n \t} while (--retry);\n \n-\tWARN_ONCE(1, \"%s: tapq rc %02x: %04x could not clear IR bit\\n\",\n-\t\t  __func__, status.response_code, apqn);\n+\tWARN_ONCE(1, \"%s: tapq rc %02x: timed out waiting for interrupts disabled for %02x.%04x\\n\",\n+\t\t  __func__, status.response_code,\n+\t\t  AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\treturn -ETIMEDOUT;\n }\n \n /**\n@@ -289,20 +304,28 @@ static void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)\n }\n \n /**\n- * vfio_ap_irq_disable - disables and clears an ap_queue interrupt\n- * @q: The vfio_ap_queue\n+ * vfio_ap_irq_disable - disable interrupts for an AP queue\n+ * @q: the vfio_ap_queue\n  *\n- * Uses ap_aqic to disable the interruption and in case of success, reset\n- * in progress or IRQ disable command already proceeded: calls\n- * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear\n- * and calls vfio_ap_free_aqic_resources() to free the resources associated\n- * with the AP interrupt handling.\n+ * Issues PQAP(AQIC) to disable interrupts for the AP queue. On success\n+ * (AP_RESPONSE_NORMAL or AP_RESPONSE_OTHERWISE_CHANGED), polls via\n+ * vfio_ap_wait_for_irqclear() until the IR bit is confirmed clear before\n+ * freeing the pinned NIB page and unregistering the guest ISC. This wait is\n+ * necessary because AQIC disable is asynchronous: freeing the NIB before IR=0\n+ * is confirmed risks a wild DMA write to a freed host page.\n  *\n- * In the case the AP is busy, or a reset is in progress,\n- * retries after 20ms, up to 5 times.\n+ * Retries up to 5 times (with 20ms sleep) if the queue is busy or a reset is\n+ * in progress.\n  *\n- * Returns if ap_aqic function failed with invalid, deconfigured or\n- * checkstopped AP.\n+ * If the IR bit cannot be confirmed clear (timeout), the NIB page and guest\n+ * ISC are intentionally leaked. If the page were unpinned and returned to the\n+ * allocator, a subsequent hardware DMA write to that physical address would\n+ * corrupt memory belonging to a new owner — a wild DMA write that could crash\n+ * or compromise the host kernel.\n+ *\n+ * If the queue is non-operational (deconfigured, checkstopped, not available),\n+ * resources are freed immediately since the hardware can no longer write to\n+ * the NIB.\n  *\n  * Return: \u0026struct ap_queue_status\n  */\n@@ -310,15 +333,38 @@ static struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)\n {\n \tunion ap_qirq_ctrl aqic_gisa = { .value = 0 };\n \tstruct ap_queue_status status;\n-\tint retries = 5;\n+\tint retries = 5, ret;\n \n \tdo {\n \t\tstatus = ap_aqic(q-\u003eapqn, aqic_gisa, 0);\n \t\tswitch (status.response_code) {\n \t\tcase AP_RESPONSE_OTHERWISE_CHANGED:\n \t\tcase AP_RESPONSE_NORMAL:\n-\t\t\tvfio_ap_wait_for_irqclear(q-\u003eapqn);\n-\t\t\tgoto end_free;\n+\t\t\t/*\n+\t\t\t * AQIC disable was accepted (NORMAL), or the queue was\n+\t\t\t * already disabled or a prior async request is still\n+\t\t\t * completing (OTHERWISE_CHANGED).  In both cases, we must\n+\t\t\t * wait until interrupt processing has been disabled\n+\t\t\t * before proceeding.\n+\t\t\t */\n+\t\t\tret = vfio_ap_wait_for_irqclear(q-\u003eapqn);\n+\t\t\tif (ret == 0 || ret == -ENODEV)\n+\t\t\t\tgoto end_free;\n+\t\t\t/*\n+\t\t\t * Timed out waiting to confirm interrupts are disabled.\n+\t\t\t * If ap_aqic returned NORMAL, the guest would incorrectly\n+\t\t\t * interpret that as a successful disable and may free or\n+\t\t\t * reuse the NIB while hardware can still write to it.\n+\t\t\t * Zero the status word and set OTHERWISE_CHANGED to mimic\n+\t\t\t * what the hardware does for that response code. This\n+\t\t\t * signals to the guest that the reset operation did not\n+\t\t\t * complete.\n+\t\t\t */\n+\t\t\tif (status.response_code == AP_RESPONSE_NORMAL) {\n+\t\t\t\tmemset(\u0026status, 0, sizeof(status));\n+\t\t\t\tstatus.response_code = AP_RESPONSE_OTHERWISE_CHANGED;\n+\t\t\t}\n+\t\t\tgoto end_fail;\n \t\tcase AP_RESPONSE_RESET_IN_PROGRESS:\n \t\tcase AP_RESPONSE_BUSY:\n \t\t\tmsleep(20);\n@@ -326,18 +372,47 @@ static struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)\n \t\tcase AP_RESPONSE_Q_NOT_AVAIL:\n \t\tcase AP_RESPONSE_DECONFIGURED:\n \t\tcase AP_RESPONSE_CHECKSTOPPED:\n+\t\t\t/* AP not operational; no further interrupts possible */\n+\t\t\tWARN_ONCE(1, \"%s: ap_aqic status %d\\n\", __func__,\n+\t\t\t\t  status.response_code);\n+\t\t\tgoto end_free;\n \t\tcase AP_RESPONSE_INVALID_ADDRESS:\n \t\tdefault:\n-\t\t\t/* All cases in default means AP not operational */\n+\t\t\t/*\n+\t\t\t * The AQIC disable was rejected; IRQ is still enabled\n+\t\t\t * and the hardware still holds the NIB address. Do not\n+\t\t\t * free resources.\n+\t\t\t */\n \t\t\tWARN_ONCE(1, \"%s: ap_aqic status %d\\n\", __func__,\n \t\t\t\t  status.response_code);\n-\t\t\tgoto end_free;\n+\t\t\tgoto end_fail;\n \t\t}\n \t} while (retries--);\n \n \tWARN_ONCE(1, \"%s: ap_aqic status %d\\n\", __func__,\n \t\t  status.response_code);\n+\n+end_fail:\n+\t/*\n+\t * We are here either because the AQIC instruction failed to disable\n+\t * interrupts, or because IR=0 could not be confirmed. In either case\n+\t * the NIB page and guest ISC cannot be freed: hardware may still write\n+\t * to the NIB, and unpinning the page would allow it to be reallocated\n+\t * to a new owner. A subsequent hardware DMA write to that physical\n+\t * address would corrupt the new owner's memory — a wild DMA write that\n+\t * could crash or compromise the host kernel. The resources are\n+\t * therefore intentionally leaked.\n+\t */\n+\treturn status;\n+\n end_free:\n+\t/*\n+\t * This label is reached because the queue was successfully disabled,\n+\t * or because the queue is not operational or not available, in which case\n+\t * interrupts can not be processed, so free the AQIC resources - the pinned NIB\n+\t * page and the registered guest ISC - used to enable interrupts so they will\n+\t * not be leaked.\n+\t */\n \tvfio_ap_free_aqic_resources(q);\n \treturn status;\n }\n@@ -401,22 +476,29 @@ static int ensure_nib_shared(unsigned long addr)\n }\n \n /**\n- * vfio_ap_irq_enable - Enable Interruption for a APQN\n+ * vfio_ap_irq_enable - enable interrupts for an AP queue on behalf of a guest\n  *\n- * @q:\t the vfio_ap_queue holding AQIC parameters\n+ * @q:\t the vfio_ap_queue for which interrupts are to be enabled\n  * @isc: the guest ISC to register with the GIB interface\n- * @vcpu: the vcpu object containing the registers specifying the parameters\n- *\t  passed to the PQAP(AQIC) instruction.\n+ * @vcpu: the vcpu whose registers contain the PQAP(AQIC) parameters\n+ *\n+ * Pins the guest NIB page, registers the guest ISC with the GIB to obtain a\n+ * host ISC, and reissues PQAP(AQIC) with the translated host-absolute NIB\n+ * address and host ISC on behalf of the guest.\n  *\n- * Pin the NIB saved in *q\n- * Register the guest ISC to GIB interface and retrieve the\n- * host ISC to issue the host side PQAP/AQIC\n+ * The condition code and AP-queue status word returned by PQAP(AQIC) are\n+ * reflected back to the guest as-is. IRQ state verification (polling until\n+ * IR=1) is the responsibility of the guest AP bus, not the host.\n  *\n- * status.response_code may be set to AP_RESPONSE_INVALID_ADDRESS in case the\n- * vfio_pin_pages or kvm_s390_gisc_register failed.\n+ * Resource management is based solely on whether hardware accepted the new NIB:\n+ * - AP_RESPONSE_NORMAL (CC=0): hardware accepted the new NIB; the old pinned\n+ *   NIB page and registered guest ISC are freed and the new ones saved.\n+ * - All other responses: hardware did not accept the new NIB; the newly pinned\n+ *   page and registered ISC are freed and the previously saved resources are\n+ *   left intact.\n  *\n- * Otherwise return the ap_queue_status returned by the ap_aqic(),\n- * all retry handling will be done by the guest.\n+ * AP_RESPONSE_INVALID_ADDRESS is returned if vfio_pin_pages() or\n+ * kvm_s390_gisc_register() fails before the AQIC instruction is issued.\n  *\n  * Return: \u0026struct ap_queue_status\n  */\n@@ -428,11 +510,10 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,\n \tstruct ap_queue_status status = {};\n \tstruct kvm_s390_gisa *gisa;\n \tstruct page *h_page;\n-\tint nisc;\n+\tint nisc, ret;\n \tstruct kvm *kvm;\n \tphys_addr_t h_nib;\n \tdma_addr_t nib;\n-\tint ret;\n \n \t/* Verify that the notification indicator byte address is valid */\n \tif (vfio_ap_validate_nib(vcpu, \u0026nib)) {\n@@ -489,24 +570,33 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,\n \tstatus = ap_aqic(q-\u003eapqn, aqic_gisa, h_nib);\n \tswitch (status.response_code) {\n \tcase AP_RESPONSE_NORMAL:\n-\t\t/* See if we did clear older IRQ configuration */\n+\t\t/*\n+\t\t * Hardware accepted the new NIB address (CC=0). The old NIB and\n+\t\t * guest ISC are no longer used by hardware and can be freed.\n+\t\t * The new resources are saved for tracking and future teardown.\n+\t\t *\n+\t\t * IRQ state verification (polling until IR=1) is the\n+\t\t * responsibility of the guest AP bus, not the host. The\n+\t\t * condition code and status word are reflected back to the\n+\t\t * guest to respond to the PQAP-AQIC instruction.\n+\t\t */\n \t\tvfio_ap_free_aqic_resources(q);\n \t\tq-\u003esaved_iova = nib;\n \t\tq-\u003esaved_isc = isc;\n \t\tbreak;\n-\tcase AP_RESPONSE_OTHERWISE_CHANGED:\n-\t\t/* We could not modify IRQ settings: clear new configuration */\n+\tdefault:\n+\t\t/*\n+\t\t * Hardware did not accept the new NIB (CC=3 or error). The\n+\t\t * previously saved NIB and guest ISC remain active and must\n+\t\t * not be freed. Release the newly pinned page and registered\n+\t\t * ISC that were prepared for this (rejected) request.\n+\t\t */\n \t\tret = kvm_s390_gisc_unregister(kvm, isc);\n \t\tif (ret)\n \t\t\tVFIO_AP_DBF_WARN(\"%s: kvm_s390_gisc_unregister: rc=%d isc=%d, apqn=%#04x\\n\",\n \t\t\t\t\t __func__, ret, isc, q-\u003eapqn);\n \t\tvfio_unpin_pages(\u0026q-\u003ematrix_mdev-\u003evdev, nib, 1);\n \t\tbreak;\n-\tdefault:\n-\t\tpr_warn(\"%s: apqn %04x: response: %02x\\n\", __func__, q-\u003eapqn,\n-\t\t\tstatus.response_code);\n-\t\tvfio_ap_irq_disable(q);\n-\t\tbreak;\n \t}\n \n \tif (status.response_code != AP_RESPONSE_NORMAL) {\n@@ -635,7 +725,6 @@ static int handle_pqap(struct kvm_vcpu *vcpu)\n \t}\n \n \tstatus = vcpu-\u003erun-\u003es.regs.gprs[1];\n-\n \t/* If IR bit(16) is set we enable the interrupt */\n \tif ((status \u003e\u003e (63 - 16)) \u0026 0x01)\n \t\tqstatus = vfio_ap_irq_enable(q, status \u0026 0x07, vcpu);\n@@ -692,14 +781,14 @@ static bool _queue_passable(struct vfio_ap_queue *q)\n \tif (!q)\n \t\treturn false;\n \n-\tswitch (q-\u003ereset_status.response_code) {\n-\tcase AP_RESPONSE_NORMAL:\n-\tcase AP_RESPONSE_DECONFIGURED:\n-\tcase AP_RESPONSE_CHECKSTOPPED:\n-\t\treturn true;\n-\tdefault:\n-\t\treturn false;\n-\t}\n+\t/*\n+\t * A queue is only passable if zeroization was confirmed by\n+\t * apq_reset_check() via TAPQ status bit verification. This is\n+\t * indicated by reset_status.response_code == AP_RESPONSE_NORMAL (0).\n+\t * This is to protect against leaking the internal state of the queue\n+\t * to the guest.\n+\t */\n+\treturn q-\u003ereset_status.response_code == AP_RESPONSE_NORMAL;\n }\n \n /*\n@@ -1410,7 +1499,7 @@ static void vfio_ap_mdev_hot_unplug_domain(struct ap_matrix_mdev *matrix_mdev,\n {\n \tDECLARE_BITMAP(apqis, AP_DOMAINS);\n \n-\tbitmap_zero(apqis, AP_DEVICES);\n+\tbitmap_zero(apqis, AP_DOMAINS);\n \tset_bit_inv(apqi, apqis);\n \tvfio_ap_mdev_hot_unplug_domains(matrix_mdev, apqis);\n }\n@@ -1919,12 +2008,47 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)\n {\n \tswitch (status-\u003eresponse_code) {\n \tcase AP_RESPONSE_NORMAL:\n+\t\t/*\n+\t\t * This response code only indicates that the PQAP(ZAPQ) has\n+\t\t * been initiated. The following bit settings in the status\n+\t\t * returned from TAPQ must be verified to confirm that the\n+\t\t * asynchronous portion of the queue zeroization has completed.\n+\t\t */\n+\t\tif (status-\u003equeue_empty \u0026\u0026 !status-\u003ereplies_waiting \u0026\u0026\n+\t\t    !status-\u003eirq_enabled \u0026\u0026 !status-\u003easync)\n+\t\t\treturn 0;\n+\n+\t\t/* Async zeroization still in progress; keep waiting */\n+\t\treturn -EBUSY;\n+\n \tcase AP_RESPONSE_DECONFIGURED:\n \tcase AP_RESPONSE_CHECKSTOPPED:\n-\t\treturn 0;\n+\t\t/*\n+\t\t * The queue is non-operational: interrupts are not possible so\n+\t\t * AQIC resources can be safely freed. However, zeroization\n+\t\t * cannot be confirmed because all status bits are zeroed when\n+\t\t * these response codes are returned — there is no way to\n+\t\t * distinguish a zeroized queue from one that has not been\n+\t\t * zeroized. Return -ENODEV to signal that AQIC resources should\n+\t\t * be freed but that zeroization has not been confirmed.\n+\t\t */\n+\t\treturn -ENODEV;\n+\n \tcase AP_RESPONSE_RESET_IN_PROGRESS:\n-\tcase AP_RESPONSE_BUSY:\n+\t\t/*\n+\t\t * A reset is in progress. It may be the reset we issued or one\n+\t\t * issued prior to ours; either way, once it completes the queue\n+\t\t * will be zeroized, so keep waiting.\n+\t\t */\n \t\treturn -EBUSY;\n+\n+\tcase AP_RESPONSE_BUSY:\n+\t\t/*\n+\t\t * The queue is busy with something unrelated to a reset and our\n+\t\t * ZAPQ was rejected outright. Re-issue the ZAPQ.\n+\t\t */\n+\t\treturn -EAGAIN;\n+\n \tcase AP_RESPONSE_ASSOC_SECRET_NOT_UNIQUE:\n \tcase AP_RESPONSE_ASSOC_FAILED:\n \t\t/*\n@@ -1935,6 +2059,7 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)\n \t\t * a value indicating a reset needs to be performed again.\n \t\t */\n \t\treturn -EAGAIN;\n+\n \tdefault:\n \t\tWARN(true,\n \t\t     \"failed to verify reset of queue %02x.%04x: TAPQ rc=%u\\n\",\n@@ -1944,6 +2069,33 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)\n \t}\n }\n \n+static void report_aqic_resource_leak(struct vfio_ap_queue *q)\n+{\n+\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID || q-\u003esaved_iova) {\n+\t\tif (q-\u003ematrix_mdev) {\n+\t\t\tdev_warn_ratelimited(mdev_dev(q-\u003ematrix_mdev-\u003emdev),\n+\t\t\t\t\t     \"Reset timed out for APQN %02x.%04x: leaking AQIC resources (NIB page \u0026 GISC) to prevent host crash\\n\",\n+\t\t\t\t\t     AP_QID_CARD(q-\u003eapqn),\n+\t\t\t\t\t     AP_QID_QUEUE(q-\u003eapqn));\n+\t\t} else {\n+\t\t\tpr_warn_ratelimited(\"Reset timed out for APQN %02x.%04x: leaking AQIC resources (NIB page \u0026 GISC) to prevent host crash\\n\",\n+\t\t\t\t\t    AP_QID_CARD(q-\u003eapqn),\n+\t\t\t\t\t    AP_QID_QUEUE(q-\u003eapqn));\n+\t\t}\n+\t} else {\n+\t\tif (q-\u003ematrix_mdev) {\n+\t\t\tdev_warn_ratelimited(mdev_dev(q-\u003ematrix_mdev-\u003emdev),\n+\t\t\t\t\t     \"Reset timed out for APQN %02x.%04x\\n\",\n+\t\t\t\t\t     AP_QID_CARD(q-\u003eapqn),\n+\t\t\t\t\t     AP_QID_QUEUE(q-\u003eapqn));\n+\t\t} else {\n+\t\t\tpr_warn_ratelimited(\"Reset timed out for APQN %02x.%04x\\n\",\n+\t\t\t\t\t    AP_QID_CARD(q-\u003eapqn),\n+\t\t\t\t\t    AP_QID_QUEUE(q-\u003eapqn));\n+\t\t}\n+\t}\n+}\n+\n #define WAIT_MSG \"Waited %dms for reset of queue %02x.%04x (%u, %u, %u)\"\n \n static void apq_reset_check(struct work_struct *reset_work)\n@@ -1961,6 +2113,56 @@ static void apq_reset_check(struct work_struct *reset_work)\n \t\tret = apq_status_check(q-\u003eapqn, \u0026status);\n \t\tif (ret == -EIO)\n \t\t\treturn;\n+\t\tif (q-\u003ereset_max_wait \u0026\u0026 elapsed \u003e= q-\u003ereset_max_wait) {\n+\t\t\t/*\n+\t\t\t * Zeroization confirmed (ret == 0): the TAPQ status bits\n+\t\t\t * indicate the async portion of the ZAPQ completed\n+\t\t\t * successfully. Free AQIC resources and return.\n+\t\t\t *\n+\t\t\t * Queue non-operational (ret == -ENODEV): the queue is\n+\t\t\t * deconfigured or checkstopped; interrupts are not\n+\t\t\t * possible so AQIC resources can be safely freed.\n+\t\t\t * Zeroization cannot be confirmed in this state, but the\n+\t\t\t * queue cannot generate interrupts, so the NIB page is\n+\t\t\t * no longer a DMA target and it is safe to free it.\n+\t\t\t */\n+\t\t\tif (!ret || ret == -ENODEV)\n+\t\t\t\tgoto done;\n+\t\t\t/*\n+\t\t\t * Timed out without being able to verify zapq completed.\n+\t\t\t *\n+\t\t\t * The AQIC resources associated with this queue - the pinned\n+\t\t\t * page containing the NIB and the registered guest ISC -\n+\t\t\t * cannot be freed here. The NIB is the active DMA target\n+\t\t\t * for AP interrupt delivery until the reset completes;\n+\t\t\t * freeing the pinned page while the hardware may still\n+\t\t\t * write to it would result in a wild DMA write that could\n+\t\t\t * corrupt host memory.\n+\t\t\t *\n+\t\t\t * If the reset eventually completes, interrupts will be\n+\t\t\t * terminated and the pinned NIB page and ISC registration\n+\t\t\t * will be leaked. This is preferable to either a wild DMA\n+\t\t\t * write or waiting indefinitely: flush_work() callers hold\n+\t\t\t * the matrix_dev-\u003emdevs_lock mutex which serializes access\n+\t\t\t * to all mdev objects system-wide, so blocking here would\n+\t\t\t * hang all guests to which those mdevs are attached.\n+\t\t\t */\n+\t\t\treport_aqic_resource_leak(q);\n+\t\t\t/*\n+\t\t\t * Report the actual non-zero hardware response code, or\n+\t\t\t * synthesize AP_RESPONSE_RESET_IN_PROGRESS if TAPQ\n+\t\t\t * completed normally but the status bits failed to\n+\t\t\t * transition to their post-reset states.\n+\t\t\t */\n+\t\t\tif (status.response_code == AP_RESPONSE_NORMAL)\n+\t\t\t\tq-\u003ereset_status.response_code = AP_RESPONSE_RESET_IN_PROGRESS;\n+\t\t\telse\n+\t\t\t\tq-\u003ereset_status.response_code = status.response_code;\n+\n+\t\t\treturn;\n+\t\t}\n+\t\tif (!ret || ret == -ENODEV)\n+\t\t\tgoto done;\n \t\tif (ret == -EBUSY) {\n \t\t\tpr_notice_ratelimited(WAIT_MSG, elapsed,\n \t\t\t\t\t      AP_QID_CARD(q-\u003eapqn),\n@@ -1975,13 +2177,13 @@ static void apq_reset_check(struct work_struct *reset_work)\n \t\t\t    ret == -EAGAIN) {\n \t\t\t\tstatus = ap_zapq(q-\u003eapqn, 0);\n \t\t\t\tmemcpy(\u0026q-\u003ereset_status, \u0026status, sizeof(status));\n-\t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID)\n-\t\t\t\tvfio_ap_free_aqic_resources(q);\n-\t\t\tbreak;\n \t\t}\n \t}\n+\n+done:\n+\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID)\n+\t\tvfio_ap_free_aqic_resources(q);\n }\n \n static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q)\n@@ -2061,12 +2263,28 @@ static int vfio_ap_mdev_open_device(struct vfio_device *vdev)\n \treturn vfio_ap_mdev_set_kvm(matrix_mdev, vdev-\u003ekvm);\n }\n \n+static void vfio_ap_mdev_release_eventfds(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tif (matrix_mdev-\u003ereq_trigger) {\n+\t\teventfd_ctx_put(matrix_mdev-\u003ereq_trigger);\n+\t\tmatrix_mdev-\u003ereq_trigger = NULL;\n+\t}\n+\tif (matrix_mdev-\u003ecfg_chg_trigger) {\n+\t\teventfd_ctx_put(matrix_mdev-\u003ecfg_chg_trigger);\n+\t\tmatrix_mdev-\u003ecfg_chg_trigger = NULL;\n+\t}\n+}\n+\n static void vfio_ap_mdev_close_device(struct vfio_device *vdev)\n {\n \tstruct ap_matrix_mdev *matrix_mdev =\n \t\tcontainer_of(vdev, struct ap_matrix_mdev, vdev);\n \n \tvfio_ap_mdev_unset_kvm(matrix_mdev);\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tvfio_ap_mdev_release_eventfds(matrix_mdev);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n }\n \n static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)\n@@ -2457,8 +2675,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)\n \n \tq-\u003eapqn = apqn;\n \tq-\u003esaved_isc = VFIO_AP_ISC_INVALID;\n-\tmemset(\u0026q-\u003ereset_status, 0, sizeof(q-\u003ereset_status));\n+\tq-\u003ereset_max_wait = AP_RESET_MAX_WAIT;\n \tINIT_WORK(\u0026q-\u003ereset_work, apq_reset_check);\n+\tvfio_ap_mdev_reset_queue(q);\n+\tflush_work(\u0026q-\u003ereset_work);\n \n \tif (matrix_mdev) {\n \t\tvfio_ap_mdev_link_queue(matrix_mdev, q);\n@@ -2541,8 +2761,19 @@ void vfio_ap_mdev_remove_queue(struct ap_device *apdev)\n \t\tvfio_ap_unlink_queue_fr_mdev(q);\n \n \tdev_set_drvdata(\u0026apdev-\u003edevice, NULL);\n-\tkfree(q);\n \trelease_update_locks_for_mdev(matrix_mdev);\n+\n+\tif (q-\u003ereset_status.response_code != AP_RESPONSE_NORMAL) {\n+\t\t/*\n+\t\t * Loop until zeroization of queue is verified so we don't leak\n+\t\t * the internal state of the queue to the caller.\n+\t\t */\n+\t\tq-\u003ereset_max_wait = 0;\n+\t\tvfio_ap_mdev_reset_queue(q);\n+\t\tflush_work(\u0026q-\u003ereset_work);\n+\t}\n+\n+\tkfree(q);\n }\n \n /**\n@@ -2697,11 +2928,11 @@ static void vfio_ap_mdev_on_cfg_remove(struct ap_config_info *cur_config_info,\n \tdo_remove |= bitmap_andnot(aqrem,\n \t\t\t\t   (unsigned long *)prev_config_info-\u003eaqm,\n \t\t\t\t   (unsigned long *)cur_config_info-\u003eaqm,\n-\t\t\t\t   AP_DEVICES);\n+\t\t\t\t   AP_DOMAINS);\n \tdo_remove |= bitmap_andnot(cdrem,\n \t\t\t\t   (unsigned long *)prev_config_info-\u003eadm,\n \t\t\t\t   (unsigned long *)cur_config_info-\u003eadm,\n-\t\t\t\t   AP_DEVICES);\n+\t\t\t\t   AP_DOMAINS);\n \n \tif (do_remove)\n \t\tvfio_ap_mdev_cfg_remove(aprem, aqrem, cdrem);\n@@ -2812,7 +3043,7 @@ static void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add,\n \t\tbitmap_and(matrix_mdev-\u003eaqm_add,\n \t\t\t   matrix_mdev-\u003ematrix.aqm, aqm_add, AP_DOMAINS);\n \t\tbitmap_and(matrix_mdev-\u003eadm_add,\n-\t\t\t   matrix_mdev-\u003ematrix.adm, adm_add, AP_DEVICES);\n+\t\t\t   matrix_mdev-\u003ematrix.adm, adm_add, AP_DOMAINS);\n \n \t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n \t}\ndiff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h\nindex 9bff666b0b35d..914ea5d41d330 100644\n--- a/drivers/s390/crypto/vfio_ap_private.h\n+++ b/drivers/s390/crypto/vfio_ap_private.h\n@@ -139,6 +139,8 @@ struct ap_matrix_mdev {\n  *\t\t that need to be reset\n  * @reset_status: the status from the last reset of the queue\n  * @reset_work: work to wait for queue reset to complete\n+ * @reset_max_wait: the max amount of time to wait for a reset to complete;\n+ *\t\t    specifying 0 means wait as long as it takes.\n  */\n struct vfio_ap_queue {\n \tstruct ap_matrix_mdev *matrix_mdev;\n@@ -150,6 +152,7 @@ struct vfio_ap_queue {\n \tstruct list_head reset_qnode;\n \tstruct ap_queue_status reset_status;\n \tstruct work_struct reset_work;\n+\tunsigned int reset_max_wait;\n };\n \n int vfio_ap_mdev_register(void);\n"
}

2/1 2026/09/05 18:22 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the s390 VFIO AP (Adjunct Processor) driver in drivers/s390/crypto/, which is exclusive to the s390 architecture (CONFIG_VFIO_AP) and is neither compiled nor reachable on the target architecture (amd64).",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit f3c869c7fcd3e292fb2f6d226424aa4a2174decb
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Sep 5 18:22:06 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 940c0ff668bee..e054fd4a94970 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -31,6 +31,7 @@
 #define AP_QUEUE_IN_USE "in use"
 
 #define AP_RESET_INTERVAL		20	/* Reset sleep interval (20ms)		*/
+#define AP_RESET_MAX_WAIT		2000	/* Maximum wait for reset (2000ms)	*/
 
 static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev);
 static int vfio_ap_mdev_reset_qlist(struct list_head *qlist);
@@ -226,16 +227,27 @@ static struct vfio_ap_queue *vfio_ap_mdev_get_queue(
 }
 
 /**
- * vfio_ap_wait_for_irqclear - clears the IR bit or gives up after 5 tries
- * @apqn: The AP Queue number
- *
- * Checks the IRQ bit for the status of this APQN using ap_tapq.
- * Returns if the ap_tapq function succeeded and the bit is clear.
- * Returns if ap_tapq function failed with invalid, deconfigured or
- * checkstopped AP.
- * Otherwise retries up to 5 times after waiting 20ms.
+ * vfio_ap_wait_for_irqclear - wait for the IR bit to clear after a disable
+ *
+ * @apqn: the APQN of the queue
+ *
+ * Repeatedly polls the AP queue status via PQAP(TAPQ) every 20ms until the IR
+ * bit is clear, the queue becomes non-operational, or 5 retries are exhausted.
+ *
+ * Because PQAP(AQIC) disable initiates an asynchronous process, a
+ * condition-code 0 completion does not guarantee the IR bit has been cleared.
+ * The host must confirm IR=0 before unpinning the NIB page to avoid a wild
+ * DMA write to a freed page.
+ *
+ * Return:
+ * - 0		if the IR bit is clear (i.e., interrupts are disabled).
+ *
+ * - -ENODEV	if the PQAP-TAPQ response code indicates the queue is not available,
+ *		is deconfigured, or is checkstopped (i.e., not operational).
+ *
+ * - -ETIMEDOUT	the function timed out before the IR bit was cleared.
  */
-static void vfio_ap_wait_for_irqclear(int apqn)
+static int vfio_ap_wait_for_irqclear(int apqn)
 {
 	struct ap_queue_status status;
 	int retry = 5;
@@ -246,7 +258,7 @@ static void vfio_ap_wait_for_irqclear(int apqn)
 		case AP_RESPONSE_NORMAL:
 		case AP_RESPONSE_RESET_IN_PROGRESS:
 			if (!status.irq_enabled)
-				return;
+				return 0;
 			fallthrough;
 		case AP_RESPONSE_BUSY:
 			msleep(20);
@@ -257,12 +269,15 @@ static void vfio_ap_wait_for_irqclear(int apqn)
 		default:
 			WARN_ONCE(1, "%s: tapq rc %02x: %04x\n", __func__,
 				  status.response_code, apqn);
-			return;
+			return -ENODEV;
 		}
 	} while (--retry);
 
-	WARN_ONCE(1, "%s: tapq rc %02x: %04x could not clear IR bit\n",
-		  __func__, status.response_code, apqn);
+	WARN_ONCE(1, "%s: tapq rc %02x: timed out waiting for interrupts disabled for %02x.%04x\n",
+		  __func__, status.response_code,
+		  AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+	return -ETIMEDOUT;
 }
 
 /**
@@ -289,20 +304,28 @@ static void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)
 }
 
 /**
- * vfio_ap_irq_disable - disables and clears an ap_queue interrupt
- * @q: The vfio_ap_queue
+ * vfio_ap_irq_disable - disable interrupts for an AP queue
+ * @q: the vfio_ap_queue
  *
- * Uses ap_aqic to disable the interruption and in case of success, reset
- * in progress or IRQ disable command already proceeded: calls
- * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear
- * and calls vfio_ap_free_aqic_resources() to free the resources associated
- * with the AP interrupt handling.
+ * Issues PQAP(AQIC) to disable interrupts for the AP queue. On success
+ * (AP_RESPONSE_NORMAL or AP_RESPONSE_OTHERWISE_CHANGED), polls via
+ * vfio_ap_wait_for_irqclear() until the IR bit is confirmed clear before
+ * freeing the pinned NIB page and unregistering the guest ISC. This wait is
+ * necessary because AQIC disable is asynchronous: freeing the NIB before IR=0
+ * is confirmed risks a wild DMA write to a freed host page.
  *
- * In the case the AP is busy, or a reset is in progress,
- * retries after 20ms, up to 5 times.
+ * Retries up to 5 times (with 20ms sleep) if the queue is busy or a reset is
+ * in progress.
  *
- * Returns if ap_aqic function failed with invalid, deconfigured or
- * checkstopped AP.
+ * If the IR bit cannot be confirmed clear (timeout), the NIB page and guest
+ * ISC are intentionally leaked. If the page were unpinned and returned to the
+ * allocator, a subsequent hardware DMA write to that physical address would
+ * corrupt memory belonging to a new owner — a wild DMA write that could crash
+ * or compromise the host kernel.
+ *
+ * If the queue is non-operational (deconfigured, checkstopped, not available),
+ * resources are freed immediately since the hardware can no longer write to
+ * the NIB.
  *
  * Return: &struct ap_queue_status
  */
@@ -310,15 +333,38 @@ static struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)
 {
 	union ap_qirq_ctrl aqic_gisa = { .value = 0 };
 	struct ap_queue_status status;
-	int retries = 5;
+	int retries = 5, ret;
 
 	do {
 		status = ap_aqic(q->apqn, aqic_gisa, 0);
 		switch (status.response_code) {
 		case AP_RESPONSE_OTHERWISE_CHANGED:
 		case AP_RESPONSE_NORMAL:
-			vfio_ap_wait_for_irqclear(q->apqn);
-			goto end_free;
+			/*
+			 * AQIC disable was accepted (NORMAL), or the queue was
+			 * already disabled or a prior async request is still
+			 * completing (OTHERWISE_CHANGED).  In both cases, we must
+			 * wait until interrupt processing has been disabled
+			 * before proceeding.
+			 */
+			ret = vfio_ap_wait_for_irqclear(q->apqn);
+			if (ret == 0 || ret == -ENODEV)
+				goto end_free;
+			/*
+			 * Timed out waiting to confirm interrupts are disabled.
+			 * If ap_aqic returned NORMAL, the guest would incorrectly
+			 * interpret that as a successful disable and may free or
+			 * reuse the NIB while hardware can still write to it.
+			 * Zero the status word and set OTHERWISE_CHANGED to mimic
+			 * what the hardware does for that response code. This
+			 * signals to the guest that the reset operation did not
+			 * complete.
+			 */
+			if (status.response_code == AP_RESPONSE_NORMAL) {
+				memset(&status, 0, sizeof(status));
+				status.response_code = AP_RESPONSE_OTHERWISE_CHANGED;
+			}
+			goto end_fail;
 		case AP_RESPONSE_RESET_IN_PROGRESS:
 		case AP_RESPONSE_BUSY:
 			msleep(20);
@@ -326,18 +372,47 @@ static struct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)
 		case AP_RESPONSE_Q_NOT_AVAIL:
 		case AP_RESPONSE_DECONFIGURED:
 		case AP_RESPONSE_CHECKSTOPPED:
+			/* AP not operational; no further interrupts possible */
+			WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
+				  status.response_code);
+			goto end_free;
 		case AP_RESPONSE_INVALID_ADDRESS:
 		default:
-			/* All cases in default means AP not operational */
+			/*
+			 * The AQIC disable was rejected; IRQ is still enabled
+			 * and the hardware still holds the NIB address. Do not
+			 * free resources.
+			 */
 			WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
 				  status.response_code);
-			goto end_free;
+			goto end_fail;
 		}
 	} while (retries--);
 
 	WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
 		  status.response_code);
+
+end_fail:
+	/*
+	 * We are here either because the AQIC instruction failed to disable
+	 * interrupts, or because IR=0 could not be confirmed. In either case
+	 * the NIB page and guest ISC cannot be freed: hardware may still write
+	 * to the NIB, and unpinning the page would allow it to be reallocated
+	 * to a new owner. A subsequent hardware DMA write to that physical
+	 * address would corrupt the new owner's memory — a wild DMA write that
+	 * could crash or compromise the host kernel. The resources are
+	 * therefore intentionally leaked.
+	 */
+	return status;
+
 end_free:
+	/*
+	 * This label is reached because the queue was successfully disabled,
+	 * or because the queue is not operational or not available, in which case
+	 * interrupts can not be processed, so free the AQIC resources - the pinned NIB
+	 * page and the registered guest ISC - used to enable interrupts so they will
+	 * not be leaked.
+	 */
 	vfio_ap_free_aqic_resources(q);
 	return status;
 }
@@ -401,22 +476,29 @@ static int ensure_nib_shared(unsigned long addr)
 }
 
 /**
- * vfio_ap_irq_enable - Enable Interruption for a APQN
+ * vfio_ap_irq_enable - enable interrupts for an AP queue on behalf of a guest
  *
- * @q:	 the vfio_ap_queue holding AQIC parameters
+ * @q:	 the vfio_ap_queue for which interrupts are to be enabled
  * @isc: the guest ISC to register with the GIB interface
- * @vcpu: the vcpu object containing the registers specifying the parameters
- *	  passed to the PQAP(AQIC) instruction.
+ * @vcpu: the vcpu whose registers contain the PQAP(AQIC) parameters
+ *
+ * Pins the guest NIB page, registers the guest ISC with the GIB to obtain a
+ * host ISC, and reissues PQAP(AQIC) with the translated host-absolute NIB
+ * address and host ISC on behalf of the guest.
  *
- * Pin the NIB saved in *q
- * Register the guest ISC to GIB interface and retrieve the
- * host ISC to issue the host side PQAP/AQIC
+ * The condition code and AP-queue status word returned by PQAP(AQIC) are
+ * reflected back to the guest as-is. IRQ state verification (polling until
+ * IR=1) is the responsibility of the guest AP bus, not the host.
  *
- * status.response_code may be set to AP_RESPONSE_INVALID_ADDRESS in case the
- * vfio_pin_pages or kvm_s390_gisc_register failed.
+ * Resource management is based solely on whether hardware accepted the new NIB:
+ * - AP_RESPONSE_NORMAL (CC=0): hardware accepted the new NIB; the old pinned
+ *   NIB page and registered guest ISC are freed and the new ones saved.
+ * - All other responses: hardware did not accept the new NIB; the newly pinned
+ *   page and registered ISC are freed and the previously saved resources are
+ *   left intact.
  *
- * Otherwise return the ap_queue_status returned by the ap_aqic(),
- * all retry handling will be done by the guest.
+ * AP_RESPONSE_INVALID_ADDRESS is returned if vfio_pin_pages() or
+ * kvm_s390_gisc_register() fails before the AQIC instruction is issued.
  *
  * Return: &struct ap_queue_status
  */
@@ -428,11 +510,10 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
 	struct ap_queue_status status = {};
 	struct kvm_s390_gisa *gisa;
 	struct page *h_page;
-	int nisc;
+	int nisc, ret;
 	struct kvm *kvm;
 	phys_addr_t h_nib;
 	dma_addr_t nib;
-	int ret;
 
 	/* Verify that the notification indicator byte address is valid */
 	if (vfio_ap_validate_nib(vcpu, &nib)) {
@@ -489,24 +570,33 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,
 	status = ap_aqic(q->apqn, aqic_gisa, h_nib);
 	switch (status.response_code) {
 	case AP_RESPONSE_NORMAL:
-		/* See if we did clear older IRQ configuration */
+		/*
+		 * Hardware accepted the new NIB address (CC=0). The old NIB and
+		 * guest ISC are no longer used by hardware and can be freed.
+		 * The new resources are saved for tracking and future teardown.
+		 *
+		 * IRQ state verification (polling until IR=1) is the
+		 * responsibility of the guest AP bus, not the host. The
+		 * condition code and status word are reflected back to the
+		 * guest to respond to the PQAP-AQIC instruction.
+		 */
 		vfio_ap_free_aqic_resources(q);
 		q->saved_iova = nib;
 		q->saved_isc = isc;
 		break;
-	case AP_RESPONSE_OTHERWISE_CHANGED:
-		/* We could not modify IRQ settings: clear new configuration */
+	default:
+		/*
+		 * Hardware did not accept the new NIB (CC=3 or error). The
+		 * previously saved NIB and guest ISC remain active and must
+		 * not be freed. Release the newly pinned page and registered
+		 * ISC that were prepared for this (rejected) request.
+		 */
 		ret = kvm_s390_gisc_unregister(kvm, isc);
 		if (ret)
 			VFIO_AP_DBF_WARN("%s: kvm_s390_gisc_unregister: rc=%d isc=%d, apqn=%#04x\n",
 					 __func__, ret, isc, q->apqn);
 		vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1);
 		break;
-	default:
-		pr_warn("%s: apqn %04x: response: %02x\n", __func__, q->apqn,
-			status.response_code);
-		vfio_ap_irq_disable(q);
-		break;
 	}
 
 	if (status.response_code != AP_RESPONSE_NORMAL) {
@@ -635,7 +725,6 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
 	}
 
 	status = vcpu->run->s.regs.gprs[1];
-
 	/* If IR bit(16) is set we enable the interrupt */
 	if ((status >> (63 - 16)) & 0x01)
 		qstatus = vfio_ap_irq_enable(q, status & 0x07, vcpu);
@@ -692,14 +781,14 @@ static bool _queue_passable(struct vfio_ap_queue *q)
 	if (!q)
 		return false;
 
-	switch (q->reset_status.response_code) {
-	case AP_RESPONSE_NORMAL:
-	case AP_RESPONSE_DECONFIGURED:
-	case AP_RESPONSE_CHECKSTOPPED:
-		return true;
-	default:
-		return false;
-	}
+	/*
+	 * A queue is only passable if zeroization was confirmed by
+	 * apq_reset_check() via TAPQ status bit verification. This is
+	 * indicated by reset_status.response_code == AP_RESPONSE_NORMAL (0).
+	 * This is to protect against leaking the internal state of the queue
+	 * to the guest.
+	 */
+	return q->reset_status.response_code == AP_RESPONSE_NORMAL;
 }
 
 /*
@@ -1410,7 +1499,7 @@ static void vfio_ap_mdev_hot_unplug_domain(struct ap_matrix_mdev *matrix_mdev,
 {
 	DECLARE_BITMAP(apqis, AP_DOMAINS);
 
-	bitmap_zero(apqis, AP_DEVICES);
+	bitmap_zero(apqis, AP_DOMAINS);
 	set_bit_inv(apqi, apqis);
 	vfio_ap_mdev_hot_unplug_domains(matrix_mdev, apqis);
 }
@@ -1919,12 +2008,47 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)
 {
 	switch (status->response_code) {
 	case AP_RESPONSE_NORMAL:
+		/*
+		 * This response code only indicates that the PQAP(ZAPQ) has
+		 * been initiated. The following bit settings in the status
+		 * returned from TAPQ must be verified to confirm that the
+		 * asynchronous portion of the queue zeroization has completed.
+		 */
+		if (status->queue_empty && !status->replies_waiting &&
+		    !status->irq_enabled && !status->async)
+			return 0;
+
+		/* Async zeroization still in progress; keep waiting */
+		return -EBUSY;
+
 	case AP_RESPONSE_DECONFIGURED:
 	case AP_RESPONSE_CHECKSTOPPED:
-		return 0;
+		/*
+		 * The queue is non-operational: interrupts are not possible so
+		 * AQIC resources can be safely freed. However, zeroization
+		 * cannot be confirmed because all status bits are zeroed when
+		 * these response codes are returned — there is no way to
+		 * distinguish a zeroized queue from one that has not been
+		 * zeroized. Return -ENODEV to signal that AQIC resources should
+		 * be freed but that zeroization has not been confirmed.
+		 */
+		return -ENODEV;
+
 	case AP_RESPONSE_RESET_IN_PROGRESS:
-	case AP_RESPONSE_BUSY:
+		/*
+		 * A reset is in progress. It may be the reset we issued or one
+		 * issued prior to ours; either way, once it completes the queue
+		 * will be zeroized, so keep waiting.
+		 */
 		return -EBUSY;
+
+	case AP_RESPONSE_BUSY:
+		/*
+		 * The queue is busy with something unrelated to a reset and our
+		 * ZAPQ was rejected outright. Re-issue the ZAPQ.
+		 */
+		return -EAGAIN;
+
 	case AP_RESPONSE_ASSOC_SECRET_NOT_UNIQUE:
 	case AP_RESPONSE_ASSOC_FAILED:
 		/*
@@ -1935,6 +2059,7 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)
 		 * a value indicating a reset needs to be performed again.
 		 */
 		return -EAGAIN;
+
 	default:
 		WARN(true,
 		     "failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n",
@@ -1944,6 +2069,33 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)
 	}
 }
 
+static void report_aqic_resource_leak(struct vfio_ap_queue *q)
+{
+	if (q->saved_isc != VFIO_AP_ISC_INVALID || q->saved_iova) {
+		if (q->matrix_mdev) {
+			dev_warn_ratelimited(mdev_dev(q->matrix_mdev->mdev),
+					     "Reset timed out for APQN %02x.%04x: leaking AQIC resources (NIB page & GISC) to prevent host crash\n",
+					     AP_QID_CARD(q->apqn),
+					     AP_QID_QUEUE(q->apqn));
+		} else {
+			pr_warn_ratelimited("Reset timed out for APQN %02x.%04x: leaking AQIC resources (NIB page & GISC) to prevent host crash\n",
+					    AP_QID_CARD(q->apqn),
+					    AP_QID_QUEUE(q->apqn));
+		}
+	} else {
+		if (q->matrix_mdev) {
+			dev_warn_ratelimited(mdev_dev(q->matrix_mdev->mdev),
+					     "Reset timed out for APQN %02x.%04x\n",
+					     AP_QID_CARD(q->apqn),
+					     AP_QID_QUEUE(q->apqn));
+		} else {
+			pr_warn_ratelimited("Reset timed out for APQN %02x.%04x\n",
+					    AP_QID_CARD(q->apqn),
+					    AP_QID_QUEUE(q->apqn));
+		}
+	}
+}
+
 #define WAIT_MSG "Waited %dms for reset of queue %02x.%04x (%u, %u, %u)"
 
 static void apq_reset_check(struct work_struct *reset_work)
@@ -1961,6 +2113,56 @@ static void apq_reset_check(struct work_struct *reset_work)
 		ret = apq_status_check(q->apqn, &status);
 		if (ret == -EIO)
 			return;
+		if (q->reset_max_wait && elapsed >= q->reset_max_wait) {
+			/*
+			 * Zeroization confirmed (ret == 0): the TAPQ status bits
+			 * indicate the async portion of the ZAPQ completed
+			 * successfully. Free AQIC resources and return.
+			 *
+			 * Queue non-operational (ret == -ENODEV): the queue is
+			 * deconfigured or checkstopped; interrupts are not
+			 * possible so AQIC resources can be safely freed.
+			 * Zeroization cannot be confirmed in this state, but the
+			 * queue cannot generate interrupts, so the NIB page is
+			 * no longer a DMA target and it is safe to free it.
+			 */
+			if (!ret || ret == -ENODEV)
+				goto done;
+			/*
+			 * Timed out without being able to verify zapq completed.
+			 *
+			 * The AQIC resources associated with this queue - the pinned
+			 * page containing the NIB and the registered guest ISC -
+			 * cannot be freed here. The NIB is the active DMA target
+			 * for AP interrupt delivery until the reset completes;
+			 * freeing the pinned page while the hardware may still
+			 * write to it would result in a wild DMA write that could
+			 * corrupt host memory.
+			 *
+			 * If the reset eventually completes, interrupts will be
+			 * terminated and the pinned NIB page and ISC registration
+			 * will be leaked. This is preferable to either a wild DMA
+			 * write or waiting indefinitely: flush_work() callers hold
+			 * the matrix_dev->mdevs_lock mutex which serializes access
+			 * to all mdev objects system-wide, so blocking here would
+			 * hang all guests to which those mdevs are attached.
+			 */
+			report_aqic_resource_leak(q);
+			/*
+			 * Report the actual non-zero hardware response code, or
+			 * synthesize AP_RESPONSE_RESET_IN_PROGRESS if TAPQ
+			 * completed normally but the status bits failed to
+			 * transition to their post-reset states.
+			 */
+			if (status.response_code == AP_RESPONSE_NORMAL)
+				q->reset_status.response_code = AP_RESPONSE_RESET_IN_PROGRESS;
+			else
+				q->reset_status.response_code = status.response_code;
+
+			return;
+		}
+		if (!ret || ret == -ENODEV)
+			goto done;
 		if (ret == -EBUSY) {
 			pr_notice_ratelimited(WAIT_MSG, elapsed,
 					      AP_QID_CARD(q->apqn),
@@ -1975,13 +2177,13 @@ static void apq_reset_check(struct work_struct *reset_work)
 			    ret == -EAGAIN) {
 				status = ap_zapq(q->apqn, 0);
 				memcpy(&q->reset_status, &status, sizeof(status));
-				continue;
 			}
-			if (q->saved_isc != VFIO_AP_ISC_INVALID)
-				vfio_ap_free_aqic_resources(q);
-			break;
 		}
 	}
+
+done:
+	if (q->saved_isc != VFIO_AP_ISC_INVALID)
+		vfio_ap_free_aqic_resources(q);
 }
 
 static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q)
@@ -2061,12 +2263,28 @@ static int vfio_ap_mdev_open_device(struct vfio_device *vdev)
 	return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
 }
 
+static void vfio_ap_mdev_release_eventfds(struct ap_matrix_mdev *matrix_mdev)
+{
+	if (matrix_mdev->req_trigger) {
+		eventfd_ctx_put(matrix_mdev->req_trigger);
+		matrix_mdev->req_trigger = NULL;
+	}
+	if (matrix_mdev->cfg_chg_trigger) {
+		eventfd_ctx_put(matrix_mdev->cfg_chg_trigger);
+		matrix_mdev->cfg_chg_trigger = NULL;
+	}
+}
+
 static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
 {
 	struct ap_matrix_mdev *matrix_mdev =
 		container_of(vdev, struct ap_matrix_mdev, vdev);
 
 	vfio_ap_mdev_unset_kvm(matrix_mdev);
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	vfio_ap_mdev_release_eventfds(matrix_mdev);
+	mutex_unlock(&matrix_dev->mdevs_lock);
 }
 
 static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)
@@ -2457,8 +2675,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
 
 	q->apqn = apqn;
 	q->saved_isc = VFIO_AP_ISC_INVALID;
-	memset(&q->reset_status, 0, sizeof(q->reset_status));
+	q->reset_max_wait = AP_RESET_MAX_WAIT;
 	INIT_WORK(&q->reset_work, apq_reset_check);
+	vfio_ap_mdev_reset_queue(q);
+	flush_work(&q->reset_work);
 
 	if (matrix_mdev) {
 		vfio_ap_mdev_link_queue(matrix_mdev, q);
@@ -2541,8 +2761,19 @@ void vfio_ap_mdev_remove_queue(struct ap_device *apdev)
 		vfio_ap_unlink_queue_fr_mdev(q);
 
 	dev_set_drvdata(&apdev->device, NULL);
-	kfree(q);
 	release_update_locks_for_mdev(matrix_mdev);
+
+	if (q->reset_status.response_code != AP_RESPONSE_NORMAL) {
+		/*
+		 * Loop until zeroization of queue is verified so we don't leak
+		 * the internal state of the queue to the caller.
+		 */
+		q->reset_max_wait = 0;
+		vfio_ap_mdev_reset_queue(q);
+		flush_work(&q->reset_work);
+	}
+
+	kfree(q);
 }
 
 /**
@@ -2697,11 +2928,11 @@ static void vfio_ap_mdev_on_cfg_remove(struct ap_config_info *cur_config_info,
 	do_remove |= bitmap_andnot(aqrem,
 				   (unsigned long *)prev_config_info->aqm,
 				   (unsigned long *)cur_config_info->aqm,
-				   AP_DEVICES);
+				   AP_DOMAINS);
 	do_remove |= bitmap_andnot(cdrem,
 				   (unsigned long *)prev_config_info->adm,
 				   (unsigned long *)cur_config_info->adm,
-				   AP_DEVICES);
+				   AP_DOMAINS);
 
 	if (do_remove)
 		vfio_ap_mdev_cfg_remove(aprem, aqrem, cdrem);
@@ -2812,7 +3043,7 @@ static void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add,
 		bitmap_and(matrix_mdev->aqm_add,
 			   matrix_mdev->matrix.aqm, aqm_add, AP_DOMAINS);
 		bitmap_and(matrix_mdev->adm_add,
-			   matrix_mdev->matrix.adm, adm_add, AP_DEVICES);
+			   matrix_mdev->matrix.adm, adm_add, AP_DOMAINS);
 
 		mutex_unlock(&matrix_dev->mdevs_lock);
 	}
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index 9bff666b0b35d..914ea5d41d330 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -139,6 +139,8 @@ struct ap_matrix_mdev {
  *		 that need to be reset
  * @reset_status: the status from the last reset of the queue
  * @reset_work: work to wait for queue reset to complete
+ * @reset_max_wait: the max amount of time to wait for a reset to complete;
+ *		    specifying 0 means wait as long as it takes.
  */
 struct vfio_ap_queue {
 	struct ap_matrix_mdev *matrix_mdev;
@@ -150,6 +152,7 @@ struct vfio_ap_queue {
 	struct list_head reset_qnode;
 	struct ap_queue_status reset_status;
 	struct work_struct reset_work;
+	unsigned int reset_max_wait;
 };
 
 int vfio_ap_mdev_register(void);

3/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11046
							output: 31
							thoughts: 92
						

Thoughts:
**Analyzing the Patch for AMD64**

Okay, let's dive into this patch. My immediate focus is on understanding the modifications and their impact, especially considering the target architecture is amd64. I see the changes are confined to `drivers/s390/crypto/vfio_ap_ops.c` and `drivers/s390/crypto/vfio_ap_private.h`. Since these are in the s390 directory, I'll need to immediately think about the cross-architecture implications and whether the code utilizes any of the features that make s390 distinct from amd64. Therefore, I need to verify Kconfig and architecture dependencies for the source file. This is crucial for determining how this patch integrates and any potential architectural constraints. This file is probably interacting with VFIO and AP, therefore further research into those specific subsystems may be important.



4/2 2026/09/05 18:22 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config VFIO_AP",
  "PathPrefix": "drivers/s390"
}

Results:
{
  "error": "no matches"
}

5/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11370
							output: 30
							thoughts: 16
						

Thoughts:
Here's your summary, as if I were thinking it:

**Navigating the S390 Crypto Landscape**

Okay, so I need to get my bearings and locate the Kconfig file within the s390 crypto driver structure. Given the architecture-specific nature, I'll start by drilling down into the `drivers/s390` directory. My expectation is that the crypto-related configuration will be organized beneath that. I'm reasonably confident, based on previous experience, that the configuration options will be managed via Kconfig. It's the standard for the kernel, and the crypto subsystem is fairly well-established. Let's dig in and see what we find in `drivers/s390/crypto`. From there, I'll be able to trace dependencies and understand how the crypto features are being configured.



6/2 2026/09/05 18:22 tool grepper
0m Args:
{
  "Expression": "VFIO_AP",
  "PathPrefix": "drivers/s390"
}

Results:
{
  "Output": "drivers/s390/crypto/Makefile=37=vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o\ndrivers/s390/crypto/Makefile:38:obj-$(CONFIG_VFIO_AP) += vfio_ap.o\n--\ndrivers/s390/crypto/vfio_ap_debug.h-6- */\ndrivers/s390/crypto/vfio_ap_debug.h:7:#ifndef VFIO_AP_DEBUG_H\ndrivers/s390/crypto/vfio_ap_debug.h:8:#define VFIO_AP_DEBUG_H\ndrivers/s390/crypto/vfio_ap_debug.h-9-\n--\ndrivers/s390/crypto/vfio_ap_debug.h-18-\ndrivers/s390/crypto/vfio_ap_debug.h:19:#define VFIO_AP_DBF(...)\t\t\t\t\t\\\ndrivers/s390/crypto/vfio_ap_debug.h-20-\tdebug_sprintf_event(vfio_ap_dbf_info, ##__VA_ARGS__)\ndrivers/s390/crypto/vfio_ap_debug.h:21:#define VFIO_AP_DBF_ERR(...)\t\t\t\t\t\\\ndrivers/s390/crypto/vfio_ap_debug.h-22-\tdebug_sprintf_event(vfio_ap_dbf_info, DBF_ERR, ##__VA_ARGS__)\ndrivers/s390/crypto/vfio_ap_debug.h:23:#define VFIO_AP_DBF_WARN(...)\t\t\t\t\t\\\ndrivers/s390/crypto/vfio_ap_debug.h-24-\tdebug_sprintf_event(vfio_ap_dbf_info, DBF_WARN, ##__VA_ARGS__)\ndrivers/s390/crypto/vfio_ap_debug.h:25:#define VFIO_AP_DBF_INFO(...)\t\t\t\t\t\\\ndrivers/s390/crypto/vfio_ap_debug.h-26-\tdebug_sprintf_event(vfio_ap_dbf_info, DBF_INFO, ##__VA_ARGS__)\ndrivers/s390/crypto/vfio_ap_debug.h:27:#define VFIO_AP_DBF_DBG(...)\t\t\t\t\t\\\ndrivers/s390/crypto/vfio_ap_debug.h-28-\tdebug_sprintf_event(vfio_ap_dbf_info, DBF_DEBUG, ##__VA_ARGS__)\n--\ndrivers/s390/crypto/vfio_ap_debug.h=30=extern debug_info_t *vfio_ap_dbf_info;\ndrivers/s390/crypto/vfio_ap_debug.h-31-\ndrivers/s390/crypto/vfio_ap_debug.h:32:#endif /* VFIO_AP_DEBUG_H */\n--\ndrivers/s390/crypto/vfio_ap_drv.c-18-\ndrivers/s390/crypto/vfio_ap_drv.c:19:#define VFIO_AP_ROOT_NAME \"vfio_ap\"\ndrivers/s390/crypto/vfio_ap_drv.c:20:#define VFIO_AP_DEV_NAME \"matrix\"\ndrivers/s390/crypto/vfio_ap_drv.c-21-\n--\ndrivers/s390/crypto/vfio_ap_drv.c=86=static int vfio_ap_matrix_dev_create(void)\n--\ndrivers/s390/crypto/vfio_ap_drv.c-90-\ndrivers/s390/crypto/vfio_ap_drv.c:91:\troot_device = root_device_register(VFIO_AP_ROOT_NAME);\ndrivers/s390/crypto/vfio_ap_drv.c-92-\tif (IS_ERR(root_device))\n--\ndrivers/s390/crypto/vfio_ap_drv.c-115-\ndrivers/s390/crypto/vfio_ap_drv.c:116:\tdev_set_name(\u0026matrix_dev-\u003edevice, \"%s\", VFIO_AP_DEV_NAME);\ndrivers/s390/crypto/vfio_ap_drv.c-117-\tmatrix_dev-\u003edevice.parent = root_device;\n--\ndrivers/s390/crypto/vfio_ap_drv.c=167=static int __init vfio_ap_init(void)\n--\ndrivers/s390/crypto/vfio_ap_drv.c-182-\ndrivers/s390/crypto/vfio_ap_drv.c:183:\tret = ap_driver_register(\u0026vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);\ndrivers/s390/crypto/vfio_ap_drv.c-184-\tif (ret) {\n--\ndrivers/s390/crypto/vfio_ap_ops.c-25-\ndrivers/s390/crypto/vfio_ap_ops.c:26:#define VFIO_AP_MDEV_TYPE_HWVIRT \"passthrough\"\ndrivers/s390/crypto/vfio_ap_ops.c:27:#define VFIO_AP_MDEV_NAME_HWVIRT \"VFIO AP Passthrough Device\"\ndrivers/s390/crypto/vfio_ap_ops.c-28-\n--\ndrivers/s390/crypto/vfio_ap_ops.c=291=static void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-294-\t\treturn;\ndrivers/s390/crypto/vfio_ap_ops.c:295:\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID \u0026\u0026\ndrivers/s390/crypto/vfio_ap_ops.c-296-\t    !WARN_ON(!(q-\u003ematrix_mdev \u0026\u0026 q-\u003ematrix_mdev-\u003ekvm))) {\ndrivers/s390/crypto/vfio_ap_ops.c-297-\t\tkvm_s390_gisc_unregister(q-\u003ematrix_mdev-\u003ekvm, q-\u003esaved_isc);\ndrivers/s390/crypto/vfio_ap_ops.c:298:\t\tq-\u003esaved_isc = VFIO_AP_ISC_INVALID;\ndrivers/s390/crypto/vfio_ap_ops.c-299-\t}\n--\ndrivers/s390/crypto/vfio_ap_ops.c=505=static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q,\n--\ndrivers/s390/crypto/vfio_ap_ops.c-519-\tif (vfio_ap_validate_nib(vcpu, \u0026nib)) {\ndrivers/s390/crypto/vfio_ap_ops.c:520:\t\tVFIO_AP_DBF_WARN(\"%s: invalid NIB address: nib=%pad, apqn=%#04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-521-\t\t\t\t __func__, \u0026nib, q-\u003eapqn);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-533-\tdefault:\ndrivers/s390/crypto/vfio_ap_ops.c:534:\t\tVFIO_AP_DBF_WARN(\"%s: vfio_pin_pages failed: rc=%d,\"\ndrivers/s390/crypto/vfio_ap_ops.c-535-\t\t\t\t \"nib=%pad, apqn=%#04x\\n\",\n--\ndrivers/s390/crypto/vfio_ap_ops.c-557-\tif (nisc \u003c 0) {\ndrivers/s390/crypto/vfio_ap_ops.c:558:\t\tVFIO_AP_DBF_WARN(\"%s: gisc registration failed: nisc=%d, isc=%d, apqn=%#04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-559-\t\t\t\t __func__, nisc, isc, q-\u003eapqn);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-595-\t\tif (ret)\ndrivers/s390/crypto/vfio_ap_ops.c:596:\t\t\tVFIO_AP_DBF_WARN(\"%s: kvm_s390_gisc_unregister: rc=%d isc=%d, apqn=%#04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-597-\t\t\t\t\t __func__, ret, isc, q-\u003eapqn);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-602-\tif (status.response_code != AP_RESPONSE_NORMAL) {\ndrivers/s390/crypto/vfio_ap_ops.c:603:\t\tVFIO_AP_DBF_WARN(\"%s: PQAP(AQIC) failed with status=%#02x: \"\ndrivers/s390/crypto/vfio_ap_ops.c-604-\t\t\t\t \"zone=%#x, ir=%#x, gisc=%#x, f=%#x,\"\n--\ndrivers/s390/crypto/vfio_ap_ops.c=678=static int handle_pqap(struct kvm_vcpu *vcpu)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-691-\tif (!(vcpu-\u003earch.sie_block-\u003eeca \u0026 ECA_AIV)) {\ndrivers/s390/crypto/vfio_ap_ops.c:692:\t\tVFIO_AP_DBF_WARN(\"%s: AIV facility not installed: apqn=0x%04x, eca=0x%04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-693-\t\t\t\t __func__, apqn, vcpu-\u003earch.sie_block-\u003eeca);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-700-\tif (!vcpu-\u003ekvm-\u003earch.crypto.pqap_hook) {\ndrivers/s390/crypto/vfio_ap_ops.c:701:\t\tVFIO_AP_DBF_WARN(\"%s: PQAP(AQIC) hook not registered with the vfio_ap driver: apqn=0x%04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-702-\t\t\t\t __func__, apqn);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-712-\t\tvfio_ap_le_guid_to_be_uuid(\u0026matrix_mdev-\u003emdev-\u003euuid, uuid);\ndrivers/s390/crypto/vfio_ap_ops.c:713:\t\tVFIO_AP_DBF_WARN(\"%s: mdev %08lx-%04lx-%04lx-%04lx-%04lx%08lx not in use: apqn=0x%04x\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-714-\t\t\t\t __func__, uuid[0],  uuid[1], uuid[2],\n--\ndrivers/s390/crypto/vfio_ap_ops.c-720-\tif (!q) {\ndrivers/s390/crypto/vfio_ap_ops.c:721:\t\tVFIO_AP_DBF_WARN(\"%s: Queue %02x.%04x not bound to the vfio_ap driver\\n\",\ndrivers/s390/crypto/vfio_ap_ops.c-722-\t\t\t\t __func__, AP_QID_CARD(apqn),\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2072=static void report_aqic_resource_leak(struct vfio_ap_queue *q)\ndrivers/s390/crypto/vfio_ap_ops.c-2073-{\ndrivers/s390/crypto/vfio_ap_ops.c:2074:\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID || q-\u003esaved_iova) {\ndrivers/s390/crypto/vfio_ap_ops.c-2075-\t\tif (q-\u003ematrix_mdev) {\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2101=static void apq_reset_check(struct work_struct *reset_work)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2184-done:\ndrivers/s390/crypto/vfio_ap_ops.c:2185:\tif (q-\u003esaved_isc != VFIO_AP_ISC_INVALID)\ndrivers/s390/crypto/vfio_ap_ops.c-2186-\t\tvfio_ap_free_aqic_resources(q);\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2319=static int vfio_ap_mdev_get_device_info(unsigned long arg)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2333-\tinfo.num_regions = 0;\ndrivers/s390/crypto/vfio_ap_ops.c:2334:\tinfo.num_irqs = VFIO_AP_NUM_IRQS;\ndrivers/s390/crypto/vfio_ap_ops.c-2335-\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2339=static ssize_t vfio_ap_get_irq_info(unsigned long arg)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2348-\ndrivers/s390/crypto/vfio_ap_ops.c:2349:\tif (info.argsz \u003c minsz || info.index \u003e= VFIO_AP_NUM_IRQS)\ndrivers/s390/crypto/vfio_ap_ops.c-2350-\t\treturn -EINVAL;\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2352-\tswitch (info.index) {\ndrivers/s390/crypto/vfio_ap_ops.c:2353:\tcase VFIO_AP_REQ_IRQ_INDEX:\ndrivers/s390/crypto/vfio_ap_ops.c-2354-\t\tinfo.count = 1;\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2356-\t\tbreak;\ndrivers/s390/crypto/vfio_ap_ops.c:2357:\tcase VFIO_AP_CFG_CHG_IRQ_INDEX:\ndrivers/s390/crypto/vfio_ap_ops.c-2358-\t\tinfo.count = 1;\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2368=static int vfio_ap_irq_set_init(struct vfio_irq_set *irq_set, unsigned long arg)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2378-\ndrivers/s390/crypto/vfio_ap_ops.c:2379:\tret = vfio_set_irqs_validate_and_prepare(irq_set, 1, VFIO_AP_NUM_IRQS,\ndrivers/s390/crypto/vfio_ap_ops.c-2380-\t\t\t\t\t\t \u0026data_size);\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2457=static int vfio_ap_set_irqs(struct ap_matrix_mdev *matrix_mdev,\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2469-\t\tswitch (irq_set.index) {\ndrivers/s390/crypto/vfio_ap_ops.c:2470:\t\tcase VFIO_AP_REQ_IRQ_INDEX:\ndrivers/s390/crypto/vfio_ap_ops.c-2471-\t\t\treturn vfio_ap_set_request_irq(matrix_mdev, arg);\ndrivers/s390/crypto/vfio_ap_ops.c:2472:\t\tcase VFIO_AP_CFG_CHG_IRQ_INDEX:\ndrivers/s390/crypto/vfio_ap_ops.c-2473-\t\t\treturn vfio_ap_set_cfg_change_irq(matrix_mdev, arg);\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2627=int vfio_ap_mdev_register(void)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2634-\ndrivers/s390/crypto/vfio_ap_ops.c:2635:\tmatrix_dev-\u003emdev_type.sysfs_name = VFIO_AP_MDEV_TYPE_HWVIRT;\ndrivers/s390/crypto/vfio_ap_ops.c:2636:\tmatrix_dev-\u003emdev_type.pretty_name = VFIO_AP_MDEV_NAME_HWVIRT;\ndrivers/s390/crypto/vfio_ap_ops.c-2637-\tmatrix_dev-\u003emdev_types = \u0026matrix_dev-\u003emdev_type;\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2656=int vfio_ap_mdev_probe_queue(struct ap_device *apdev)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2676-\tq-\u003eapqn = apqn;\ndrivers/s390/crypto/vfio_ap_ops.c:2677:\tq-\u003esaved_isc = VFIO_AP_ISC_INVALID;\ndrivers/s390/crypto/vfio_ap_ops.c-2678-\tq-\u003ereset_max_wait = AP_RESET_MAX_WAIT;\n--\ndrivers/s390/crypto/vfio_ap_private.h-11-\ndrivers/s390/crypto/vfio_ap_private.h:12:#ifndef _VFIO_AP_PRIVATE_H_\ndrivers/s390/crypto/vfio_ap_private.h:13:#define _VFIO_AP_PRIVATE_H_\ndrivers/s390/crypto/vfio_ap_private.h-14-\n--\ndrivers/s390/crypto/vfio_ap_private.h-25-\ndrivers/s390/crypto/vfio_ap_private.h:26:#define VFIO_AP_MODULE_NAME \"vfio_ap\"\ndrivers/s390/crypto/vfio_ap_private.h:27:#define VFIO_AP_DRV_NAME \"vfio_ap\"\ndrivers/s390/crypto/vfio_ap_private.h-28-\n--\ndrivers/s390/crypto/vfio_ap_private.h=145=struct vfio_ap_queue {\n--\ndrivers/s390/crypto/vfio_ap_private.h-148-\tint\tapqn;\ndrivers/s390/crypto/vfio_ap_private.h:149:#define VFIO_AP_ISC_INVALID 0xff\ndrivers/s390/crypto/vfio_ap_private.h-150-\tunsigned char saved_isc;\n--\ndrivers/s390/crypto/vfio_ap_private.h=168=void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,\n--\ndrivers/s390/crypto/vfio_ap_private.h-170-\ndrivers/s390/crypto/vfio_ap_private.h:171:#endif /* _VFIO_AP_PRIVATE_H_ */\n"
}

7/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16664
							output: 32
							thoughts: 43
						

Thoughts:
Okay, let's see. My first thought is to pinpoint where the dependency of `VFIO_AP` lies in relation to the S390 architecture. Given my experience, the logical place to start digging would be in the configuration files. I'll need to check the Kconfig files related to the S390 architecture, and the crypto section feels like the most likely place to start. Specifically, I'll examine `drivers/s390/Kconfig` and `drivers/s390/crypto/Kconfig`. This should reveal whether `VFIO_AP` is directly contingent on the S390 platform or some related crypto features of the system.



8/2 2026/09/05 18:22 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config VFIO_AP",
  "PathPrefix": "drivers/s390/"
}

Results:
{
  "error": "no matches"
}

9/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16897
							output: 19
							thoughts: 16
						

Thoughts:
Okay, let's dive into this Kconfig hunt.

**Hunting for VFIO_AP in the Kconfig Files**

Right, so I need to track down every instance where `VFIO_AP` is actually defined within the Kconfig files. This is a pretty straightforward, though potentially tedious, task. I'm assuming there's a good reason for this search – maybe I'm tracking a dependency, debugging a build issue, or perhaps trying to understand the configuration space around Accelerated Processing support in VFIO. Whatever the reason, my immediate goal is clear: find all those declarations. I'll need to use my usual tools for this - probably a combination of `find` and `grep`, or perhaps a more sophisticated tool if the codebase is particularly complex. Time to get to work.



10/2 2026/09/05 18:22 tool grepper
0m Args:
{
  "Expression": "config VFIO_AP"
}

Results:
{
  "Output": "arch/s390/Kconfig=835=config VFIO_CCW\n--\narch/s390/Kconfig-845-\narch/s390/Kconfig:846:config VFIO_AP\narch/s390/Kconfig-847-\tdef_tristate n\n"
}

11/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 17190
							output: 37
							thoughts: 22
						

Thoughts:
Here's a summary of my thought process, as requested:

**Diving into the s390 Architecture Configuration**

Okay, time to get my hands dirty with the s390 architecture. I need to understand some configuration options, and I remember `Kconfig` files are the place to be. Let's see... I'm thinking the relevant section is probably around line 846 of `arch/s390/Kconfig`. That's likely where the core settings related to whatever I'm investigating are defined. Let's get to it. Time to start the deep dive into the code.



12/2 2026/09/05 18:22 tool read-file
0m Args:
{
  "File": "arch/s390/Kconfig",
  "FirstLine": 840,
  "LineCount": 30
}

Results:
{
  "Contents": " 840:\t\thelp\n 841:\t\t  This driver allows usage of I/O subchannels via VFIO-CCW.\n 842:\t\n 843:\t\t  To compile this driver as a module, choose M here: the\n 844:\t\t  module will be called vfio_ccw.\n 845:\t\n 846:\tconfig VFIO_AP\n 847:\t\tdef_tristate n\n 848:\t\tprompt \"VFIO support for AP devices\"\n 849:\t\tdepends on KVM\n 850:\t\tdepends on VFIO\n 851:\t\tdepends on AP\n 852:\t\tselect VFIO_MDEV\n 853:\t\thelp\n 854:\t\t  This driver grants access to Adjunct Processor (AP) devices\n 855:\t\t  via the VFIO mediated device interface.\n 856:\t\n 857:\t\t  To compile this driver as a module, choose M here: the module\n 858:\t\t  will be called vfio_ap.\n 859:\t\n 860:\tendmenu\n 861:\t\n 862:\tconfig CCW\n 863:\t\tdef_bool y\n 864:\t\n 865:\tconfig HAVE_PNETID\n 866:\t\ttristate\n 867:\t\tdefault (SMC || CCWGROUP)\n 868:\t\n 869:\tmenu \"Virtualization\"\n"
}

13/2 2026/09/05 18:22 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 17765
							output: 91
							thoughts: 483
						

Thoughts:
**VFIO_AP Patch Assessment for amd64 Architecture**

Okay, so I'm looking at this patch and my initial impression is that it's probably *not* fuzzable for my target architecture, amd64. Let's see... the `VFIO_AP` symbol is defined within `arch/s390/Kconfig`, which immediately flags it as s390-specific. The whole thing is within the "I/O subsystem" menu in `arch/s390`, that's a red flag.

Now, I need to apply the rules. Specifically, the "NEGATIVE CRITERIA" section. The rule I need to focus on here is "Unreachable execution paths: * Code paths exclusive to architectures other than the target architecture." Perfect.

Let's see what files are changed. Ah, it looks like `drivers/s390/crypto/vfio_ap_ops.c` and `drivers/s390/crypto/vfio_ap_private.h`. Both right in `drivers/s390/`, exactly where I would expect. They're only compiled when `CONFIG_VFIO_AP` is set, which we know is defined in `arch/s390/Kconfig` and depends on s390 architecture. This is all screaming s390. I'm on amd64. 

Therefore, the modified code *is* exclusive to s390 and definitely unreachable on amd64. No ifs, ands, or buts.

Time for the `set-results` sanity check. Let's see...

-   `WorthFuzzing`: false - Yep, check.
-   `Reasoning`: "The patch modifies the s390 VFIO AP (Adjunct Processor) driver (`drivers/s390/crypto/vfio_ap_ops.c` and `drivers/s390/crypto/vfio_ap_private.h`), which is exclusive to the s390 architecture (CONFIG_VFIO_AP) and not compiled or reachable on the target architecture (amd64)." - Spot on, explains it perfectly.
-   `FocusSymbols`: [] - Correct, nothing to focus on.
-   `EnableConfigs`: [] - All good.

Alright, I'm confident. Let's call `set-results`. 



14/2 2026/09/05 18:22 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the s390 VFIO AP (Adjunct Processor) driver in drivers/s390/crypto/, which is exclusive to the s390 architecture (CONFIG_VFIO_AP) and is neither compiled nor reachable on the target architecture (amd64).",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the s390 VFIO AP (Adjunct Processor) driver in drivers/s390/crypto/, which is exclusive to the s390 architecture (CONFIG_VFIO_AP) and is neither compiled nor reachable on the target architecture (amd64).",
  "WorthFuzzing": false
}

15/1 2026/09/05 18:22 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

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)