[linux] 03/06: mm: migrate dirty page without clear_page_dirty_for_io etc (CVE-2016-3070)

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Fri Jun 24 19:56:44 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch jessie-security
in repository linux.

commit 7972ce88889b6a239b28a10cc4dc48f4bde6df14
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Fri Jun 24 21:45:25 2016 +0200

    mm: migrate dirty page without clear_page_dirty_for_io etc (CVE-2016-3070)
---
 debian/changelog                                   |   1 +
 ...-page-without-clear_page_dirty_for_io-etc.patch | 154 +++++++++++++++++++++
 debian/patches/series                              |   1 +
 3 files changed, 156 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index d42442f..a6cdf8c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -35,6 +35,7 @@ linux (3.16.7-ckt25-2+deb8u1) UNRELEASED; urgency=medium
   * USB: cdc-acm: more sanity checking (CVE-2016-3138)
   * USB: digi_acceleport: do sanity checking for the number of ports
     (CVE-2016-3140)
+  * mm: migrate dirty page without clear_page_dirty_for_io etc (CVE-2016-3070)
 
   [ Salvatore Bonaccorso ]
   * [x86] USB: usbip: fix potential out-of-bounds write (CVE-2016-3955)
diff --git a/debian/patches/bugfix/all/mm-migrate-dirty-page-without-clear_page_dirty_for_io-etc.patch b/debian/patches/bugfix/all/mm-migrate-dirty-page-without-clear_page_dirty_for_io-etc.patch
new file mode 100644
index 0000000..e88c81d
--- /dev/null
+++ b/debian/patches/bugfix/all/mm-migrate-dirty-page-without-clear_page_dirty_for_io-etc.patch
@@ -0,0 +1,154 @@
+From: Hugh Dickins <hughd at google.com>
+Date: Thu, 5 Nov 2015 18:50:05 -0800
+Subject: mm: migrate dirty page without clear_page_dirty_for_io etc
+Origin: https://git.kernel.org/linus/42cb14b110a5698ccf26ce59c4441722605a3743
+
+clear_page_dirty_for_io() has accumulated writeback and memcg subtleties
+since v2.6.16 first introduced page migration; and the set_page_dirty()
+which completed its migration of PageDirty, later had to be moderated to
+__set_page_dirty_nobuffers(); then PageSwapBacked had to skip that too.
+
+No actual problems seen with this procedure recently, but if you look into
+what the clear_page_dirty_for_io(page)+set_page_dirty(newpage) is actually
+achieving, it turns out to be nothing more than moving the PageDirty flag,
+and its NR_FILE_DIRTY stat from one zone to another.
+
+It would be good to avoid a pile of irrelevant decrementations and
+incrementations, and improper event counting, and unnecessary descent of
+the radix_tree under tree_lock (to set the PAGECACHE_TAG_DIRTY which
+radix_tree_replace_slot() left in place anyway).
+
+Do the NR_FILE_DIRTY movement, like the other stats movements, while
+interrupts still disabled in migrate_page_move_mapping(); and don't even
+bother if the zone is the same.  Do the PageDirty movement there under
+tree_lock too, where old page is frozen and newpage not yet visible:
+bearing in mind that as soon as newpage becomes visible in radix_tree, an
+un-page-locked set_page_dirty() might interfere (or perhaps that's just
+not possible: anything doing so should already hold an additional
+reference to the old page, preventing its migration; but play safe).
+
+But we do still need to transfer PageDirty in migrate_page_copy(), for
+those who don't go the mapping route through migrate_page_move_mapping().
+
+Signed-off-by: Hugh Dickins <hughd at google.com>
+Cc: Christoph Lameter <cl at linux.com>
+Cc: "Kirill A. Shutemov" <kirill.shutemov at linux.intel.com>
+Cc: Rik van Riel <riel at redhat.com>
+Cc: Vlastimil Babka <vbabka at suse.cz>
+Cc: Davidlohr Bueso <dave at stgolabs.net>
+Cc: Oleg Nesterov <oleg at redhat.com>
+Cc: Sasha Levin <sasha.levin at oracle.com>
+Cc: Dmitry Vyukov <dvyukov at google.com>
+Cc: KOSAKI Motohiro <kosaki.motohiro at jp.fujitsu.com>
+Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+[bwh: Backported to 3.16: adjust context.  This is not just an optimisation,
+ but turned out to fix a possible oops (CVE-2016-3070).]
+Signed-off-by: Ben Hutchings <ben at decadent.org.uk>
+---
+ mm/migrate.c | 51 +++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 31 insertions(+), 20 deletions(-)
+
+--- a/mm/migrate.c
++++ b/mm/migrate.c
+@@ -30,6 +30,7 @@
+ #include <linux/mempolicy.h>
+ #include <linux/vmalloc.h>
+ #include <linux/security.h>
++#include <linux/backing-dev.h>
+ #include <linux/memcontrol.h>
+ #include <linux/syscalls.h>
+ #include <linux/hugetlb.h>
+@@ -342,6 +343,8 @@ int migrate_page_move_mapping(struct add
+ 		struct buffer_head *head, enum migrate_mode mode,
+ 		int extra_count)
+ {
++	struct zone *oldzone, *newzone;
++	int dirty;
+ 	int expected_count = 1 + extra_count;
+ 	void **pslot;
+ 
+@@ -352,6 +355,9 @@ int migrate_page_move_mapping(struct add
+ 		return MIGRATEPAGE_SUCCESS;
+ 	}
+ 
++	oldzone = page_zone(page);
++	newzone = page_zone(newpage);
++
+ 	spin_lock_irq(&mapping->tree_lock);
+ 
+ 	pslot = radix_tree_lookup_slot(&mapping->page_tree,
+@@ -392,6 +398,13 @@ int migrate_page_move_mapping(struct add
+ 		set_page_private(newpage, page_private(page));
+ 	}
+ 
++	/* Move dirty while page refs frozen and newpage not yet exposed */
++	dirty = PageDirty(page);
++	if (dirty) {
++		ClearPageDirty(page);
++		SetPageDirty(newpage);
++	}
++
+ 	radix_tree_replace_slot(pslot, newpage);
+ 
+ 	/*
+@@ -401,6 +414,9 @@ int migrate_page_move_mapping(struct add
+ 	 */
+ 	page_unfreeze_refs(page, expected_count - 1);
+ 
++	spin_unlock(&mapping->tree_lock);
++	/* Leave irq disabled to prevent preemption while updating stats */
++
+ 	/*
+ 	 * If moved to a different zone then also account
+ 	 * the page for that zone. Other VM counters will be
+@@ -411,13 +427,19 @@ int migrate_page_move_mapping(struct add
+ 	 * via NR_FILE_PAGES and NR_ANON_PAGES if they
+ 	 * are mapped to swap space.
+ 	 */
+-	__dec_zone_page_state(page, NR_FILE_PAGES);
+-	__inc_zone_page_state(newpage, NR_FILE_PAGES);
+-	if (!PageSwapCache(page) && PageSwapBacked(page)) {
+-		__dec_zone_page_state(page, NR_SHMEM);
+-		__inc_zone_page_state(newpage, NR_SHMEM);
++	if (newzone != oldzone) {
++		__dec_zone_state(oldzone, NR_FILE_PAGES);
++		__inc_zone_state(newzone, NR_FILE_PAGES);
++		if (PageSwapBacked(page) && !PageSwapCache(page)) {
++			__dec_zone_state(oldzone, NR_SHMEM);
++			__inc_zone_state(newzone, NR_SHMEM);
++		}
++		if (dirty && mapping_cap_account_dirty(mapping)) {
++			__dec_zone_state(oldzone, NR_FILE_DIRTY);
++			__inc_zone_state(newzone, NR_FILE_DIRTY);
++		}
+ 	}
+-	spin_unlock_irq(&mapping->tree_lock);
++	local_irq_enable();
+ 
+ 	return MIGRATEPAGE_SUCCESS;
+ }
+@@ -541,20 +563,9 @@ void migrate_page_copy(struct page *newp
+ 	if (PageMappedToDisk(page))
+ 		SetPageMappedToDisk(newpage);
+ 
+-	if (PageDirty(page)) {
+-		clear_page_dirty_for_io(page);
+-		/*
+-		 * Want to mark the page and the radix tree as dirty, and
+-		 * redo the accounting that clear_page_dirty_for_io undid,
+-		 * but we can't use set_page_dirty because that function
+-		 * is actually a signal that all of the page has become dirty.
+-		 * Whereas only part of our page may be dirty.
+-		 */
+-		if (PageSwapBacked(page))
+-			SetPageDirty(newpage);
+-		else
+-			__set_page_dirty_nobuffers(newpage);
+- 	}
++	/* Move dirty on pages not done by migrate_page_move_mapping() */
++	if (PageDirty(page))
++		SetPageDirty(newpage);
+ 
+ 	/*
+ 	 * Copy NUMA information to the new page, to prevent over-eager
diff --git a/debian/patches/series b/debian/patches/series
index 85c3752..e9ae6d7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -696,3 +696,4 @@ bugfix/all/usb-mct_u232-add-sanity-checking-in-probe.patch
 bugfix/all/usb-cypress_m8-add-endpoint-sanity-check.patch
 bugfix/all/usb-cdc-acm-more-sanity-checking.patch
 bugfix/all/usb-digi_acceleport-do-sanity-checking-for-the-numbe.patch
+bugfix/all/mm-migrate-dirty-page-without-clear_page_dirty_for_io-etc.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list