Vulnerability: arch/x86/kvm/emulate.c in the Linux kernel through 4.9.3 allows local users to obtain sensitive information from kernel memory or cause a denial of service (use-after-free) via a crafted application that leverages instruction emulation for fxrstor, fxsave, sgdt, and sidt.
Vulnerability ID: CVE-2017-2584
CWE: CWE-416
Severity score: 4.9
Project: https://github.com/torvalds/linux
Published: 2017-01-15
File: arch/x86/kvm/emulate.c
Commit message: KVM: x86: Introduce segmented_write_std

Introduces segemented_write_std.

Switches from emulated reads/writes to standard read/writes in fxsave,
fxrstor, sgdt, and sidt.  This fixes CVE-2017-2584, a longstanding
kernel memory leak.

Since commit 283c95d0e389 ("KVM: x86: emulate FXSAVE and FXRSTOR",
2016-11-09), which is luckily not yet in any final release, this would
also be an exploitable kernel memory *write*!

Reported-by: Dmitry Vyukov <dvyukov@google.com>
Cc: stable@vger.kernel.org
Fixes: 96051572c819194c37a8367624b285be10297eca
Fixes: 283c95d0e3891b64087706b344a4b545d04a6e62
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Steve Rutherford <srutherford@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Commit author: Steve Rutherford

Vulnerable version (before fix):
 static int segmented_read_std(struct x86_emulate_ctxt *ctxt, 	return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception); }  /*  * Prefetch the remaining bytes of the instruction without crossing page  * boundary if they are not in fetch_cache yet. static int emulate_store_desc_ptr(struct x86_emulate_ctxt *ctxt, 	} 	/* Disable writeback. */ 	ctxt->dst.type = OP_NONE; 	return segmented_write(ctxt, ctxt->dst.addr.mem, 			       &desc_ptr, 2 + ctxt->op_bytes); }  static int em_sgdt(struct x86_emulate_ctxt *ctxt) static int em_fxsave(struct x86_emulate_ctxt *ctxt) 	else 		size = offsetof(struct fxregs_state, xmm_space[0]);  	return segmented_write(ctxt, ctxt->memop.addr.mem, &fx_state, size); }  static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt, static int em_fxrstor(struct x86_emulate_ctxt *ctxt) 	if (rc != X86EMUL_CONTINUE) 		return rc;  	rc = segmented_read(ctxt, ctxt->memop.addr.mem, &fx_state, 512); 	if (rc != X86EMUL_CONTINUE) 		return rc; 

Fixed version (after fix):
 static int segmented_read_std(struct x86_emulate_ctxt *ctxt, 	return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception); }  static int segmented_write_std(struct x86_emulate_ctxt *ctxt, 			       struct segmented_address addr, 			       void *data, 			       unsigned int size) { 	int rc; 	ulong linear;  	rc = linearize(ctxt, addr, size, true, &linear); 	if (rc != X86EMUL_CONTINUE) 		return rc; 	return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception); }  /*  * Prefetch the remaining bytes of the instruction without crossing page  * boundary if they are not in fetch_cache yet. static int emulate_store_desc_ptr(struct x86_emulate_ctxt *ctxt, 	} 	/* Disable writeback. */ 	ctxt->dst.type = OP_NONE; 	return segmented_write_std(ctxt, ctxt->dst.addr.mem, 				   &desc_ptr, 2 + ctxt->op_bytes); }  static int em_sgdt(struct x86_emulate_ctxt *ctxt) static int em_fxsave(struct x86_emulate_ctxt *ctxt) 	else 		size = offsetof(struct fxregs_state, xmm_space[0]);  	return segmented_write_std(ctxt, ctxt->memop.addr.mem, &fx_state, size); }  static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt, static int em_fxrstor(struct x86_emulate_ctxt *ctxt) 	if (rc != X86EMUL_CONTINUE) 		return rc;  	rc = segmented_read_std(ctxt, ctxt->memop.addr.mem, &fx_state, 512); 	if (rc != X86EMUL_CONTINUE) 		return rc; 

Patch:
patch: @@ -818,6 +818,20 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt,
 	return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception);
 }
 
+static int segmented_write_std(struct x86_emulate_ctxt *ctxt,
+			       struct segmented_address addr,
+			       void *data,
+			       unsigned int size)
+{
+	int rc;
+	ulong linear;
+
+	rc = linearize(ctxt, addr, size, true, &linear);
+	if (rc != X86EMUL_CONTINUE)
+		return rc;
+	return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception);
+}
+
 /*
  * Prefetch the remaining bytes of the instruction without crossing page
  * boundary if they are not in fetch_cache yet.
@@ -3685,8 +3699,8 @@ static int emulate_store_desc_ptr(struct x86_emulate_ctxt *ctxt,
 	}
 	/* Disable writeback. */
 	ctxt->dst.type = OP_NONE;
-	return segmented_write(ctxt, ctxt->dst.addr.mem,
-			       &desc_ptr, 2 + ctxt->op_bytes);
+	return segmented_write_std(ctxt, ctxt->dst.addr.mem,
+				   &desc_ptr, 2 + ctxt->op_bytes);
 }
 
 static int em_sgdt(struct x86_emulate_ctxt *ctxt)
@@ -3932,7 +3946,7 @@ static int em_fxsave(struct x86_emulate_ctxt *ctxt)
 	else
 		size = offsetof(struct fxregs_state, xmm_space[0]);
 
-	return segmented_write(ctxt, ctxt->memop.addr.mem, &fx_state, size);
+	return segmented_write_std(ctxt, ctxt->memop.addr.mem, &fx_state, size);
 }
 
 static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt,
@@ -3974,7 +3988,7 @@ static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
 	if (rc != X86EMUL_CONTINUE)
 		return rc;
 
-	rc = segmented_read(ctxt, ctxt->memop.addr.mem, &fx_state, 512);
+	rc = segmented_read_std(ctxt, ctxt->memop.addr.mem, &fx_state, 512);
 	if (rc != X86EMUL_CONTINUE)
 		return rc;
path: arch/x86/kvm/emulate.c