Commit efbf01e03f for qemu.org
commit efbf01e03fb0fa0f473898d33349cdd6f5946a92
Author: Peter Maydell <peter.maydell@linaro.org>
Date: Mon Jul 20 19:05:30 2026 +0100
hw/dma/omap_dma: Be more careful about overflow in transfer setup
In omap_dma_transfer_setup(), the maximum number of elements we can
transfer is 0xffff * 0xffff == 0xfffe0001 (because the max frame
count and max elements per frame are both 65535). However, we store
total element counts in 'int' variables, and use INT_MAX as a "bigger
than any valid value" sentinel, and when performing arithmetic with
the total count of transferred elements we are not careful about
avoiding overflows. Fix these:
- use uint32_t rather than int for the local variables tracking
various element and frame counts
- use UINT_MAX as our sentinel
- calculate new packet, element and frame counter values using
arithmetic on a local uint32_t, rather than doing it in-place
on local variables that are only 'int' because the actual
counter registers are 16 bits
- use 64-bit arithmetic when calculating how much to advance the
source and dest pointers and the total dma->bytes transferred
Note that since soc_dma_ch_s::bytes is only 'int' this can still
overflow; we'll fix that in a subsequent patch.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Jim MacArthur <jim.macarthur@linaro.org>
Message-id: 20260710105907.2570621-5-peter.maydell@linaro.org
diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c
index 77f1441498..14b310bf40 100644
--- a/hw/dma/omap_dma.c
+++ b/hw/dma/omap_dma.c
@@ -391,7 +391,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
struct omap_dma_reg_set_s *a;
struct omap_dma_channel_s *ch = dma->opaque;
struct omap_dma_s *s = dma->dma->opaque;
- int frames, min_elems, elements[__omap_dma_intr_last];
+ uint32_t frames, min_elems, elements[__omap_dma_intr_last];
a = &ch->active_set;
@@ -403,7 +403,14 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
__func__, dma->num);
}
- min_elems = INT_MAX;
+ /*
+ * The maximum frame count and maximum element count are both 0xffff,
+ * so our worst case possible number of elements to transfer is
+ * 0xffff * 0xffff == 0xfffe0001. We can therefore keep element
+ * counts in a uint32_t and use UINT_MAX as a sentinel value for
+ * "not set" / "condition does not occur".
+ */
+ min_elems = UINT_MAX;
/* Check all the conditions that terminate the transfer starting
* with those that can occur the soonest. */
@@ -413,7 +420,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
if (elements[id] < min_elems) \
min_elems = elements[id]; \
} else \
- elements[id] = INT_MAX;
+ elements[id] = UINT_MAX;
/* Elements */
INTR_CHECK(
@@ -465,7 +472,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
(a->frames - a->frame - 1) * a->elements +
(a->elements - a->element))
- dma->bytes = min_elems * ch->data_type;
+ dma->bytes = (uint64_t)min_elems * ch->data_type;
/* Set appropriate interrupts and/or deactivate channels */
@@ -528,8 +535,9 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
/* Update packet number */
if (ch->fs && ch->bs) {
- a->pck_element += min_elems;
- a->pck_element %= a->pck_elements;
+ /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */
+ uint32_t new_pck_element = a->pck_element + min_elems;
+ a->pck_element = new_pck_element % a->pck_elements;
}
/*
@@ -537,13 +545,15 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
* can skip part of this.
*/
if (dma->update) {
+ /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */
+ uint32_t new_element = a->element + min_elems;
a->element += min_elems;
- frames = a->element / a->elements;
- a->element = a->element % a->elements;
+ frames = new_element / a->elements;
+ a->element = new_element % a->elements;
a->frame += frames;
- a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0];
- a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1];
+ a->src += (uint64_t)min_elems * a->elem_delta[0] + frames * a->frame_delta[0];
+ a->dest += (uint64_t)min_elems * a->elem_delta[1] + frames * a->frame_delta[1];
/* If the channel is async, update cpc */
if (!ch->sync && frames) {