From: liyouhong <[email protected]>
min_order_for_split() accesses folio->mapping without proper
synchronization. While the compiler typically caches the value
in a register making a NULL deref unlikely in practice, the
real issue is that the callers in memory-failure.c do not hold
the folio lock at the time of the call:
- memory_failure() explicitly drops the folio lock before calling
min_order_for_split().
- soft_offline_in_use_page() has not yet acquired the folio lock
when calling min_order_for_split().
This means the value of folio->mapping may be modified by a
truncate or invalidate operation while min_order_for_split() is
executing, leading to a torn read or use of a stale mapping value.
Fixes: 689b8986776c ("mm/memory-failure: improve large block size folio handling")
Reported-by: Sashiko <[email protected]>
Closes: https://sashiko.dev/#/patchset/[email protected]
Cc: [email protected]
Signed-off-by: liyouhong <[email protected]>
---
v2:
- Dropped the approach of caching folio->mapping inside min_order_for_split() in favor of adding the folio lock at the callers.
- Added VM_WARN_ON_ONCE_FOLIO() in min_order_for_split().
- Updated the commit message to clarify that the real issue is the callers not holding the folio lock, rather than a TOCTOU race.
v1: https://lore.kernel.org/all/[email protected]/
---
mm/huge_memory.c | 2 ++
mm/memory-failure.c | 12 ++++++++++--
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 58cabe6af33d..e3f16dadc1d4 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4300,6 +4300,8 @@ int folio_split(struct folio *folio, unsigned int new_order,
*/
unsigned int min_order_for_split(struct folio *folio)
{
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
if (folio_test_anon(folio))
return 0;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 3b1e6946821b..7391524b5046 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -2437,12 +2437,17 @@ int memory_failure(unsigned long pfn, int flags)
res = -EOPNOTSUPP;
goto unlock_mutex;
}
+
folio_unlock(folio);
if (folio_test_large(folio)) {
- const int new_order = min_order_for_split(folio);
+ const int new_order;
int err;
+ folio_lock(folio);
+ new_order = min_order_for_split(folio);
+ folio_unlock(folio);
+
/*
* The flag must be set after the refcount is bumped
* otherwise it may race with THP split.
@@ -2796,8 +2801,11 @@ static int soft_offline_in_use_page(struct page *page)
};
if (!huge && folio_test_large(folio)) {
- const int new_order = min_order_for_split(folio);
+ const int new_order;
+ folio_lock(folio);
+ new_order = min_order_for_split(folio);
+ folio_unlock(folio);
/*
* If new_order (target split order) is not 0, do not split the
* folio at all to retain the still accessible large folio.
--
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.