Skip to content

Commit d0d8f9c

Browse files
committed
Merge: mm/swap: fix race when skipping swapcache
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/4031 JIRA: https://issues.redhat.com/browse/RHEL-31646 CVE: CVE-2024-26759 The commit in Fixes introduces a way to skip the swapcache for fast devices like zram, pmem and btt as a mean to reduce swap-in latency. By doing so, however it introduces the race conditions leading to the noted CVE. Fixes: 0bcac06 ("mm,swap: skip swapcache for swapin of synchronous device") Signed-off-by: Rafael Aquini <aquini@redhat.com> Approved-by: Nico Pache <npache@redhat.com> Approved-by: Chris von Recklinghausen <crecklin@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Lucas Zampieri <lzampier@redhat.com>
2 parents 4c424d6 + da2c6c4 commit d0d8f9c

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

include/linux/swap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,11 @@ static inline int swap_duplicate(swp_entry_t swp)
583583
return 0;
584584
}
585585

586+
static inline int swapcache_prepare(swp_entry_t swp)
587+
{
588+
return 0;
589+
}
590+
586591
static inline void swap_free(swp_entry_t swp)
587592
{
588593
}

mm/memory.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3730,6 +3730,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
37303730
struct page *page;
37313731
struct swap_info_struct *si = NULL;
37323732
rmap_t rmap_flags = RMAP_NONE;
3733+
bool need_clear_cache = false;
37333734
bool exclusive = false;
37343735
swp_entry_t entry;
37353736
pte_t pte;
@@ -3793,6 +3794,20 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
37933794
if (!folio) {
37943795
if (data_race(si->flags & SWP_SYNCHRONOUS_IO) &&
37953796
__swap_count(entry) == 1) {
3797+
/*
3798+
* Prevent parallel swapin from proceeding with
3799+
* the cache flag. Otherwise, another thread may
3800+
* finish swapin first, free the entry, and swapout
3801+
* reusing the same entry. It's undetectable as
3802+
* pte_same() returns true due to entry reuse.
3803+
*/
3804+
if (swapcache_prepare(entry)) {
3805+
/* Relax a bit to prevent rapid repeated page faults */
3806+
schedule_timeout_uninterruptible(1);
3807+
goto out;
3808+
}
3809+
need_clear_cache = true;
3810+
37963811
/* skip swapcache */
37973812
folio = vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0,
37983813
vma, vmf->address, false);
@@ -4041,6 +4056,9 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
40414056
if (vmf->pte)
40424057
pte_unmap_unlock(vmf->pte, vmf->ptl);
40434058
out:
4059+
/* Clear the swap cache pin for direct swapin after PTL unlock */
4060+
if (need_clear_cache)
4061+
swapcache_clear(si, entry);
40444062
if (si)
40454063
put_swap_device(si);
40464064
return ret;
@@ -4055,6 +4073,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
40554073
folio_unlock(swapcache);
40564074
folio_put(swapcache);
40574075
}
4076+
if (need_clear_cache)
4077+
swapcache_clear(si, entry);
40584078
if (si)
40594079
put_swap_device(si);
40604080
return ret;

mm/swap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ void __delete_from_swap_cache(struct folio *folio,
3838
void delete_from_swap_cache(struct folio *folio);
3939
void clear_shadow_from_swap_cache(int type, unsigned long begin,
4040
unsigned long end);
41+
void swapcache_clear(struct swap_info_struct *si, swp_entry_t entry);
4142
struct folio *swap_cache_get_folio(swp_entry_t entry,
4243
struct vm_area_struct *vma, unsigned long addr);
4344
struct folio *filemap_get_incore_folio(struct address_space *mapping,
@@ -97,6 +98,10 @@ static inline int swap_writepage(struct page *p, struct writeback_control *wbc)
9798
return 0;
9899
}
99100

101+
static inline void swapcache_clear(struct swap_info_struct *si, swp_entry_t entry)
102+
{
103+
}
104+
100105
static inline struct folio *swap_cache_get_folio(swp_entry_t entry,
101106
struct vm_area_struct *vma, unsigned long addr)
102107
{

mm/swapfile.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3400,6 +3400,19 @@ int swapcache_prepare(swp_entry_t entry)
34003400
return __swap_duplicate(entry, SWAP_HAS_CACHE);
34013401
}
34023402

3403+
void swapcache_clear(struct swap_info_struct *si, swp_entry_t entry)
3404+
{
3405+
struct swap_cluster_info *ci;
3406+
unsigned long offset = swp_offset(entry);
3407+
unsigned char usage;
3408+
3409+
ci = lock_cluster_or_swap_info(si, offset);
3410+
usage = __swap_entry_free_locked(si, offset, SWAP_HAS_CACHE);
3411+
unlock_cluster_or_swap_info(si, ci);
3412+
if (!usage)
3413+
free_swap_slot(entry);
3414+
}
3415+
34033416
struct swap_info_struct *swp_swap_info(swp_entry_t entry)
34043417
{
34053418
return swap_type_to_swap_info(swp_type(entry));

0 commit comments

Comments
 (0)