[PATCH AUTOSEL 6.19-5.10] ntb: ntb_hw_switchtec: Fix shift-out-of-bounds for 0 mw lut
Sasha Levin <[email protected]> Mon, 23 Feb 2026 11:17:05 -0500
| Newsgroups | dev.linux.lists.ntb,dev.linux.lists.patches,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
From: Maciej Grochowski <[email protected]> [ Upstream commit 186615f8855a0be4ee7d3fcd09a8ecc10e783b08 ] Number of MW LUTs depends on NTB configuration and can be set to zero, in such scenario rounddown_pow_of_two will cause undefined behaviour and should not be performed. This patch ensures that rounddown_pow_of_two is called on valid value. Signed-off-by: Maciej Grochowski <[email protected]> Signed-off-by: Jon Mason <[email protected]> Signed-off-by: Sasha Levin <[email protected]> --- LLM Generated explanations, may be completely bogus: The file has been present since 2017 (v4.14 era), so it's in all stable trees. Now let me verify the exact nature of the bug: ## Analysis ### Problem The commit fixes undefined behavior (UB) in `switchtec_ntb_init_mw()`. When `nr_lut_mw` or `peer_nr_lut_mw` is read as 0 from hardware (via `ioread16`), calling `rounddown_pow_of_two(0)` results in: - `1UL << (fls_long(0) - 1)` = `1UL << (0 - 1)` = `1UL << -1` (unsigned underflow to a huge shift value) This is explicitly documented as undefined in the kernel: the comment in `include/linux/log2.h` says "the result is undefined when n == 0". This is a **shift-out-of-bounds** bug that: - Triggers UBSAN warnings - Produces an incorrect (garbage) value for `nr_lut_mw`, which could cause further issues downstream ### Fix The fix adds a simple `if (sndev->nr_lut_mw)` guard before calling `rounddown_pow_of_two()` in two places — for both `self` and `peer` LUT MW counts. If the value is 0, it stays 0 (which is correct — no LUT memory windows). ### Stable Criteria Assessment 1. **Obviously correct and tested**: Yes — trivially correct. If the count is 0, rounding down 0 should remain 0. 2. **Fixes a real bug**: Yes — undefined behavior from shift-out-of- bounds. This can cause UBSAN splats and potentially incorrect values. 3. **Important issue**: Medium — UB can have unpredictable consequences depending on compiler optimizations. The value 0 is a valid hardware configuration. 4. **Small and contained**: Yes — 4 lines changed (2 `if` guards added), single file, single function. 5. **No new features**: Correct — purely a bug fix. 6. **Applies cleanly**: The code has been stable since 2017; should apply to all active stable trees. ### Risk Assessment - **Risk**: Extremely low. The guard only adds a check for zero before calling a function that explicitly documents UB for zero input. - **Benefit**: Eliminates undefined behavior and potential UBSAN splats on hardware configurations with 0 LUT memory windows. ### Verification - Verified `rounddown_pow_of_two` is documented as "result is undefined when n == 0" in `include/linux/log2.h` - Verified the implementation: `1UL << (fls_long(n) - 1)` with n=0 produces `1UL << (0-1)` = shift-out-of-bounds - Verified the file has existed since 2017 (commit 33dea5aae032), present in all active stable trees - Verified the fix is minimal: 2 `if` guards added, no other behavioral changes - Verified there's a related prior shift fix in the same file (ff148d8ac53e5), showing this class of bugs has been addressed before **YES** drivers/ntb/hw/mscc/ntb_hw_switchtec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c index f15ebab138144..0536521fa6ccc 100644 --- a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -1202,7 +1202,8 @@ static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) sndev->mmio_self_ctrl); sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries); - sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); + if (sndev->nr_lut_mw) + sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut\n", sndev->nr_direct_mw, sndev->nr_lut_mw); @@ -1212,7 +1213,8 @@ static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) sndev->peer_nr_lut_mw = ioread16(&sndev->mmio_peer_ctrl->lut_table_entries); - sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); + if (sndev->peer_nr_lut_mw) + sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut\n", sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw); -- 2.51.0