Hi Gustavo, On Tue, 19 May 2026 at 20:44, Gustavo A. R. Silva <[email protected]> wrote: > On 5/19/26 07:44, Lad, Prabhakar wrote: > > On Tue, May 19, 2026 at 2:34 PM Geert Uytterhoeven <[email protected]> > > wrote: > >> On Tue, 19 May 2026 at 15:30, Prabhakar <[email protected]> wrote: > >>> From: Lad Prabhakar <[email protected]> > >>> > >>> Fix an counter tracking in mmc_test_alloc_mem() that causes a kernel panic > >>> during error unwinding. > >>> > >>> The `struct mmc_test_mem` uses the `__counted_by(cnt)` annotation on its > >>> flexible array member `arr`. While kzalloc_flex() initially sets the > >>> counter field (`cnt`) to `max_segs`, the allocation loop needs to track > >>> how many elements have actually been populated. > >>> > >>> Previously, leaving `mem->cnt` at `max_segs` meant that if the loop failed > >>> midway (e.g., "Failed to map sg list"), the error unwinding path in > >>> mmc_test_free_mem() would attempt to clean up uninitialized trailing > >>> array slots. This resulted in passing NULL pointers to __free_pages(), > >>> triggering a kernel panic: > >>> > >>> [ 66.172845] mmc0: Failed to map sg list > >>> [ 66.176722] Unable to handle kernel NULL pointer dereference at > >>> virtual address 0000000000000000 > >>> ... > >>> [ 66.432747] Call trace: > >>> [ 66.435191] ___free_pages+0x1c/0xc4 (P) > >>> [ 66.439119] __free_pages+0x14/0x20 > >>> [ 66.442608] mmc_test_area_cleanup+0x58/0x84 [mmc_test] > >>> > >>> Fix this by explicitly resetting `mem->cnt` to 0 immediately after > >>> allocation. Then, move the existing `mem->cnt` increment so that it occurs > >>> prior to populating each array slot, using `mem->cnt - 1` for the actual > >>> assignment index. This guarantees that the counter accurately tracks > >>> initialized entries for safe error cleanup, while dynamically expanding > >>> the `__counted_by` validation boundary ahead of each flexible array write. > >>> > >>> Additionally, rewrite the cleanup loop in mmc_test_free_mem() to use a > >>> standard forward for-loop. This addresses the unsafe post-decrement logic > >>> in the original `while (mem->cnt--)` loop which evaluated and decremented > >>> the counter field before indexing the array, and avoids a potential > >>> integer > >>> underflow/wrap-around of the counter field if the cleanup path is invoked > >>> when `mem->cnt` is 0. > >>> > >>> Fixes: c3126dccfd7b ("mmc: mmc_test: use kzalloc_flex") > >>> Signed-off-by: Lad Prabhakar <[email protected]> > >>> --- > >>> v1->v2: > >>> - Started with cnt = 0 and incremented before assignment to ensure > >>> accurate tracking of initialized entries in mmc_test_alloc_mem(). > >>> - In mmc_test_free_mem(), replaced the while loop with a forward for-loop > >>> to > >>> safely iterate over initialized entries without risking underflow. > >>> - Updated commit message to clarify the issue and the fix. > >> > >> Thanks for your patch! > >> > >>> --- a/drivers/mmc/core/mmc_test.c > >>> +++ b/drivers/mmc/core/mmc_test.c > >>> @@ -318,9 +318,8 @@ static void mmc_test_free_mem(struct mmc_test_mem > >>> *mem) > >>> { > >>> if (!mem) > >>> return; > >>> - while (mem->cnt--) > >>> - __free_pages(mem->arr[mem->cnt].page, > >>> - mem->arr[mem->cnt].order); > >>> + for (unsigned int i = 0; i < mem->cnt; i++) > >>> + __free_pages(mem->arr[i].page, mem->arr[i].order); > >>> kfree(mem); > >>> } > >>> > >>> @@ -356,6 +355,7 @@ static struct mmc_test_mem > >>> *mmc_test_alloc_mem(unsigned long min_sz, > >>> mem = kzalloc_flex(*mem, arr, max_segs); > >>> if (!mem) > >>> return NULL; > >>> + mem->cnt = 0; > >> > >> This is not needed, as it is set to zero by kzalloc_flex(). > >> > > Actually, kzalloc_flex() automatically sets mem->cnt to max_segs > > because cnt is annotated with __counted_by. Because of that implicit > > initialization, we need this explicit reset to get it back to zero. > > An auxiliary variable could be used to avoid having to update the > counter too early[1][2].
Thank you! In light of this, Prabhakar's v1[3] is the better solution. But perhaps that the addition of "mem->cnt = max_segs;" after the kzalloc_flex() should be dropped, as it is done automatically by compilers that support __counted_by, but is irrelevant for compilers that do not support it (the correct value is set at the end anyway). Gustavo, what do you think? > I think it'll eventually become best practice to defer updating the > counter until after the flexible array has been fully initialized, or > after every major update that requires changing its boundaries. In case the update is a _decrease_. If the update is an _increase_, it must be done first? > [1] https://git.kernel.org/linus/ea9e148c803b24eb > [2] > https://embeddedor.com/blog/2024/06/18/how-to-use-the-new-counted_by-attribute-in-c-and-linux/ > (I'm in the process of updating this blogpost with some *alloc_flex() > examples and more.) [3] "[PATCH] mmc: mmc_test: Fix __counted_by handling after kzalloc_flex() conversion" https://lore.kernel.org/[email protected] Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds

