[PATCH] dma: swiotlb: Round restricted DMA pool size down to IO_TLB_SEGSIZE

From: Marek Szyprowski

Date: Tue Nov 03 2026 - 11:46:01 EST


All other paths creating a swiotlb pool ensure that nslabs is a multiple
of IO_TLB_SEGSIZE, and the swiotlb code relies on that. Since commit
0b84e4f8b793 ("swiotlb: Add restricted DMA pool initialization") a
restricted pool can have an arbitrary size taken from the device tree.

If the pool size is not a multiple of IO_TLB_SEGSIZE, the last segment is
partial and swiotlb_release_slots() reads mem->slots[index + nslabs],
which is one past the end of the slots array when the freed buffer ends
at the end of the pool:

BUG: KASAN: slab-out-of-bounds in swiotlb_release_slots
Read of size 2

Round the pool size down to a multiple of IO_TLB_SEGSIZE, wasting a bit
of memory, and warn about it.

Reported-by: Giorgi Kobakhia <gkobakhi@xxxxxxx>
Closes: https://lore.kernel.org/all/20261026032007.1957949-1-gkobakhi@xxxxxxx/
Fixes: 0b84e4f8b793 ("swiotlb: Add restricted DMA pool initialization")
CC: stable@xxxxxxxxxxxxxxx
Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
---
kernel/dma/swiotlb.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 2c57b49ac7c0..a68f35192582 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -2007,6 +2007,19 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
*/
if (!mem) {
struct io_tlb_pool *pool;
+ unsigned long aligned_nslabs = ALIGN_DOWN(nslabs, IO_TLB_SEGSIZE);
+
+ if (aligned_nslabs != nslabs) {
+ dev_warn(dev, "Restricted DMA pool size %zu is not a multiple of %lu, rounding down to %lu bytes\n",
+ (size_t)rmem->size,
+ (unsigned long)IO_TLB_SEGSIZE << IO_TLB_SHIFT,
+ aligned_nslabs << IO_TLB_SHIFT);
+ nslabs = aligned_nslabs;
+ }
+ if (!nslabs) {
+ dev_err(dev, "Restricted DMA pool is too small\n");
+ return -EINVAL;
+ }

mem = kzalloc_obj(*mem);
if (!mem)
@@ -2034,7 +2047,7 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,

mem->cc_shared = true;
ret = set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
- rmem->size >> PAGE_SHIFT);
+ (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
if (ret) {
dev_err(dev, "Failed to decrypt restricted DMA pool\n");
kfree(pool->areas);
--
2.43.0