From: liyouhong min_order_for_split() reads folio->mapping twice without any synchronization. Concurrent truncate or invalidate can clear folio->mapping between the check and subsequent function call. Even with a held folio reference preventing the folio from being freed, folio->mapping can still be overwritten to NULL. This TOCTOU race allows passing a NULL mapping into mapping_min_folio_order(), which leads to a NULL pointer dereference. Cache folio->mapping to a local variable using READ_ONCE() to guarantee a single memory load and remove the race window. Link: https://sashiko.dev/#/patchset/20260803060001.800638-1-dayou5941@163.com Signed-off-by: liyouhong --- mm/huge_memory.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 2bccb0a53a0a..54d4261a2a15 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4284,19 +4284,23 @@ int folio_split(struct folio *folio, unsigned int new_order, */ unsigned int min_order_for_split(struct folio *folio) { + struct address_space *mapping; + if (folio_test_anon(folio)) return 0; + mapping = READ_ONCE(folio->mapping); + /* * If the folio got truncated, we don't know the previous mapping and * consequently the old min order. But it doesn't matter, as any split * attempt will immediately fail with -EBUSY as the folio cannot get * split until freed. */ - if (!folio->mapping) + if (!mapping) return 0; - return mapping_min_folio_order(folio->mapping); + return mapping_min_folio_order(mapping); } int split_folio_to_list(struct folio *folio, struct list_head *list) -- 2.25.1