Dirty COW (CVE-2016-5195): Recompile Linux Kernel 4.2 with the Fix Applied

Dirty COW (CVE-2016-5195) is a privilege escalation vulnerability in the Linux kernel.

The Problem

My laptop runs Debian Jessie with a custom 4.2 kernel, which isn’t maintained by Debian.

The kernel is vulnerable to the publicly available exploit:

$ ./dirtyc0w foo m00000000000000000
mmap 7f83599c6000

madvise 0

procselfmem 1800000000
$ cat foo 
m00000000000000000

How to Fix?

We can recompile the kernel with the fix applied.

Apply Fixes to the Kernel Source

The fix is available here:

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619

Navigate to the kernel source directory /usr/src/linux-4.2.6/.

Apply the following fix (the line in green has to be added) to the file include/linux/mm.h:

#define FOLL_REMOTE 0x2000 /* we are working on non-current tsk/mm */
#define FOLL_COW 0x4000 /* internal GUP flag */

Verify:

$ grep -n FOLL_COW include/linux/mm.h
2095:#define FOLL_COW 0x4000 /* internal GUP flag */

Apply the following fixes to the file mm/gup.c. All lines in green need to be added, all lines in red must be removed or commented out.

}
// around line #35 in mm/gup.c
static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
{
        return pte_write(pte) ||
                ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
}

static struct page *follow_page_pte(struct vm_area_struct *vma,
                unsigned long address, pmd_t *pmd, unsigned int flags)
// around line #75 in mm/gup.c
}
if ((flags & FOLL_NUMA) && pte_protnone(pte))
	goto no_page;
// if ((flags & FOLL_WRITE) && !pte_write(pte)) {
if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
	pte_unmap_unlock(ptep, ptl);
	return NULL;
}
// around line #324 in mm/gup.c
if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
//	*flags &= ~FOLL_WRITE;
        *flags |= FOLL_COW;
return 0;

Verify:

$ grep -n can_follow_write_pte mm/gup.c
35:static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
75: if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
$ grep -n "= FOLL_COW" mm/gup.c
324: *flags |= FOLL_COW;

Recompile the Kernel

It should go without saying, ensure that you have a backup of the working kernel before installing the new one.

# make -j3
# make modules_install install

Reboot.

$ uname -rv
4.2.6-dev #4 SMP Sat Oct 29 10:52:26 BST 2016

The kernel shouldn’t be vulnerable anymore:

$ ./dirtyc0w foo m00000000000000000
mmap 7f23f122f000

madvise 0

procselfmem 1800000000
$ cat foo 
this is not a test

Leave a Reply

Your email address will not be published. Required fields are marked *