kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Alexandre Chartre <alexandre.chartre@oracle.com>
To: pbonzini@redhat.com, rkrcmar@redhat.com, tglx@linutronix.de,
	mingo@redhat.com, bp@alien8.de, hpa@zytor.com,
	dave.hansen@linux.intel.com, luto@kernel.org,
	peterz@infradead.org, kvm@vger.kernel.org, x86@kernel.org,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org
Cc: konrad.wilk@oracle.com, jan.setjeeilers@oracle.com,
	liran.alon@oracle.com, jwadams@google.com, graf@amazon.de,
	rppt@linux.vnet.ibm.com, alexandre.chartre@oracle.com
Subject: [RFC v2 14/26] mm/asi: Handle ASI mapped range leaks and overlaps
Date: Thu, 11 Jul 2019 16:25:26 +0200	[thread overview]
Message-ID: <1562855138-19507-15-git-send-email-alexandre.chartre@oracle.com> (raw)
In-Reply-To: <1562855138-19507-1-git-send-email-alexandre.chartre@oracle.com>

When mapping a buffer in an ASI page-table, data around the buffer can
also be mapped if the entire buffer is not aligned with the page directory
size used for the mapping. So, data can potentially leak into the ASI
page-table. In such a case, print a warning that data are leaking.

Also data effectively mapped can overlap with an already mapped buffer.
This is not an issue when mapping data but, when unmapping, make sure
data from another buffer don't get unmapped as a side effect.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 arch/x86/mm/asi_pagetable.c |  230 +++++++++++++++++++++++++++++++++++++++----
 1 files changed, 212 insertions(+), 18 deletions(-)

diff --git a/arch/x86/mm/asi_pagetable.c b/arch/x86/mm/asi_pagetable.c
index 1ff0c47..f1ee65b 100644
--- a/arch/x86/mm/asi_pagetable.c
+++ b/arch/x86/mm/asi_pagetable.c
@@ -9,6 +9,14 @@
 
 #include <asm/asi.h>
 
+static unsigned long page_directory_size[] = {
+	[PGT_LEVEL_PTE] = PAGE_SIZE,
+	[PGT_LEVEL_PMD] = PMD_SIZE,
+	[PGT_LEVEL_PUD] = PUD_SIZE,
+	[PGT_LEVEL_P4D] = P4D_SIZE,
+	[PGT_LEVEL_PGD] = PGDIR_SIZE,
+};
+
 /*
  * Structure to keep track of address ranges mapped into an ASI.
  */
@@ -17,8 +25,16 @@ struct asi_range_mapping {
 	void *ptr;			/* range start address */
 	size_t size;			/* range size */
 	enum page_table_level level;	/* mapping level */
+	int overlap;			/* overlap count */
 };
 
+#define ASI_RANGE_MAP_ADDR(r)	\
+	round_down((unsigned long)((r)->ptr), page_directory_size[(r)->level])
+
+#define ASI_RANGE_MAP_END(r)	\
+	round_up((unsigned long)((r)->ptr + (r)->size), \
+		 page_directory_size[(r)->level])
+
 /*
  * Get the pointer to the beginning of a page table directory from a page
  * table directory entry.
@@ -609,6 +625,71 @@ static int asi_copy_pgd_range(struct asi *asi,
 	return 0;
 }
 
+
+/*
+ * Map a VA range, taking into account any overlap with already mapped
+ * VA ranges. On error, return < 0. Otherwise return the number of
+ * ranges the specified range is overlapping with.
+ */
+static int asi_map_overlap(struct asi *asi, void *ptr, size_t size,
+			   enum page_table_level level)
+{
+	unsigned long map_addr, map_end;
+	unsigned long addr, end;
+	struct asi_range_mapping *range;
+	bool need_mapping;
+	int err, overlap;
+
+	addr = (unsigned long)ptr;
+	end = addr + (unsigned long)size;
+	need_mapping = true;
+	overlap = 0;
+
+	lockdep_assert_held(&asi->lock);
+	list_for_each_entry(range, &asi->mapping_list, list) {
+
+		if (range->ptr == ptr && range->size == size) {
+			/* we are mapping the same range again */
+			pr_debug("ASI %p: MAP %px/%lx/%d already mapped\n",
+				 asi, ptr, size, level);
+			return -EBUSY;
+		}
+
+		/* check overlap with mapped range */
+		map_addr = ASI_RANGE_MAP_ADDR(range);
+		map_end = ASI_RANGE_MAP_END(range);
+		if (end <= map_addr || addr >= map_end) {
+			/* no overlap, continue */
+			continue;
+		}
+
+		pr_debug("ASI %p: MAP %px/%lx/%d overlaps with %px/%lx/%d\n",
+			 asi, ptr, size, level,
+			 range->ptr, range->size, range->level);
+		range->overlap++;
+		overlap++;
+
+		/*
+		 * Check if new range is included into an existing range.
+		 * If so then the new range is already entirely mapped.
+		 */
+		if (addr >= map_addr && end <= map_end) {
+			pr_debug("ASI %p: MAP %px/%lx/%d implicitly mapped\n",
+				 asi, ptr, size, level);
+			need_mapping = false;
+		}
+	}
+
+	if (need_mapping) {
+		err = asi_copy_pgd_range(asi, asi->pgd, current->mm->pgd,
+					 addr, end, level);
+		if (err)
+			return err;
+	}
+
+	return overlap;
+}
+
 /*
  * Copy page table entries from the current page table (i.e. from the
  * kernel page table) to the specified ASI page-table. The level
@@ -619,44 +700,53 @@ int asi_map_range(struct asi *asi, void *ptr, size_t size,
 		  enum page_table_level level)
 {
 	struct asi_range_mapping *range_mapping;
+	unsigned long page_dir_size = page_directory_size[level];
 	unsigned long addr = (unsigned long)ptr;
 	unsigned long end = addr + ((unsigned long)size);
+	unsigned long map_addr, map_end;
 	unsigned long flags;
-	int err;
+	int err, overlap;
+
+	map_addr = round_down(addr, page_dir_size);
+	map_end = round_up(end, page_dir_size);
 
-	pr_debug("ASI %p: MAP %px/%lx/%d\n", asi, ptr, size, level);
+	pr_debug("ASI %p: MAP %px/%lx/%d -> %lx-%lx\n", asi, ptr, size, level,
+		 map_addr, map_end);
+	if (map_addr < addr)
+		pr_debug("ASI %p: MAP LEAK %lx-%lx\n", asi, map_addr, addr);
+	if (map_end > end)
+		pr_debug("ASI %p: MAP LEAK %lx-%lx\n", asi, end, map_end);
 
 	spin_lock_irqsave(&asi->lock, flags);
 
-	/* check if the range is already mapped */
-	range_mapping = asi_get_range_mapping(asi, ptr);
-	if (range_mapping) {
-		pr_debug("ASI %p: MAP %px/%lx/%d already mapped\n",
-			 asi, ptr, size, level);
-		err = -EBUSY;
-		goto done;
+	/*
+	 * Map the new range with taking overlap with already mapped ranges
+	 * into account.
+	 */
+	overlap = asi_map_overlap(asi, ptr, size, level);
+	if (overlap < 0) {
+		err = overlap;
+		goto error;
 	}
 
-	/* map new range */
+	/* add new range */
 	range_mapping = kmalloc(sizeof(*range_mapping), GFP_KERNEL);
 	if (!range_mapping) {
 		err = -ENOMEM;
-		goto done;
+		goto error;
 	}
 
-	err = asi_copy_pgd_range(asi, asi->pgd, current->mm->pgd,
-				 addr, end, level);
-	if (err)
-		goto done;
-
 	INIT_LIST_HEAD(&range_mapping->list);
 	range_mapping->ptr = ptr;
 	range_mapping->size = size;
 	range_mapping->level = level;
+	range_mapping->overlap = overlap;
 	list_add(&range_mapping->list, &asi->mapping_list);
-done:
 	spin_unlock_irqrestore(&asi->lock, flags);
+	return 0;
 
+error:
+	spin_unlock_irqrestore(&asi->lock, flags);
 	return err;
 }
 EXPORT_SYMBOL(asi_map_range);
@@ -776,6 +866,110 @@ static void asi_clear_pgd_range(struct asi *asi, pgd_t *pagetable,
 	} while (pgd++, addr = next, addr < end);
 }
 
+
+/*
+ * Unmap a VA range, taking into account any overlap with other mapped
+ * VA ranges. This unmaps the specified range then remap any range this
+ * range was overlapping with.
+ */
+static void asi_unmap_overlap(struct asi *asi, struct asi_range_mapping *range)
+{
+	unsigned long map_addr, map_end;
+	struct asi_range_mapping *r;
+	unsigned long addr, end;
+	unsigned long r_addr;
+	bool need_unmapping;
+	int err, overlap;
+
+	addr = (unsigned long)range->ptr;
+	end = addr + (unsigned long)range->size;
+	overlap = range->overlap;
+	need_unmapping = true;
+
+	lockdep_assert_held(&asi->lock);
+
+	/*
+	 * Adjust overlap information and check if range effectively needs
+	 * to be unmapped.
+	 */
+	list_for_each_entry(r, &asi->mapping_list, list) {
+
+		if (!overlap) {
+			/* no more overlap */
+			break;
+		}
+
+		WARN_ON(range->ptr == r->ptr && range->size == r->size);
+
+		/* check overlap with other range */
+		map_addr = ASI_RANGE_MAP_ADDR(r);
+		map_end = ASI_RANGE_MAP_END(r);
+		if (end < map_addr || addr >= map_end) {
+			/* no overlap, continue */
+			continue;
+		}
+
+		pr_debug("ASI %p: UNMAP %px/%lx/%d overlaps with %px/%lx/%d\n",
+			 asi, range->ptr, range->size, range->level,
+			 r->ptr, r->size, r->level);
+		r->overlap--;
+		overlap--;
+
+		/*
+		 * Check if range is included into a remaining mapped range.
+		 * If so then there's no need to unmap.
+		 */
+		if (map_addr <= addr && end <= map_end) {
+			pr_debug("ASI %p: UNMAP %px/%lx/%d still mapped\n",
+				 asi, range->ptr, range->size, range->level);
+			need_unmapping = false;
+		}
+	}
+
+	WARN_ON(overlap);
+
+	if (need_unmapping) {
+		asi_clear_pgd_range(asi, asi->pgd, addr, end, range->level);
+
+		/*
+		 * Remap all range we overlap with as mapping clearing
+		 * will have unmap the overlap.
+		 */
+		overlap = range->overlap;
+		list_for_each_entry(r, &asi->mapping_list, list) {
+			if (!overlap) {
+				/* no more overlap */
+				break;
+			}
+
+			/* check overlap with other range */
+			map_addr = ASI_RANGE_MAP_ADDR(r);
+			map_end = ASI_RANGE_MAP_END(r);
+			if (end < map_addr || addr >= map_end) {
+				/* no overlap, continue */
+				continue;
+			}
+			pr_debug("ASI %p: UNMAP %px/%lx/%d remaps %px/%lx/%d\n",
+				 asi, range->ptr, range->size, range->level,
+				 r->ptr, r->size, r->level);
+			overlap--;
+
+			r_addr = (unsigned long)r->ptr;
+			err = asi_copy_pgd_range(asi, asi->pgd,
+						 current->mm->pgd,
+						 r_addr, r_addr + r->size,
+						 r->level);
+			if (err) {
+				pr_debug("ASI %p: UNMAP %px/%lx/%d remaps %px/%lx/%d error %d\n",
+					 asi, range->ptr, range->size,
+					 range->level,
+					 r->ptr, r->size, r->level,
+					 err);
+			}
+		}
+	}
+}
+
 /*
  * Clear page table entries in the specified ASI page-table.
  */
@@ -797,8 +991,8 @@ void asi_unmap(struct asi *asi, void *ptr)
 	end = addr + range_mapping->size;
 	pr_debug("ASI %p: UNMAP %px/%lx/%d\n", asi, ptr,
 		 range_mapping->size, range_mapping->level);
-	asi_clear_pgd_range(asi, asi->pgd, addr, end, range_mapping->level);
 	list_del(&range_mapping->list);
+	asi_unmap_overlap(asi, range_mapping);
 	kfree(range_mapping);
 done:
 	spin_unlock_irqrestore(&asi->lock, flags);
-- 
1.7.1


  parent reply	other threads:[~2019-07-11 14:27 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-11 14:25 [RFC v2 00/27] Kernel Address Space Isolation Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 01/26] mm/x86: Introduce kernel address space isolation Alexandre Chartre
2019-07-11 21:33   ` Thomas Gleixner
2019-07-12  7:43     ` Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 02/26] mm/asi: Abort isolation on interrupt, exception and context switch Alexandre Chartre
2019-07-11 20:11   ` Andi Kleen
2019-07-11 20:17     ` Mike Rapoport
2019-07-11 20:41       ` Alexandre Chartre
2019-07-12  0:05   ` Andy Lutomirski
2019-07-12  7:50     ` Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 03/26] mm/asi: Handle page fault due to address space isolation Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 04/26] mm/asi: Functions to track buffers allocated for an ASI page-table Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 05/26] mm/asi: Add ASI page-table entry offset functions Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 06/26] mm/asi: Add ASI page-table entry allocation functions Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 07/26] mm/asi: Add ASI page-table entry set functions Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 08/26] mm/asi: Functions to populate an ASI page-table from a VA range Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 09/26] mm/asi: Helper functions to map module into ASI Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 10/26] mm/asi: Keep track of VA ranges mapped in ASI page-table Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 11/26] mm/asi: Functions to clear ASI page-table entries for a VA range Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 12/26] mm/asi: Function to copy page-table entries for percpu buffer Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 13/26] mm/asi: Add asi_remap() function Alexandre Chartre
2019-07-11 14:25 ` Alexandre Chartre [this message]
2019-07-11 14:25 ` [RFC v2 15/26] mm/asi: Initialize the ASI page-table with core mappings Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 16/26] mm/asi: Option to map current task into ASI Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 17/26] rcu: Move tree.h static forward declarations to tree.c Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 18/26] rcu: Make percpu rcu_data non-static Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 19/26] mm/asi: Add option to map RCU data Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 20/26] mm/asi: Add option to map cpu_hw_events Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 21/26] mm/asi: Make functions to read cr3/cr4 ASI aware Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 22/26] KVM: x86/asi: Introduce address_space_isolation module parameter Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 23/26] KVM: x86/asi: Introduce KVM address space isolation Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 24/26] KVM: x86/asi: Populate the KVM ASI page-table Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 25/26] KVM: x86/asi: Switch to KVM address space on entry to guest Alexandre Chartre
2019-07-11 14:25 ` [RFC v2 26/26] KVM: x86/asi: Map KVM memslots and IO buses into KVM ASI Alexandre Chartre
2019-07-11 14:40 ` [RFC v2 00/27] Kernel Address Space Isolation Alexandre Chartre
2019-07-11 22:38 ` Dave Hansen
2019-07-12  8:09   ` Alexandre Chartre
2019-07-12 13:51     ` Dave Hansen
2019-07-12 14:06       ` Alexandre Chartre
2019-07-12 15:23         ` Thomas Gleixner
2019-07-12 10:44   ` Thomas Gleixner
2019-07-12 11:56     ` Alexandre Chartre
2019-07-12 12:50       ` Peter Zijlstra
2019-07-12 13:43         ` Alexandre Chartre
2019-07-12 13:58           ` Dave Hansen
2019-07-12 14:36           ` Andy Lutomirski
2019-07-14 18:17             ` Alexander Graf
2019-07-12 13:54         ` Dave Hansen
2019-07-12 15:20           ` Peter Zijlstra
2019-07-12 15:16         ` Thomas Gleixner
2019-07-12 16:37           ` Alexandre Chartre
2019-07-12 16:45             ` Andy Lutomirski
2019-07-14 17:11               ` Mike Rapoport
2019-07-12 19:06             ` Peter Zijlstra
2019-07-14 15:06               ` Andy Lutomirski
2019-07-15 10:33                 ` Peter Zijlstra
2019-07-12 19:48             ` Thomas Gleixner
2019-07-15  8:23               ` Alexandre Chartre
2019-07-15  8:28                 ` Thomas Gleixner
2019-07-12 16:00       ` Thomas Gleixner
2019-07-12 11:44 ` Peter Zijlstra
2019-07-12 12:17   ` Alexandre Chartre
2019-07-12 12:36     ` Peter Zijlstra
2019-07-12 12:47       ` Alexandre Chartre
2019-07-12 13:07         ` Peter Zijlstra
2019-07-12 13:46           ` Alexandre Chartre
2019-07-31 16:31             ` Dario Faggioli
2019-08-22 12:31               ` Alexandre Chartre

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1562855138-19507-15-git-send-email-alexandre.chartre@oracle.com \
    --to=alexandre.chartre@oracle.com \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=graf@amazon.de \
    --cc=hpa@zytor.com \
    --cc=jan.setjeeilers@oracle.com \
    --cc=jwadams@google.com \
    --cc=konrad.wilk@oracle.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=liran.alon@oracle.com \
    --cc=luto@kernel.org \
    --cc=mingo@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rkrcmar@redhat.com \
    --cc=rppt@linux.vnet.ibm.com \
    --cc=tglx@linutronix.de \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).