]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/vfio_ccw: Fix out of bounds check on CCW array
authorEric Farman <farman@linux.ibm.com>
Tue, 28 Jul 2026 03:30:15 +0000 (05:30 +0200)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Thu, 30 Jul 2026 17:50:11 +0000 (19:50 +0200)
The routine ccwchain_calc_length() counts the number of channel
command words (CCWs) that are chained together in a single channel
program, and rejects anything larger than CCWCHAIN_LEN_MAX (256) CCWs.

The loop itself is "do..while (count < 257)", and while the logic in
is_cpa_within_range() correctly adjusts between the 0-index array of
CCWs and the count of CCWs starting at 1, this means it would look
at a possible 257th CCW before ending the loop and (correctly)
returning an error.

Fix this by restructuring the loop to break as soon as 256 CCWs
(thus indexes 0-255) are examined, without looking at memory
outside the range.

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

index 1c2890d139c6b8ea404d53e4527d8dd5c18726d6..af632f9d545356921d229e4248354ca15718837e 100644 (file)
@@ -377,11 +377,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx)
 static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
 {
        struct ccw1 *ccw = cp->guest_cp;
-       int cnt = 0;
-
-       do {
-               cnt++;
+       int cnt;
 
+       for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) {
                /*
                 * We want to keep counting if the current CCW has the
                 * command-chaining flag enabled, or if it is a TIC CCW
@@ -391,15 +389,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
                 * after the TIC, depending on the results of its operation.
                 */
                if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt))
-                       break;
-
-               ccw++;
-       } while (cnt < CCWCHAIN_LEN_MAX + 1);
-
-       if (cnt == CCWCHAIN_LEN_MAX + 1)
-               cnt = -EINVAL;
+                       return cnt;
+       }
 
-       return cnt;
+       return -EINVAL;
 }
 
 static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp)