Commit cff47b9e authored by Wei Yang's avatar Wei Yang Committed by Andrew Morton
Browse files

mm/huge_memory: fix NULL pointer deference when splitting folio

Commit c010d47f ("mm: thp: split huge page to any lower order pages")
introduced an early check on the folio's order via mapping->flags before
proceeding with the split work.

This check introduced a bug: for shmem folios in the swap cache and
truncated folios, the mapping pointer can be NULL.  Accessing
mapping->flags in this state leads directly to a NULL pointer dereference.

This commit fixes the issue by moving the check for mapping != NULL before
any attempt to access mapping->flags.

Link: https://lkml.kernel.org/r/20251119235302.24773-1-richard.weiyang@gmail.com


Fixes: c010d47f ("mm: thp: split huge page to any lower order pages")
Signed-off-by: default avatarWei Yang <richard.weiyang@gmail.com>
Reviewed-by: default avatarZi Yan <ziy@nvidia.com>
Acked-by: default avatarDavid Hildenbrand (Red Hat) <david@kernel.org>
Reviewed-by: default avatarBaolin Wang <baolin.wang@linux.alibaba.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 6c96c6bd
Loading
Loading
Loading
Loading
+10 −12
Original line number Diff line number Diff line
@@ -3619,6 +3619,16 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
	if (folio != page_folio(split_at) || folio != page_folio(lock_at))
		return -EINVAL;

	/*
	 * Folios that just got truncated cannot get split. Signal to the
	 * caller that there was a race.
	 *
	 * TODO: this will also currently refuse shmem folios that are in the
	 * swapcache.
	 */
	if (!is_anon && !folio->mapping)
		return -EBUSY;

	if (new_order >= folio_order(folio))
		return -EINVAL;

@@ -3659,18 +3669,6 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
		gfp_t gfp;

		mapping = folio->mapping;

		/* Truncated ? */
		/*
		 * TODO: add support for large shmem folio in swap cache.
		 * When shmem is in swap cache, mapping is NULL and
		 * folio_test_swapcache() is true.
		 */
		if (!mapping) {
			ret = -EBUSY;
			goto out;
		}

		min_order = mapping_min_folio_order(folio->mapping);
		if (new_order < min_order) {
			ret = -EINVAL;