folio_split_supported() used in try_folio_split_to_order() requires folio->mapping to be non NULL, but current try_folio_split_to_order() does not check it. Add the check to prevent NULL pointer dereference. There is no issue in the current code, since try_folio_split_to_order() is only used in truncate_inode_partial_folio(), where folio->mapping is not NULL. Signed-off-by: Zi Yan --- include/linux/huge_mm.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index 1d439de1ca2c..0d55354e3a34 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -407,6 +407,13 @@ static inline int split_huge_page_to_order(struct page *page, unsigned int new_o static inline int try_folio_split_to_order(struct folio *folio, struct page *page, unsigned int new_order) { + /* + * Folios that just got truncated cannot get split. Signal to the + * caller that there was a race. + */ + if (!folio_test_anon(folio) && !folio->mapping) + return -EBUSY; + if (!folio_split_supported(folio, new_order, SPLIT_TYPE_NON_UNIFORM, /* warns= */ false)) return split_huge_page_to_order(&folio->page, new_order); return folio_split(folio, new_order, page, NULL); -- 2.51.0 It clarifies that folio_split_supported() does not check folio->mapping and can dereference it. Signed-off-by: Zi Yan --- mm/huge_memory.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index efea42d68157..15e555f1b85d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3688,6 +3688,23 @@ static int __split_unmapped_folio(struct folio *folio, int new_order, return 0; } +/** + * folio_split_supported() - check if a folio can be split to a given order + * @folio: folio to be split + * @new_order: the smallest order of the after split folios (since buddy + * allocator like split generates folios with orders from @folio's + * order - 1 to new_order). + * @split_type: uniform or non-uniform split + * @warns: whether gives warnings or not for the checks in the function + * + * folio_split_supported() checks if @folio can be split to @new_order using + * @split_type method. + * + * Context: Caller must make sure folio->mapping is not NULL, since the + * function does not check it and can dereference folio->mapping + * Return: true - @folio can be split to @new_order, false - @folio cannot be + * split + */ bool folio_split_supported(struct folio *folio, unsigned int new_order, enum split_type split_type, bool warns) { -- 2.51.0 min_order_for_split() returns -EBUSY when the folio is truncated and cannot be split. In commit 77008e1b2ef7 ("mm/huge_memory: do not change split_huge_page*() target order silently"), memory_failure() does not handle it and pass -EBUSY to try_to_split_thp_page() directly. try_to_split_thp_page() returns -EINVAL since -EBUSY becomes 0xfffffff0 as new_order is unsigned int in __folio_split() and this large new_order is rejected as an invalid input. The code does not cause a bug. soft_offline_in_use_page() also uses min_order_for_split() but it always passes 0 as new_order for split. Handle it properly by checking min_order_for_split() return value and not calling try_to_split_thp_page() if the value is negative. Add a comment in soft_offline_in_use_page() to clarify the possible negative new_order value. Signed-off-by: Zi Yan --- mm/memory-failure.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 7f908ad795ad..86582f030159 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -2437,8 +2437,11 @@ int memory_failure(unsigned long pfn, int flags) * or unhandlable page. The refcount is bumped iff the * page is a valid handlable page. */ - folio_set_has_hwpoisoned(folio); - err = try_to_split_thp_page(p, new_order, /* release= */ false); + if (new_order >= 0) { + folio_set_has_hwpoisoned(folio); + err = try_to_split_thp_page(p, new_order, /* release= */ false); + } else + err = new_order; /* * If splitting a folio to order-0 fails, kill the process. * Split the folio regardless to minimize unusable pages. @@ -2779,6 +2782,7 @@ static int soft_offline_in_use_page(struct page *page) /* * If new_order (target split order) is not 0, do not split the * folio at all to retain the still accessible large folio. + * new_order can be -EBUSY, meaning the folio cannot be split. * NOTE: if minimizing the number of soft offline pages is * preferred, split it to non-zero new_order like it is done in * memory_failure(). -- 2.51.0