mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
mm/gup: check folio->mapping under folio lock in memfd_pin_folios()
memfd_pin_folios() pins folios from the page cache without taking the
folio lock. khugepaged or MADV_COLLAPSE can therefore replace a folio
after checking its refcount while memfd_pin_folios() still pins the old
folio. The caller then receives a folio which is no longer part of the
memfd.
For udmabuf and iommufd, this can cause data corruption: DMA accesses
the detached folio rather than the live memfd contents. DMA reads can
return stale data, while DMA writes update the detached copy and are
lost from the live memfd. The detached folio remains pinned, so these
accesses do not constitute a use-after-free or target another
application's memory. For memfd live update, the stale contents can
reappear in the memfd after handover.
Lock the folio and recheck folio->mapping before pinning it, like other
page cache users do. If the folio was removed, retry the folio lookup
from the index that was removed.
Fixes: 89c1905d9c ("mm/gup: introduce memfd_pin_folios() for pinning memfd folios")
Reported-by: Andy Lutomirski <luto@amacapital.net>
Closes: https://lore.kernel.org/all/CALCETrV5eqg6d9=+BQhCpRwBNe3aCQiut8vxPBs8b7PToqjaHg@mail.gmail.com/
Cc: stable@vger.kernel.org
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
Reviewed-by: Andy Lutomirski <luto@amacapital.net>
Link: https://patch.msgid.link/20261031203540.2282507-1-skhawaja@google.com
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
This commit is contained in:
parent
eadce52a9e
commit
71555ae8c7
29
mm/gup.c
29
mm/gup.c
|
|
@ -3471,7 +3471,7 @@ long memfd_pin_folios(struct file *memfd, loff_t start, loff_t end,
|
|||
{
|
||||
unsigned int flags, nr_folios, nr_found;
|
||||
unsigned int i, pgshift = PAGE_SHIFT;
|
||||
pgoff_t start_idx, end_idx;
|
||||
pgoff_t start_idx, end_idx, retry_idx;
|
||||
struct folio *folio = NULL;
|
||||
struct folio_batch fbatch;
|
||||
struct hstate *h;
|
||||
|
|
@ -3512,6 +3512,7 @@ long memfd_pin_folios(struct file *memfd, loff_t start, loff_t end,
|
|||
* reason, we try to allocate them and add them to the
|
||||
* page cache.
|
||||
*/
|
||||
retry_idx = start_idx;
|
||||
nr_found = filemap_get_folios_contig(memfd->f_mapping,
|
||||
&start_idx,
|
||||
end_idx,
|
||||
|
|
@ -3524,7 +3525,21 @@ long memfd_pin_folios(struct file *memfd, loff_t start, loff_t end,
|
|||
for (i = 0; i < nr_found; i++) {
|
||||
folio = fbatch.folios[i];
|
||||
|
||||
/*
|
||||
* The folio might have been removed from the
|
||||
* page cache (e.g., truncated or collapsed)
|
||||
* after the lookup. Recheck under the folio
|
||||
* lock.
|
||||
*/
|
||||
folio_lock(folio);
|
||||
if (unlikely(folio->mapping != memfd->f_mapping)) {
|
||||
folio_unlock(folio);
|
||||
start_idx = retry_idx;
|
||||
break;
|
||||
}
|
||||
|
||||
if (try_grab_folio(folio, 1, FOLL_PIN)) {
|
||||
folio_unlock(folio);
|
||||
folio_batch_release(&fbatch);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
|
@ -3535,14 +3550,12 @@ long memfd_pin_folios(struct file *memfd, loff_t start, loff_t end,
|
|||
* page cache that were never cleared.
|
||||
*/
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
folio_lock(folio);
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
folio_zero_range(folio, 0, folio_size(folio));
|
||||
flush_dcache_folio(folio);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
folio_unlock(folio);
|
||||
folio_zero_range(folio, 0, folio_size(folio));
|
||||
flush_dcache_folio(folio);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
retry_idx = folio_next_index(folio);
|
||||
folio_unlock(folio);
|
||||
|
||||
if (nr_folios == 0)
|
||||
*offset = offset_in_folio(folio, start);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user