1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-19 02:29:40 +00:00

ctl: fix Use-After-Free in ctl_write_buffer

The virtio_scsi device allows a guest VM to directly send SCSI commands
to the kernel driver exposed on /dev/cam/ctl. This setup makes the
vulnerability directly accessible from VMs through the pci_virtio_scsi
bhyve device.

The function ctl_write_buffer sets the CTL_FLAG_ALLOCATED flag, causing
the kern_data_ptr to be freed when the command finishes processing.
However, the buffer is still stored in lun->write_buffer, leading to a
Use-After-Free vulnerability.

Since the buffer needs to persist indefinitely, so it can be accessed by
READ BUFFER, do not set CTL_FLAG_ALLOCATED.

Reported by:	Synacktiv
Reviewed by:	Pierre Pronchery <pierre@freebsdfoundation.org>
Reviewed by:	jhb
Security:	FreeBSD-SA-24:11.ctl
Security:	CVE-2024-45063
Security:	HYP-03
Sponsored by:	Axcient
Sponsored by:	The Alpha-Omega Project
Sponsored by:	The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D46424

(cherry picked from commit 670b582db6)
This commit is contained in:
Alan Somers 2024-09-04 14:38:11 +00:00 committed by Ed Maste
parent 90af1336ed
commit 29937d7a1a
2 changed files with 19 additions and 8 deletions

View File

@ -5673,21 +5673,24 @@ ctl_write_buffer(struct ctl_scsiio *ctsio)
return (CTL_RETVAL_COMPLETE); return (CTL_RETVAL_COMPLETE);
} }
/*
* If we've got a kernel request that hasn't been malloced yet,
* malloc it and tell the caller the data buffer is here.
*/
if ((ctsio->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) {
if (lun->write_buffer == NULL) { if (lun->write_buffer == NULL) {
lun->write_buffer = malloc(CTL_WRITE_BUFFER_SIZE, lun->write_buffer = malloc(CTL_WRITE_BUFFER_SIZE,
M_CTL, M_WAITOK); M_CTL, M_WAITOK);
} }
/*
* If this kernel request hasn't started yet, initialize the data
* buffer to the correct region of the LUN's write buffer. Note that
* this doesn't set CTL_FLAG_ALLOCATED since this points into a
* persistent buffer belonging to the LUN rather than a buffer
* dedicated to this request.
*/
if (ctsio->kern_data_ptr == NULL) {
ctsio->kern_data_ptr = lun->write_buffer + buffer_offset; ctsio->kern_data_ptr = lun->write_buffer + buffer_offset;
ctsio->kern_data_len = len; ctsio->kern_data_len = len;
ctsio->kern_total_len = len; ctsio->kern_total_len = len;
ctsio->kern_rel_offset = 0; ctsio->kern_rel_offset = 0;
ctsio->kern_sg_entries = 0; ctsio->kern_sg_entries = 0;
ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
ctsio->be_move_done = ctl_config_move_done; ctsio->be_move_done = ctl_config_move_done;
ctl_datamove((union ctl_io *)ctsio); ctl_datamove((union ctl_io *)ctsio);

View File

@ -411,6 +411,14 @@ struct ctl_lun {
uint8_t pr_res_type; uint8_t pr_res_type;
int prevent_count; int prevent_count;
uint32_t *prevent; uint32_t *prevent;
/*
* The READ_BUFFER and WRITE_BUFFER commands permit access to a logical
* data buffer associated with a LUN. Accesses to the data buffer do
* not affect data stored on the storage medium. To support this,
* allocate a buffer on first use that persists until the LUN is
* destroyed.
*/
uint8_t *write_buffer; uint8_t *write_buffer;
struct ctl_devid *lun_devid; struct ctl_devid *lun_devid;
TAILQ_HEAD(tpc_lists, tpc_list) tpc_lists; TAILQ_HEAD(tpc_lists, tpc_list) tpc_lists;