[CentOS] CVE-2016-5195 DirtyCOW : Critical Linux Kernel Flaw

Wed Oct 26 00:21:54 UTC 2016
Akemi Yagi <amyagi at gmail.com>

On Tue, Oct 25, 2016 at 10:26 AM, Leon Fauster
<leonfauster at googlemail.com> wrote:
> Am 25.10.2016 um 15:39 schrieb Peter Kjellström <cap at nsc.liu.se>:
>> On Tue, 25 Oct 2016 10:06:12 +0200
>> Christian Anthon <anthon at rth.dk> wrote:
>>
>>> What is the best approach on centos 6 to mitigate the problem is
>>> officially patched? As far as I can tell Centos 6 is vulnerable to
>>> attacks using ptrace.
>>
>> I can confirm that c6 is vulnerable, we're running a patched kernel
>> (local build) using a rhel6 adaptation of the upstream fix.
>>
>> Ask off-list if you want an src.rpm
>
>
> Hi Peter, can you confirm that its this?
>
> http://pastebin.centos.org/56391/

That is for the EL-7.2 kernel. Peter was offering a patch for CentOS 6.

RH released the patched kernel for EL-6.8 today. I have attached the
diff file between 2.6.32-642.6.1.el6 and 2.6.32-642.6.2.el6. It is
more complex because the 6 kernel is older, so required more mods, I
suppose. Maybe that was the reason why the EL-6 update took longer
than EL-7.

Akemi
-------------- next part --------------
diff -uNpr linux-2.6.32-642.6.1.el6/include/linux/mm.h linux-2.6.32-642.6.2.el6/include/linux/mm.h
--- linux-2.6.32-642.6.1.el6/include/linux/mm.h	2016-08-25 08:07:47.000000000 -0700
+++ linux-2.6.32-642.6.2.el6/include/linux/mm.h	2016-10-24 06:19:16.000000000 -0700
@@ -1420,6 +1420,7 @@ struct page *follow_page(struct vm_area_
 #define FOLL_HWPOISON	0x100	/* check page is hwpoisoned */
 #define FOLL_NUMA	0x200	/* force NUMA hinting page fault */
 #define FOLL_MIGRATION	0x400	/* wait for page to replace migration entry */
+#define FOLL_COW	0x4000	/* internal GUP flag */
 
 typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
 			void *data);
diff -uNpr linux-2.6.32-642.6.1.el6/mm/memory.c linux-2.6.32-642.6.2.el6/mm/memory.c
--- linux-2.6.32-642.6.1.el6/mm/memory.c	2016-08-25 08:06:57.000000000 -0700
+++ linux-2.6.32-642.6.2.el6/mm/memory.c	2016-10-24 06:19:16.000000000 -0700
@@ -1177,6 +1177,24 @@ int zap_vma_ptes(struct vm_area_struct *
 }
 EXPORT_SYMBOL_GPL(zap_vma_ptes);
 
+static inline bool can_follow_write_pte(pte_t pte, struct page *page,
+					unsigned int flags)
+{
+	if (pte_write(pte))
+		return true;
+
+	/*
+	 * Make sure that we are really following CoWed page. We do not really
+	 * have to care about exclusiveness of the page because we only want
+	 * to ensure that once COWed page hasn't disappeared in the meantime
+	 * or it hasn't been merged to a KSM page.
+	 */
+	if ((flags & FOLL_FORCE) && (flags & FOLL_COW))
+		return page && PageAnon(page) && !PageKsm(page);
+
+	return false;
+}
+
 /*
  * Do a quick page-table lookup for a single page.
  */
@@ -1266,10 +1284,11 @@ split_fallthrough:
 		migration_entry_wait(mm, pmd, address);
 		goto split_fallthrough;
 	}
-	if ((flags & FOLL_WRITE) && !pte_write(pte))
-		goto unlock;
-
 	page = vm_normal_page(vma, address, pte);
+	if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, page, flags)) {
+		pte_unmap_unlock(ptep, ptl);
+		return NULL;
+	}
 	if (unlikely(!page)) {
 		if ((flags & FOLL_DUMP) ||
 		    !is_zero_pfn(pte_pfn(pte)))
@@ -1290,7 +1309,6 @@ split_fallthrough:
 		 */
 		mark_page_accessed(page);
 	}
-unlock:
 	pte_unmap_unlock(ptep, ptl);
 out:
 	return page;
@@ -1489,17 +1507,13 @@ int __get_user_pages(struct task_struct 
 				 * The VM_FAULT_WRITE bit tells us that
 				 * do_wp_page has broken COW when necessary,
 				 * even if maybe_mkwrite decided not to set
-				 * pte_write. We can thus safely do subsequent
-				 * page lookups as if they were reads. But only
-				 * do so when looping for pte_write is futile:
-				 * in some cases userspace may also be wanting
-				 * to write to the gotten user page, which a
-				 * read fault here might prevent (a readonly
-				 * page might get reCOWed by userspace write).
+				 * pte_write. We cannot simply drop FOLL_WRITE
+				 * here because the COWed page might be gone by
+				 * the time we do the subsequent page lookups.
 				 */
 				if ((ret & VM_FAULT_WRITE) &&
 				    !(vma->vm_flags & VM_WRITE))
-					foll_flags &= ~FOLL_WRITE;
+					foll_flags |= FOLL_COW;
 
 				cond_resched();
 			}