Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39c4c5c7 authored by Vasily Averin's avatar Vasily Averin Committed by Greg Kroah-Hartman
Browse files

drm/qxl: qxl_release use after free



commit 933db73351d359f74b14f4af095808260aff11f9 upstream.

qxl_release should not be accesses after qxl_push_*_ring_release() calls:
userspace driver can process submitted command quickly, move qxl_release
into release_ring, generate interrupt and trigger garbage collector.

It can lead to crashes in qxl driver or trigger memory corruption
in some kmalloc-192 slab object

Gerd Hoffmann proposes to swap the qxl_release_fence_buffer_objects() +
qxl_push_{cursor,command}_ring_release() calls to close that race window.

cc: stable@vger.kernel.org
Fixes: f64122c1 ("drm: add new QXL driver. (v1.4)")
Signed-off-by: default avatarVasily Averin <vvs@virtuozzo.com>
Link: http://patchwork.freedesktop.org/patch/msgid/fa17b338-66ae-f299-68fe-8d32419d9071@virtuozzo.com


Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
[backported to v4.9-stable]
Signed-off-by: default avatarVasily Averin <vvs@virtuozzo.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6affa87d
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -529,8 +529,8 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
	/* no need to add a release to the fence for this surface bo,
	   since it is only released when we ask to destroy the surface
	   and it would never signal otherwise */
	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);

	surf->hw_surf_alloc = true;
	spin_lock(&qdev->surf_id_idr_lock);
@@ -572,9 +572,8 @@ int qxl_hw_surface_dealloc(struct qxl_device *qdev,
	cmd->surface_id = id;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);

	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);

	return 0;
}
+4 −4
Original line number Diff line number Diff line
@@ -292,8 +292,8 @@ qxl_hide_cursor(struct qxl_device *qdev)
	cmd->type = QXL_CURSOR_HIDE;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	return 0;
}

@@ -333,8 +333,8 @@ static int qxl_crtc_apply_cursor(struct drm_crtc *crtc)
	cmd->u.set.visible = 1;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);

	return ret;

@@ -436,8 +436,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
	cmd->u.set.visible = 1;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);

	/* finish with the userspace bo */
	ret = qxl_bo_reserve(user_bo, false);
@@ -497,8 +497,8 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
	cmd->u.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);

	return 0;
}
+4 −4
Original line number Diff line number Diff line
@@ -241,8 +241,8 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
		qxl_bo_physical_address(qdev, dimage->bo, 0);
	qxl_release_unmap(qdev, release, &drawable->release_info);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);

out_free_palette:
	if (palette_bo)
@@ -382,8 +382,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
	}
	qxl_bo_kunmap(clips_bo);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);

out_release_backoff:
	if (ret)
@@ -433,8 +433,8 @@ void qxl_draw_copyarea(struct qxl_device *qdev,
	drawable->u.copy_bits.src_pos.y = sy;
	qxl_release_unmap(qdev, release, &drawable->release_info);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);

out_free_release:
	if (ret)
@@ -477,8 +477,8 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)

	qxl_release_unmap(qdev, release, &drawable->release_info);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_fence_buffer_objects(release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);

out_free_release:
	if (ret)
+1 −4
Original line number Diff line number Diff line
@@ -256,11 +256,8 @@ static int qxl_process_single_command(struct qxl_device *qdev,
			apply_surf_reloc(qdev, &reloc_info[i]);
	}

	ret = qxl_push_command_ring_release(qdev, release, cmd->type, true);
	if (ret)
		qxl_release_backoff_reserve_list(release);
	else
	qxl_release_fence_buffer_objects(release);
	ret = qxl_push_command_ring_release(qdev, release, cmd->type, true);

out_free_bos:
out_free_release: