[Devel] [PATCH rh7 v4] tcache: fix use-after-free in tcache_invalidate_node_pages()

Andrey Ryabinin aryabinin at virtuozzo.com
Wed Dec 9 02:51:21 PST 2015


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.
Wit 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>
---
 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);
-- 
2.4.10



More information about the Devel mailing list