]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/vfio_ccw: Limit the number of channel program segments
authorEric Farman <farman@linux.ibm.com>
Tue, 28 Jul 2026 03:30:14 +0000 (05:30 +0200)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Thu, 30 Jul 2026 17:50:07 +0000 (19:50 +0200)
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 <mjrosato@linux.ibm.com>
Signed-off-by: Eric Farman <farman@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
drivers/s390/cio/vfio_ccw_cp.c
drivers/s390/cio/vfio_ccw_cp.h

index 086d1b54bdb056259fece3a05336b936de515490..1c2890d139c6b8ea404d53e4527d8dd5c18726d6 100644 (file)
@@ -332,6 +332,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len)
                goto out_err;
 
        list_add_tail(&chain->next, &cp->ccwchain_list);
+       cp->ccwchain_count++;
 
        return chain;
 
@@ -441,6 +442,10 @@ static int ccwchain_handle_ccw(dma32_t 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)
@@ -745,6 +750,7 @@ int cp_init(struct channel_program *cp, union orb *orb)
                        vdev->dev,
                        "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));
 
index fc31eb699807246d99c00e4afdbc5bad996ce367..a9b1d8dbc6f655e712cf658172af2dc76e6d6296 100644 (file)
  */
 #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
  * @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
@@ -38,6 +45,7 @@ struct channel_program {
        union orb orb;
        bool initialized;
        struct ccw1 *guest_cp;
+       unsigned int ccwchain_count;
 };
 
 int cp_init(struct channel_program *cp, union orb *orb);