diff options
author | John Baldwin <jhb@FreeBSD.org> | 2020-04-21 23:38:54 +0000 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2020-04-21 23:38:54 +0000 |
commit | 81b62a766393215461b8439ff96ba1de57a28e62 (patch) | |
tree | 934a28ea2c156f895c7400c0a85a7e7a79288ad4 /sys | |
parent | 24c2e17d2b5ac2c4450732e0f14f5de84025b981 (diff) | |
download | src-test2-81b62a766393215461b8439ff96ba1de57a28e62.tar.gz src-test2-81b62a766393215461b8439ff96ba1de57a28e62.zip |
Don't pass a user buffer pointer as the data pointer in a CCB.
Allocate a temporary buffer in the kernel to serve as the CCB data
pointer for a pass-through transaction and use copyin/copyout to
shuffle the data to/from the user buffer.
Reviewed by: scottl, brooks
Obtained from: CheriBSD
MFC after: 2 weeks
Sponsored by: DARPA
Differential Revision: https://reviews.freebsd.org/D24489
Notes
Notes:
svn path=/head/; revision=360179
Diffstat (limited to 'sys')
-rw-r--r-- | sys/cam/scsi/scsi_sg.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/sys/cam/scsi/scsi_sg.c b/sys/cam/scsi/scsi_sg.c index 96a66cab1a6a..ebf40853649a 100644 --- a/sys/cam/scsi/scsi_sg.c +++ b/sys/cam/scsi/scsi_sg.c @@ -508,6 +508,7 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) struct cam_periph *periph; struct sg_softc *softc; struct sg_io_hdr *req; + void *data_ptr; int dir, error; periph = (struct cam_periph *)dev->si_drv1; @@ -552,12 +553,20 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) break; } + if (req->dxfer_len > MAXPHYS) { + error = EINVAL; + break; + } + + data_ptr = malloc(req->dxfer_len, M_DEVBUF, M_WAITOK); + ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; error = copyin(req->cmdp, &csio->cdb_io.cdb_bytes, req->cmd_len); if (error) { + free(data_ptr, M_DEVBUF); xpt_release_ccb(ccb); break; } @@ -570,7 +579,7 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) dir = CAM_DIR_IN; break; case SG_DXFER_TO_FROM_DEV: - dir = CAM_DIR_IN | CAM_DIR_OUT; + dir = CAM_DIR_BOTH; break; case SG_DXFER_NONE: default: @@ -578,12 +587,21 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) break; } + if (dir == CAM_DIR_IN || dir == CAM_DIR_BOTH) { + error = copyin(req->dxferp, data_ptr, req->dxfer_len); + if (error) { + free(data_ptr, M_DEVBUF); + xpt_release_ccb(ccb); + break; + } + } + cam_fill_csio(csio, /*retries*/1, /*cbfcnp*/NULL, dir|CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, - req->dxferp, + data_ptr, req->dxfer_len, req->mx_sb_len, req->cmd_len, @@ -593,6 +611,7 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) if (error) { req->host_status = DID_ERROR; req->driver_status = DRIVER_INVALID; + free(data_ptr, M_DEVBUF); xpt_release_ccb(ccb); break; } @@ -611,6 +630,10 @@ sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) req->sb_len_wr); } + if ((dir == CAM_DIR_OUT || dir == CAM_DIR_BOTH) && error == 0) + error = copyout(data_ptr, req->dxferp, req->dxfer_len); + + free(data_ptr, M_DEVBUF); xpt_release_ccb(ccb); break; |