Skip to content

Commit

Permalink
ctl: fix Use-After-Free in ctl_write_buffer
Browse files Browse the repository at this point in the history
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
  • Loading branch information
asomers authored and emaste committed Sep 4, 2024
1 parent 5c9308a commit 670b582
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 8 deletions.
19 changes: 11 additions & 8 deletions sys/cam/ctl/ctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5625,21 +5625,24 @@ ctl_write_buffer(struct ctl_scsiio *ctsio)
return (CTL_RETVAL_COMPLETE);
}

if (lun->write_buffer == NULL) {
lun->write_buffer = malloc(CTL_WRITE_BUFFER_SIZE,
M_CTL, M_WAITOK);
}

/*
* 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 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->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) {
if (lun->write_buffer == NULL) {
lun->write_buffer = malloc(CTL_WRITE_BUFFER_SIZE,
M_CTL, M_WAITOK);
}
if (ctsio->kern_data_ptr == NULL) {
ctsio->kern_data_ptr = lun->write_buffer + buffer_offset;
ctsio->kern_data_len = len;
ctsio->kern_total_len = len;
ctsio->kern_rel_offset = 0;
ctsio->kern_sg_entries = 0;
ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
ctsio->be_move_done = ctl_config_move_done;
ctl_datamove((union ctl_io *)ctsio);

Expand Down
8 changes: 8 additions & 0 deletions sys/cam/ctl/ctl_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,14 @@ struct ctl_lun {
uint8_t pr_res_type;
int prevent_count;
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;
struct ctl_devid *lun_devid;
TAILQ_HEAD(tpc_lists, tpc_list) tpc_lists;
Expand Down

0 comments on commit 670b582

Please sign in to comment.