Vulnerability: Use-after-free vulnerability in the vhost_net_set_backend function in drivers/vhost/net.c in the Linux kernel through 3.10.3 allows local users to cause a denial of service (OOPS and system crash) via vectors involving powering on a virtual machine.
Vulnerability ID: CVE-2013-4127
CWE: CWE-399
Severity score: 6.9
Project: https://github.com/torvalds/linux
Published: 2013-07-29
File: drivers/vhost/net.c
Commit message: vhost-net: fix use-after-free in vhost_net_flush

vhost_net_ubuf_put_and_wait has a confusing name:
it will actually also free it's argument.
Thus since commit 1280c27f8e29acf4af2da914e80ec27c3dbd5c01
    "vhost-net: flush outstanding DMAs on memory change"
vhost_net_flush tries to use the argument after passing it
to vhost_net_ubuf_put_and_wait, this results
in use after free.
To fix, don't free the argument in vhost_net_ubuf_put_and_wait,
add an new API for callers that want to free ubufs.

Acked-by: Asias He <asias@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Commit author: Michael S. Tsirkin

Vulnerable version (before fix):
 static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs) { 	kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal); 	wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount)); 	kfree(ubufs); }  static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) 	mutex_unlock(&vq->mutex);  	if (oldubufs) { 		vhost_net_ubuf_put_and_wait(oldubufs); 		mutex_lock(&vq->mutex); 		vhost_zerocopy_signal_used(n, vq); 		mutex_unlock(&vq->mutex); static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) 	rcu_assign_pointer(vq->private_data, oldsock); 	vhost_net_enable_vq(n, vq); 	if (ubufs) 		vhost_net_ubuf_put_and_wait(ubufs); err_ubufs: 	fput(sock->file); err_vq:

Fixed version (after fix):
 static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs) { 	kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal); 	wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount)); }  static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs) { 	vhost_net_ubuf_put_and_wait(ubufs); 	kfree(ubufs); }  static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) 	mutex_unlock(&vq->mutex);  	if (oldubufs) { 		vhost_net_ubuf_put_wait_and_free(oldubufs); 		mutex_lock(&vq->mutex); 		vhost_zerocopy_signal_used(n, vq); 		mutex_unlock(&vq->mutex); static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) 	rcu_assign_pointer(vq->private_data, oldsock); 	vhost_net_enable_vq(n, vq); 	if (ubufs) 		vhost_net_ubuf_put_wait_and_free(ubufs); err_ubufs: 	fput(sock->file); err_vq:

Patch:
patch: @@ -150,6 +150,11 @@ static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs)
 {
 	kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal);
 	wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount));
+}
+
+static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs)
+{
+	vhost_net_ubuf_put_and_wait(ubufs);
 	kfree(ubufs);
 }
 
@@ -948,7 +953,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
 	mutex_unlock(&vq->mutex);
 
 	if (oldubufs) {
-		vhost_net_ubuf_put_and_wait(oldubufs);
+		vhost_net_ubuf_put_wait_and_free(oldubufs);
 		mutex_lock(&vq->mutex);
 		vhost_zerocopy_signal_used(n, vq);
 		mutex_unlock(&vq->mutex);
@@ -966,7 +971,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
 	rcu_assign_pointer(vq->private_data, oldsock);
 	vhost_net_enable_vq(n, vq);
 	if (ubufs)
-		vhost_net_ubuf_put_and_wait(ubufs);
+		vhost_net_ubuf_put_wait_and_free(ubufs);
 err_ubufs:
 	fput(sock->file);
 err_vq:
path: drivers/vhost/net.c