Skip to content

Commit 9c1e345

Browse files
committed
swap: fix do_swap_page() race with swapoff
jira LE-1907 cve CVE-2024-26759 Rebuild_History Non-Buildable kernel-4.18.0-553.8.1.el8_10 commit-author Miaohe Lin <[email protected]> commit 2799e77 When I was investigating the swap code, I found the below possible race window: CPU 1 CPU 2 ----- ----- do_swap_page if (data_race(si->flags & SWP_SYNCHRONOUS_IO) swap_readpage if (data_race(sis->flags & SWP_FS_OPS)) { swapoff .. p->swap_file = NULL; .. struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping;[oops!] Note that for the pages that are swapped in through swap cache, this isn't an issue. Because the page is locked, and the swap entry will be marked with SWAP_HAS_CACHE, so swapoff() can not proceed until the page has been unlocked. Fix this race by using get/put_swap_device() to guard against concurrent swapoff. Link: https://lkml.kernel.org/r/[email protected] Fixes: 0bcac06 ("mm,swap: skip swapcache for swapin of synchronous device") Signed-off-by: Miaohe Lin <[email protected]> Reviewed-by: "Huang, Ying" <[email protected]> Cc: Alex Shi <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Dennis Zhou <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Tim Chen <[email protected]> Cc: Wei Yang <[email protected]> Cc: Yang Shi <[email protected]> Cc: Yu Zhao <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> (cherry picked from commit 2799e77) Signed-off-by: Jonathan Maple <[email protected]>
1 parent dcb459c commit 9c1e345

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

include/linux/swap.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,15 @@ static inline struct swap_info_struct *swp_swap_info(swp_entry_t entry)
496496
return NULL;
497497
}
498498

499+
static inline struct swap_info_struct *get_swap_device(swp_entry_t entry)
500+
{
501+
return NULL;
502+
}
503+
504+
static inline void put_swap_device(struct swap_info_struct *si)
505+
{
506+
}
507+
499508
#define swap_address_space(entry) (NULL)
500509
#define get_nr_swap_pages() 0L
501510
#define total_swap_pages 0L

mm/memory.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
31793179
{
31803180
struct vm_area_struct *vma = vmf->vma;
31813181
struct page *page = NULL, *swapcache;
3182+
struct swap_info_struct *si = NULL;
31823183
swp_entry_t entry;
31833184
pte_t pte;
31843185
int locked;
@@ -3206,14 +3207,16 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
32063207
goto out;
32073208
}
32083209

3210+
/* Prevent swapoff from happening to us. */
3211+
si = get_swap_device(entry);
3212+
if (unlikely(!si))
3213+
goto out;
32093214

32103215
delayacct_set_flag(current, DELAYACCT_PF_SWAPIN);
32113216
page = lookup_swap_cache(entry, vma, vmf->address);
32123217
swapcache = page;
32133218

32143219
if (!page) {
3215-
struct swap_info_struct *si = swp_swap_info(entry);
3216-
32173220
if (data_race(si->flags & SWP_SYNCHRONOUS_IO) &&
32183221
__swap_count(entry) == 1) {
32193222
/* skip swapcache */
@@ -3382,6 +3385,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
33823385
unlock:
33833386
pte_unmap_unlock(vmf->pte, vmf->ptl);
33843387
out:
3388+
if (si)
3389+
put_swap_device(si);
33853390
return ret;
33863391
out_nomap:
33873392
pte_unmap_unlock(vmf->pte, vmf->ptl);
@@ -3393,6 +3398,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
33933398
unlock_page(swapcache);
33943399
put_page(swapcache);
33953400
}
3401+
if (si)
3402+
put_swap_device(si);
33963403
return ret;
33973404
}
33983405

0 commit comments

Comments
 (0)