danctnix-besser: add TX bounce-buffer fix as patch 0021 (pkgrel=5)

2f9b4c7 (bes2600: bounce SDIO TX buffers to avoid DMA OOB read) was
present in the cumulative single-patch but omitted from the per-series
reconstruction. Its absence caused KFENCE OOB hits in sdio_tx_work
(bes_sdio_memcpy_to_io_helper reads past tx_buffer->buf when align >
tx_buffer->len) leading to periodic TX workqueue stalls and latency
scatter under KFENCE_SAMPLE_INTERVAL=100ms.

Diagnosed during pkgrel=4 soak: 0 KFENCE hits in 6h on cumulative,
5-7/h on pkgrel=4 per-series. Root cause confirmed by diffing the
sdio_tx_work sg_set_buf call site in both patch sets.

Signed-off-by: Claude (noether) <claude@reauktion.de>
This commit is contained in:
2026-05-21 00:38:57 +02:00
parent f6448c44fe
commit 818d7b8a34
2 changed files with 126 additions and 1 deletions
@@ -0,0 +1,123 @@
From 49d9b77a695dfa42d0c39e7c517829b2f7c15b1b Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Thu, 23 Apr 2026 11:58:31 +0200
Subject: [PATCH] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
The SDIO TX path rounds the DMA transfer length up to the host's
current block size and hands that length to dma_map_sg() via
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
tx_buffer->buf typically aliases into an skb linear head whose
allocated size matches tx_buffer->len, not the block-aligned
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
to one block past the end of the skb. On a PineTab2 with KFENCE
enabled this fires as:
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
Out-of-bounds read at ... (704B right of kfence-#...):
__pi_memcpy_generic
swiotlb_tbl_map_single
swiotlb_map
dma_direct_map_sg
__dma_map_sg_attrs
dma_map_sg_attrs
dw_mci_pre_dma_transfer
__dw_mci_start_request
...
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
sdio_tx_work+0x2b4/0x4a0 [bes2600]
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
In addition to being undefined behavior, the padding bytes (which
come from whatever memory follows the skb) are transmitted to the
peer, leaking kernel memory on the air.
Allocate a driver-owned DMA-page bounce buffer sized to
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
via single_gathered_buffer; a separate allocation is used for the
TX path because single_gathered_buffer is only serialised via
sdio_claim_host and sdio_tx_work accumulates scatter entries before
claiming the bus.
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index b943882..e306748 100644
--- a/drivers/staging/bes2600/bes2600_sdio.c
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -99,6 +99,7 @@ struct sbus_priv {
struct work_struct tx_work;
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
+ u8 *tx_bounce;
u32 tx_data_cnt;
u32 tx_xfer_cnt;
u32 tx_proc_cnt;
@@ -1151,7 +1152,26 @@ static void sdio_tx_work(struct work_struct *work)
}
}
- sg_set_buf(&sg[scatters], tx_buffer->buf, align);
+ /*
+ * The transfer length is rounded up to the SDIO block
+ * size, but tx_buffer->buf is only tx_buffer->len bytes
+ * long (it usually aliases into an skb linear head).
+ * Copy into a driver-owned bounce buffer and zero-pad
+ * to the aligned size; otherwise DMA reads past the
+ * skb and leaks adjacent kernel memory on the wire --
+ * observed as KFENCE OOB reads from
+ * bes_sdio_memcpy_to_io_helper via dma_map_sg.
+ */
+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
+ goto flush_previous;
+ memcpy(self->tx_bounce + total_len,
+ tx_buffer->buf, tx_buffer->len);
+ if (align > tx_buffer->len)
+ memset(self->tx_bounce + total_len +
+ tx_buffer->len, 0,
+ align - tx_buffer->len);
+ sg_set_buf(&sg[scatters],
+ self->tx_bounce + total_len, align);
total_len += align;
++scatters;
/*del_node:*/
@@ -1936,6 +1956,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (!self->single_gathered_buffer)
return -ENOMEM;
#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
+ get_order(MAX_SDIO_TRANSFER_LEN));
+ if (!self->tx_bounce) {
+#ifndef SDIO_HOST_ADMA_SUPPORT
+ free_pages((unsigned long)self->single_gathered_buffer,
+ get_order(MAX_SDIO_TRANSFER_LEN));
+#endif
+ return -ENOMEM;
+ }
+#endif
#ifdef BES_SDIO_RXTX_TOGGLE
self->fw_started = false;
#endif
@@ -2063,6 +2094,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
if (self->single_gathered_buffer) {
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
}
+#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+ if (self->tx_bounce) {
+ free_pages((unsigned long)self->tx_bounce,
+ get_order(MAX_SDIO_TRANSFER_LEN));
+ }
#endif
kfree(self);
}
--
2.54.0
+3 -1
View File
@@ -11,7 +11,7 @@
pkgbase=linux-pinetab2-danctnix-besser
pkgver=7.0.danctnix1
pkgrel=4
pkgrel=5
pkgdesc='PineTab2 (BESser bes2600 driver patchset)'
_srcname=linux-pinetab2
_srctag=v${pkgver%.*}-${pkgver##*.}
@@ -58,6 +58,7 @@ source=(
0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch
0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch
0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch
0021-bes2600-bounce-sdio-tx-buffers.patch
0002-bes2600-filter-5ghz-scan.patch
config # the main kernel config file
)
@@ -90,6 +91,7 @@ b2sums=('3d9795083c8938f80f480de0d10bfd9c525640e59d5c7f22983de3f12ee42c84c31be90
'e318299e4cb828220ac7d5142dc41969f22f83f1f791bd46f7f4ce19dbd1d7074b0faa9ac6a4daac4f70e6c7852b38a6482de62111bb7e653cd870d2968fce70'
'5c71b88f2ae8a7ebd0932db9a4da72a3ba8c636f31a1bed953a81359588bcb0309f62aa9dee98db62bdc988a9b669341910da2b133d9fb92d14c27d64b54efe9'
'e09273ddcdc44f4d40fe8a69e0fd70b963681ec4434ce63cf6114ea38954891e709ced877e0be914054854e2d295a2991e8c3d8dc0deb244bfc8b0568c681687'
'3be2f7d74baf721ad933f04dea39d79e87a2a1dac3e987615d976e984b1043b90d8ede5ab01c4eb371784eb6ca21cdf69ff7b7c8ee6c678c0aea4bf464fdb92f'
'396acbdcf570eada62533c0b8f505ed18077e8432249bab5b8ac8d1107cabc9489bdb91a5780446237ec4fd9ba5fc57a49dff34c16ddab60dc30513fc535f00f'
'656a998ab40cb85ee4c00f087b071a91632a6c091da2c84b0f74236b51d2dea6e9db6886625f80ad81dc249d8494ec47cd79d6dd9ea4f5e44f3cde857f861e10')