CVE-2022-50014
Publication date:
18/06/2025
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
mm/gup: fix FOLL_FORCE COW security issue and remove FOLL_COW<br />
<br />
Ever since the Dirty COW (CVE-2016-5195) security issue happened, we know<br />
that FOLL_FORCE can be possibly dangerous, especially if there are races<br />
that can be exploited by user space.<br />
<br />
Right now, it would be sufficient to have some code that sets a PTE of a<br />
R/O-mapped shared page dirty, in order for it to erroneously become<br />
writable by FOLL_FORCE. The implications of setting a write-protected PTE<br />
dirty might not be immediately obvious to everyone.<br />
<br />
And in fact ever since commit 9ae0f87d009c ("mm/shmem: unconditionally set<br />
pte dirty in mfill_atomic_install_pte"), we can use UFFDIO_CONTINUE to map<br />
a shmem page R/O while marking the pte dirty. This can be used by<br />
unprivileged user space to modify tmpfs/shmem file content even if the<br />
user does not have write permissions to the file, and to bypass memfd<br />
write sealing -- Dirty COW restricted to tmpfs/shmem (CVE-2022-2590).<br />
<br />
To fix such security issues for good, the insight is that we really only<br />
need that fancy retry logic (FOLL_COW) for COW mappings that are not<br />
writable (!VM_WRITE). And in a COW mapping, we really only broke COW if<br />
we have an exclusive anonymous page mapped. If we have something else<br />
mapped, or the mapped anonymous page might be shared (!PageAnonExclusive),<br />
we have to trigger a write fault to break COW. If we don&#39;t find an<br />
exclusive anonymous page when we retry, we have to trigger COW breaking<br />
once again because something intervened.<br />
<br />
Let&#39;s move away from this mandatory-retry + dirty handling and rely on our<br />
PageAnonExclusive() flag for making a similar decision, to use the same<br />
COW logic as in other kernel parts here as well. In case we stumble over<br />
a PTE in a COW mapping that does not map an exclusive anonymous page, COW<br />
was not properly broken and we have to trigger a fake write-fault to break<br />
COW.<br />
<br />
Just like we do in can_change_pte_writable() added via commit 64fe24a3e05e<br />
("mm/mprotect: try avoiding write faults for exclusive anonymous pages<br />
when changing protection") and commit 76aefad628aa ("mm/mprotect: fix<br />
soft-dirty check in can_change_pte_writable()"), take care of softdirty<br />
and uffd-wp manually.<br />
<br />
For example, a write() via /proc/self/mem to a uffd-wp-protected range has<br />
to fail instead of silently granting write access and bypassing the<br />
userspace fault handler. Note that FOLL_FORCE is not only used for debug<br />
access, but also triggered by applications without debug intentions, for<br />
example, when pinning pages via RDMA.<br />
<br />
This fixes CVE-2022-2590. Note that only x86_64 and aarch64 are<br />
affected, because only those support CONFIG_HAVE_ARCH_USERFAULTFD_MINOR.<br />
<br />
Fortunately, FOLL_COW is no longer required to handle FOLL_FORCE. So<br />
let&#39;s just get rid of it.<br />
<br />
Thanks to Nadav Amit for pointing out that the pte_dirty() check in<br />
FOLL_FORCE code is problematic and might be exploitable.<br />
<br />
Note 1: We don&#39;t check for the PTE being dirty because it doesn&#39;t matter<br />
for making a "was COWed" decision anymore, and whoever modifies the<br />
page has to set the page dirty either way.<br />
<br />
Note 2: Kernels before extended uffd-wp support and before<br />
PageAnonExclusive (
Severity CVSS v4.0: Pending analysis
Last modification:
14/11/2025