qxl_process_single_command() copies relocation entries from userspace and
passes them to apply_reloc() / apply_surf_reloc() without checking
whether dst_offset falls within the destination buffer object.
apply_reloc() writes 8 bytes and apply_surf_reloc() writes 4 bytes at
the byte offset named by the relocation. A userspace-chosen offset that
exceeds the BO's allocation leads to an out-of-bounds write into
adjacent slab memory.
When the destination is the command/release BO itself (dst_handle == 0),
the write lands relative to the release's own release_info header. An
offset of zero overwrites the release_info.id, which the garbage
collector later uses as a release index, leading to a use-after-free of
a release chosen by userspace. Both paths are reachable from any render
client (DRM_AUTH).
Add two bounds checks:
- When writing into the release's own BO (dst_handle == 0), require
that dst_offset points past the release_info header and stays within
the command data area.
- For every relocation, require that the final byte offset plus the
write width does not exceed the destination BO size.
Fixes: f64122c1f6ad ("drm: add qxl driver.")
Cc: [email protected]
Signed-off-by: Aldo Ariel Panzardo <[email protected]>
---
drivers/gpu/drm/qxl/qxl_ioctl.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index 591b026ce..b62f32b6d 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -227,10 +227,27 @@ static int qxl_process_single_command(struct qxl_device
*qdev,
goto out_free_bos;
reloc_info[i].dst_offset = reloc.dst_offset;
} else {
+ if (reloc.dst_offset < sizeof(union qxl_release_info) ||
+ reloc.dst_offset >= sizeof(union qxl_release_info) +
+ cmd->command_size) {
+ ret = -EINVAL;
+ goto out_free_bos;
+ }
reloc_info[i].dst_bo = cmd_bo;
reloc_info[i].dst_offset = reloc.dst_offset +
release->release_offset;
}
+ {
+ size_t write_size = reloc.reloc_type ==
QXL_RELOC_TYPE_BO ?
+ sizeof(uint64_t) : sizeof(uint32_t);
+
+ if (reloc_info[i].dst_offset + write_size >
+ reloc_info[i].dst_bo->tbo.base.size) {
+ ret = -EINVAL;
+ goto out_free_bos;
+ }
+ }
+
/* reserve and validate the reloc dst bo */
if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle) {
ret = qxlhw_handle_to_bo(file_priv, reloc.src_handle,
release,
--
2.43.0