[SRU][F][PATCH 0/1] CVE-2024-26960

Koichiro Den koichiro.den at canonical.com
Thu Sep 5 06:36:43 UTC 2024


[Impact]

mm: swap: fix race between free_swap_and_cache() and swapoff()

There was previously a theoretical window where swapoff() could run and
teardown a swap_info_struct while a call to free_swap_and_cache() was
running in another thread.  This could cause, amongst other bad
possibilities, swap_page_trans_huge_swapped() (called by
free_swap_and_cache()) to access the freed memory for swap_map.

This is a theoretical problem and I haven't been able to provoke it from a
test case.  But there has been agreement based on code review that this is
possible (see link below).

Fix it by using get_swap_device()/put_swap_device(), which will stall
swapoff().  There was an extra check in _swap_info_get() to confirm that
the swap entry was not free.  This isn't present in get_swap_device()
because it doesn't make sense in general due to the race between getting
the reference and swapoff.  So I've added an equivalent check directly in
free_swap_and_cache().

Details of how to provoke one possible issue:

--8<-----

CPU0                               CPU1
----                               ----
shmem_undo_range
  shmem_free_swap
    xa_cmpxchg_irq
    free_swap_and_cache
      __swap_entry_free
      /* swap_count() become 0 */
                                   swapoff
                                     try_to_unuse
                                       shmem_unuse /* cannot find swap entry */
                                       find_next_to_unuse
                                       filemap_get_folio
                                       folio_free_swap
                                       /* remove swap cache */
                                       /* free si->swap_map[] */
      swap_page_trans_huge_swapped <-- access freed si->swap_map !!!

--8<-----

[Backport]

First and foremost, the follow-up v3 patch [1] offered a clearer description 
of the potential race condition and also removed an incorrect comment. 
However, it did not make it to mm-stable, while the v2 patch was merged.
Later, the comment was revised in [2], which depends on commit 
a95722a04772 ("swap: comments get_swap_device() with usage rule"). 
As this is a CVE backport patch, we typically do not merge such comment 
improvements; hence we are merging [1] only.

Additionally, there were conflicts due to missing commits:
- commit 33e16272fe98 ("mm/swapfile.c: __swap_entry_free() always free 1 entry")
- commit a95722a04772 ("swap: comments get_swap_device() with usage rule")

I resolved these conflicts by adjusting contexts without merging the commits.

[1] https://lore.kernel.org/all/20240311084426.447164-1-ying.huang@intel.com/
[2] https://lkml.kernel.org/r/20240407065450.498821-1-ying.huang@intel.com

[Fix]

Noble:  fixed via stable
Jammy:  fixed via stable
Focal:  Backport - adjusted contexts due to missing commits, see [Backport]
Bionic: fix sent to esm ML
Xenial: not affected
Trusty: not affected

[Test case]

Compile and boot tested.

Additionally, this backport was stress tested by the following procedure
on my amd64 kvm setup with 8GB RAM. Despite extensive testing, the issue
could not be reproduced on the kernel without this backport, likely due
to the narrow race window and the fact that it's hard to make it happen
so frequently. Still, this proved the robustness of the backport patch.

  # Create swap file
  sudo fallocate -l 4G /swapfile
  sudo chmod 600 /swapfile
  sudo mkswap /swapfile
  sudo mount -o remount,size=7G /dev/shm

  # Test script
  $ cat ~/test.sh
  #!/bin/bash
  sudo swapon /swapfile
  cat /dev/urandom > /dev/shm/test
  # trigger shmem_undo_range -> free_swap_and_cache
  echo > /dev/shm/test &
  # try to swapoff
  while ! sudo swapoff /swapfile; do
    :
  done

  # Run
  $ while :; do ./test.sh; done

[Where problem could occur]

This fix affects all architectures and is generic in nature. It
addresses a potential UAF that could occur if, during final
free_swap_and_cache() for shm, a swapoff operation subtly slips in and
frees the swap_map before swap_page_trans_huge_swapped() is invoked.

Ryan Roberts (1):
  mm: swap: fix race between free_swap_and_cache() and swapoff()

 mm/swapfile.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

-- 
2.43.0




More information about the kernel-team mailing list