From d2cb025516588785ffcbd8e7c5f6a5b63c096bdf Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Tue, 28 Jul 2026 11:30:14 +0800 Subject: [PATCH] s390/vfio_ccw: Limit the number of channel program segments ANBZ: #44656 commit 5405c90d6a47b3014e74ee0618a162449abbbc93 upstream. s390/vfio_ccw: Limit the number of channel program segments The processing of channel programs, and the CCWs within them, is done recursively. As such, there is an arbitrary (but not architectural) limit to the number of CCWs that can exist in a single channel program. The vfio-ccw logic breaks these channel programs into segments whenever it encounters a Transfer-In-Channel (TIC) CCW, and the combined number of segments count towards the global limit. Impose an equivalent limit to the number of segments until such logic can be made non-recursive. Fixes: 0a19e61e6d4c ("vfio: ccw: introduce channel program interfaces") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato Signed-off-by: Eric Farman Signed-off-by: Christian Borntraeger Fixes: CVE-2026-80554 Assisted-by: PatchPilot Signed-off-by: Xunlei Pang --- drivers/s390/cio/vfio_ccw_cp.c | 6 ++++++ drivers/s390/cio/vfio_ccw_cp.h | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 8d1b2771c1aa..05a76f973387 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -334,6 +334,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len) chain->ch_len = len; list_add_tail(&chain->next, &cp->ccwchain_list); + cp->ccwchain_count++; return chain; } @@ -441,6 +442,10 @@ static int ccwchain_handle_ccw(u32 cda, struct channel_program *cp) if (len < 0) return len; + /* Limit number of chains in a single channel program */ + if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX) + return -EINVAL; + /* Need alloc a new chain for this one. */ chain = ccwchain_alloc(cp, len); if (!chain) @@ -652,6 +657,7 @@ int cp_init(struct channel_program *cp, struct device *mdev, union orb *orb) if (!orb->cmd.pfch && __ratelimit(&ratelimit_state)) dev_warn(mdev, "Prefetching channel program even though prefetch not specified in ORB"); + cp->ccwchain_count = 0; INIT_LIST_HEAD(&cp->ccwchain_list); memcpy(&cp->orb, orb, sizeof(*orb)); cp->mdev = mdev; diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index ba31240ce965..6ac5f0b945d7 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -23,12 +23,19 @@ */ #define CCWCHAIN_LEN_MAX 256 +/* + * Maximum number of chains + */ +#define CCWCHAIN_COUNT_MAX 16 + /** * struct channel_program - manage information for channel program * @ccwchain_list: list head of ccwchains * @orb: orb for the currently processed ssch request * @mdev: the mediated device to perform page pinning/unpinning * @initialized: whether this instance is actually initialized + * @guest_cp: copy of guest channel program + * @ccwchain_count: number of channel program segments (linked by TIC) * * @ccwchain_list is the head of a ccwchain list, that contents the * translated result of the guest channel program that pointed out by @@ -40,6 +47,7 @@ struct channel_program { struct device *mdev; bool initialized; struct ccw1 *guest_cp; + unsigned int ccwchain_count; }; extern int cp_init(struct channel_program *cp, struct device *mdev, -- Gitee