linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andy Lutomirski <luto@amacapital.net>
To: linux-kernel@vger.kernel.org
Cc: Andy Lutomirski <luto@amacapital.net>, stable@vger.kernel.org
Subject: [PATCH] x86_64, vdso: Fix the vdso address randomization algorithm
Date: Fri, 19 Dec 2014 16:29:15 -0800	[thread overview]
Message-ID: <ad42f6d17cbcaef5ff7c9d5741669ab64603c29c.1419035095.git.luto@amacapital.net> (raw)
In-Reply-To: <CALCETrWtAM5VGokNkw0tHLXRnRsORND7YY6ehYArtvEtt19COA@mail.gmail.com>

The theory behind vdso randomization is that it's mapped at a random
offset above the top of the stack.  To avoid wasting a page of
memory for an extra page table, the vdso isn't supposed to extend
past the lowest PMD into which it can fit.  Other than that, the
address should be a uniformly distributed address that meets all of
the alignment requirements.

The current algorithm is buggy: the vdso has about a 50% probability
of being at the very end of a PMD.  The current algorithm also has a
decent chance of failing outright due to incorrect handling of the
case where the top of the stack is near the top of its PMD.

This fixes the implementation.  The paxtest estimate of vdso
"randomisation" improves from 11 bits to 18 bits.  (Disclaimer: I
don't know what the paxtest code is actually calculating.)

It's worth noting that this algorithm is inherently biased: the vdso
is more likely to end up near the end of its PMD than near the
beginning.  Ideally we would either nix the PMD sharing requirement
or jointly randomize the vdso and the stack to eliminate the bias.

In the mean time, this is a considerable improvement with basically
no risk of incompatibility issues, since the allowed outputs of the
algorithm are unchanged.

As an easy test, doing this:

for i in `seq 10000`
  do grep -P vdso /proc/self/maps |cut -d- -f1
done |sort |uniq -d

used to produce lots of output (1445 lines on my most recent run).
A tiny subset looks like this:

7fffdfffe000
7fffe01fe000
7fffe05fe000
7fffe07fe000
7fffe09fe000
7fffe0bfe000
7fffe0dfe000

Note the suspicious fe000 endings.  With the fix, I get a much more
palatable 76 repeated addresses.

Cc: stable@vger.kernel.org
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
---

No particular rush here.  This should probably go in some time after -rc1,
unless someone feels that it's more urgent than that.

arch/x86/vdso/vma.c | 47 +++++++++++++++++++++++++++++++----------------
 1 file changed, 31 insertions(+), 16 deletions(-)

diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 009495b9ab4b..62be12eb3f16 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -41,12 +41,17 @@ void __init init_vdso_image(const struct vdso_image *image)
 
 struct linux_binprm;
 
-/* Put the vdso above the (randomized) stack with another randomized offset.
-   This way there is no hole in the middle of address space.
-   To save memory make sure it is still in the same PTE as the stack top.
-   This doesn't give that many random bits.
-
-   Only used for the 64-bit and x32 vdsos. */
+/*
+ * Put the vdso above the (randomized) stack with another randomized
+ * offset.  This way there is no hole in the middle of address space.
+ * To save memory make sure it is still in the same PTE as the stack
+ * top.  This doesn't give that many random bits.
+ *
+ * Note that this algorithm is imperfect: the distribution of the vdso
+ * start address within a PMD is biased toward the end.
+ *
+ * Only used for the 64-bit and x32 vdsos.
+ */
 static unsigned long vdso_addr(unsigned long start, unsigned len)
 {
 #ifdef CONFIG_X86_32
@@ -54,22 +59,32 @@ static unsigned long vdso_addr(unsigned long start, unsigned len)
 #else
 	unsigned long addr, end;
 	unsigned offset;
-	end = (start + PMD_SIZE - 1) & PMD_MASK;
+
+	/*
+	 * Round up the start address.  It can start out unaligned as a result
+	 * of stack start randomization.
+	 */
+	start = PAGE_ALIGN(start);
+
+	/* Round the lowest possible end address up to a PMD boundary. */
+	end = (start + len + PMD_SIZE - 1) & PMD_MASK;
 	if (end >= TASK_SIZE_MAX)
 		end = TASK_SIZE_MAX;
 	end -= len;
-	/* This loses some more bits than a modulo, but is cheaper */
-	offset = get_random_int() & (PTRS_PER_PTE - 1);
-	addr = start + (offset << PAGE_SHIFT);
-	if (addr >= end)
-		addr = end;
+
+	if (end > start) {
+		offset = get_random_int() % ((end - start) >> PAGE_SHIFT);
+		addr = start + (offset << PAGE_SHIFT);
+		if (WARN_ON_ONCE(addr > end))
+			addr = end;
+	} else {
+		addr = start;
+	}
 
 	/*
-	 * page-align it here so that get_unmapped_area doesn't
-	 * align it wrongfully again to the next page. addr can come in 4K
-	 * unaligned here as a result of stack start randomization.
+	 * Forcibly align the final address in case we have a hardware
+	 * issue that requires alignment for performance reasons.
 	 */
-	addr = PAGE_ALIGN(addr);
 	addr = align_vdso_addr(addr);
 
 	return addr;
-- 
2.1.0


  reply	other threads:[~2014-12-20  0:29 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <5489E6D2.2060200@upv.es>
2014-12-11 20:12 ` [PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack Hector Marco
2014-12-11 22:11   ` Kees Cook
2014-12-12 16:32     ` Hector Marco
2014-12-12 17:17       ` Andy Lutomirski
2014-12-19 22:04         ` Hector Marco
2014-12-19 22:11           ` Andy Lutomirski
2014-12-19 22:19             ` Cyrill Gorcunov
2014-12-19 23:53             ` Andy Lutomirski
2014-12-20  0:29               ` Andy Lutomirski [this message]
2014-12-20 17:40               ` [PATCH v2] x86_64, vdso: Fix the vdso address randomization algorithm Andy Lutomirski
2014-12-20 21:13                 ` Kees Cook
2014-12-22 17:36               ` [PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack Hector Marco Gisbert
2014-12-22 17:56                 ` Andy Lutomirski
2014-12-22 19:49                   ` Jiri Kosina
2014-12-22 20:00                     ` Andy Lutomirski
2014-12-22 20:03                       ` Jiri Kosina
2014-12-22 20:13                         ` Andy Lutomirski
2014-12-22 23:23                   ` Hector Marco Gisbert
2014-12-22 23:38                     ` Andy Lutomirski
     [not found]                       ` <CAH4rwTKeN0P84FJnocoKV4t9rc2Ox_EYc+LEibD+Y83n7C8aVA@mail.gmail.com>
2014-12-23  8:15                         ` Andy Lutomirski
2014-12-23 20:06                           ` Hector Marco Gisbert
2014-12-23 20:53                             ` Andy Lutomirski
2015-01-07 17:26     ` Hector Marco Gisbert

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=ad42f6d17cbcaef5ff7c9d5741669ab64603c29c.1419035095.git.luto@amacapital.net \
    --to=luto@amacapital.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.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).