[Devel] [PATCH RHEL7 COMMIT] tcache: fix use-after-free in tcache_invalidate_node_pages()

Konstantin Khorenko khorenko at virtuozzo.com
Mon Dec 28 05:44:23 PST 2015


The commit is pushed to "branch-rh7-3.10.0-229.7.2.vz7.9.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-229.7.2.vz7.9.18
------>
commit d4d33052ae27b0cf4bb28b4bbc8c60468211fe87
Author: Andrey Ryabinin <aryabinin at virtuozzo.com>
Date:   Mon Dec 28 17:44:22 2015 +0400

    tcache: fix use-after-free in tcache_invalidate_node_pages()
    
    tcache_invalidate_node_pages() temporarly drops/takes back node->tree_lock.
    Once lock was dropped, we can't continue iterating to the next slot, because
    another thread might remove and free it. If lock was dropped tree iteration
    has to be restarted.
    With this patch we also drop the lock iff we need to resched the task.
    
    https://jira.sw.ru/browse/PSBM-42104
    
    Signed-off-by: Andrey Ryabinin <aryabinin at virtuozzo.com>
    Acked-by: Vladimir Davydov <vdavydov at virtuozzo.com>
---
 mm/tcache.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/mm/tcache.c b/mm/tcache.c
index b8757cf..9bf7564 100644
--- a/mm/tcache.c
+++ b/mm/tcache.c
@@ -121,8 +121,9 @@ static struct tcache_lru *tcache_lru_node;
 /*
  * Locking rules:
  *
- * - tcache_node_tree->lock nests inside tcache_node->tree_lock
- * - tcache_lru->lock is independent
+ *  tcache_node->tree_lock
+ *       tcache_node_tree->lock
+ *       tcache_lru->lock
  */
 
 /* Enable/disable tcache backend (set at boot time) */
@@ -662,6 +663,7 @@ tcache_invalidate_node_pages(struct tcache_node *node)
 	struct radix_tree_iter iter;
 	struct page *page;
 	void **slot;
+	pgoff_t index = 0;
 
 	spin_lock_irq(&node->tree_lock);
 
@@ -674,19 +676,25 @@ tcache_invalidate_node_pages(struct tcache_node *node)
 	 * Now truncate all pages. Be careful, because pages can still be
 	 * deleted from this node by the shrinker or by concurrent lookups.
 	 */
-	radix_tree_for_each_slot(slot, &node->page_tree, &iter, 0) {
+restart:
+	radix_tree_for_each_slot(slot, &node->page_tree, &iter, index) {
 		page = radix_tree_deref_slot_protected(slot, &node->tree_lock);
 		BUG_ON(!__tcache_page_tree_delete(node, page->index, page));
-		spin_unlock(&node->tree_lock);
-
 		tcache_lru_del(page);
 		put_page(page);
 
-		local_irq_enable();
-		cond_resched();
-		local_irq_disable();
-
-		spin_lock(&node->tree_lock);
+		if (need_resched()) {
+			spin_unlock_irq(&node->tree_lock);
+			cond_resched();
+			spin_lock_irq(&node->tree_lock);
+			/*
+			 * Restart iteration over the radix tree, because the
+			 * current node could have been freed when we dropped
+			 * the lock.
+			 */
+			index = iter.index + 1;
+			goto restart;
+		}
 	}
 
 	BUG_ON(node->nr_pages != 0);


More information about the Devel mailing list