Skip to content

Commit 26b5b87

Browse files
thejhmartinkpetersen
authored andcommitted
scsi: sg: mitigate read/write abuse
As Al Viro noted in commit 128394e ("sg_write()/bsg_write() is not fit to be called under KERNEL_DS"), sg improperly accesses userspace memory outside the provided buffer, permitting kernel memory corruption via splice(). But it doesn't just do it on ->write(), also on ->read(). As a band-aid, make sure that the ->read() and ->write() handlers can not be called in weird contexts (kernel context or credentials different from file opener), like for ib_safe_file_access(). If someone needs to use these interfaces from different security contexts, a new interface should be written that goes through the ->ioctl() handler. I've mostly copypasted ib_safe_file_access() over as sg_safe_file_access() because I couldn't find a good common header - please tell me if you know a better way. [mkp: s/_safe_/_check_/] Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: <[email protected]> Signed-off-by: Jann Horn <[email protected]> Acked-by: Douglas Gilbert <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 59b433c commit 26b5b87

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

drivers/scsi/sg.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static int sg_version_num = 30536; /* 2 digits for each component */
5151
#include <linux/atomic.h>
5252
#include <linux/ratelimit.h>
5353
#include <linux/uio.h>
54+
#include <linux/cred.h> /* for sg_check_file_access() */
5455

5556
#include "scsi.h"
5657
#include <scsi/scsi_dbg.h>
@@ -209,6 +210,33 @@ static void sg_device_destroy(struct kref *kref);
209210
sdev_prefix_printk(prefix, (sdp)->device, \
210211
(sdp)->disk->disk_name, fmt, ##a)
211212

213+
/*
214+
* The SCSI interfaces that use read() and write() as an asynchronous variant of
215+
* ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways
216+
* to trigger read() and write() calls from various contexts with elevated
217+
* privileges. This can lead to kernel memory corruption (e.g. if these
218+
* interfaces are called through splice()) and privilege escalation inside
219+
* userspace (e.g. if a process with access to such a device passes a file
220+
* descriptor to a SUID binary as stdin/stdout/stderr).
221+
*
222+
* This function provides protection for the legacy API by restricting the
223+
* calling context.
224+
*/
225+
static int sg_check_file_access(struct file *filp, const char *caller)
226+
{
227+
if (filp->f_cred != current_real_cred()) {
228+
pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
229+
caller, task_tgid_vnr(current), current->comm);
230+
return -EPERM;
231+
}
232+
if (uaccess_kernel()) {
233+
pr_err_once("%s: process %d (%s) called from kernel context, this is not allowed.\n",
234+
caller, task_tgid_vnr(current), current->comm);
235+
return -EACCES;
236+
}
237+
return 0;
238+
}
239+
212240
static int sg_allow_access(struct file *filp, unsigned char *cmd)
213241
{
214242
struct sg_fd *sfp = filp->private_data;
@@ -393,6 +421,14 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
393421
struct sg_header *old_hdr = NULL;
394422
int retval = 0;
395423

424+
/*
425+
* This could cause a response to be stranded. Close the associated
426+
* file descriptor to free up any resources being held.
427+
*/
428+
retval = sg_check_file_access(filp, __func__);
429+
if (retval)
430+
return retval;
431+
396432
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
397433
return -ENXIO;
398434
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
@@ -580,9 +616,11 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
580616
struct sg_header old_hdr;
581617
sg_io_hdr_t *hp;
582618
unsigned char cmnd[SG_MAX_CDB_SIZE];
619+
int retval;
583620

584-
if (unlikely(uaccess_kernel()))
585-
return -EINVAL;
621+
retval = sg_check_file_access(filp, __func__);
622+
if (retval)
623+
return retval;
586624

587625
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
588626
return -ENXIO;

0 commit comments

Comments
 (0)