Prevent potential double frees in sg driver

sg_ioctl could be spammed by requests, leading to a double free in
__free_pages. This protects the entry points of sg_ioctl where the
memory could be corrupted by a double call to __free_pages if multiple
requests are happening concurrently.

Bug:35644812

Change-Id: Ie13f65beb6974430f90292e2742841b26aecb8b1
Signed-off-by: Robb Glasser <rglasser@google.com>
Git-commit: 22d8e80738b5ce8784d59b48b0b051a520da4bec
Git-repo: https://source.codeaurora.org/quic/la/kernel/msm-3.10/commit
Signed-off-by: Sayali Lokhande <sayalil@codeaurora.org>
This commit is contained in:
Robb Glasser 2017-03-24 16:23:37 -07:00 committed by Gerrit - the friendly Code Review server
parent fee040aaa1
commit d31d4288a7
1 changed files with 22 additions and 14 deletions

View File

@ -170,6 +170,7 @@ typedef struct sg_fd { /* holds the state of a file descriptor */
typedef struct sg_device { /* holds the state of each scsi generic device */
struct scsi_device *device;
struct mutex open_rel_lock;
wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
int sg_tablesize; /* adapter's max scatter-gather table size */
u32 index; /* device index number */
@ -486,7 +487,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
old_hdr->result = EIO;
break;
case DID_ERROR:
old_hdr->result = (srp->sense_b[0] == 0 &&
old_hdr->result = (srp->sense_b[0] == 0 &&
hp->masked_status == GOOD) ? 0 : EIO;
break;
default:
@ -832,8 +833,10 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR))
return -EFAULT;
mutex_lock(&sfp->parentdp->open_rel_lock);
result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
1, read_only, 1, &srp);
mutex_unlock(&sfp->parentdp->open_rel_lock);
if (result < 0)
return result;
result = wait_event_interruptible(sfp->read_wait,
@ -873,8 +876,10 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
sfp->low_dma = 1;
if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {
val = (int) sfp->reserve.bufflen;
mutex_lock(&sfp->parentdp->open_rel_lock);
sg_remove_scat(&sfp->reserve);
sg_build_reserve(sfp, val);
mutex_unlock(&sfp->parentdp->open_rel_lock);
}
} else {
if (sdp->detached)
@ -942,15 +947,17 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
result = get_user(val, ip);
if (result)
return result;
if (val < 0)
return -EINVAL;
if (val < 0)
return -EINVAL;
val = min_t(int, val,
queue_max_sectors(sdp->device->request_queue) * 512);
if (val != sfp->reserve.bufflen) {
if (sg_res_in_use(sfp) || sfp->mmap_called)
return -EBUSY;
mutex_lock(&sfp->parentdp->open_rel_lock);
sg_remove_scat(&sfp->reserve);
sg_build_reserve(sfp, val);
mutex_unlock(&sfp->parentdp->open_rel_lock);
}
return 0;
case SG_GET_RESERVED_SIZE:
@ -1003,8 +1010,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
if (srp) {
rinfo[val].req_state = srp->done + 1;
rinfo[val].problem =
srp->header.masked_status &
srp->header.host_status &
srp->header.masked_status &
srp->header.host_status &
srp->header.driver_status;
if (srp->done)
rinfo[val].duration =
@ -1025,7 +1032,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
}
}
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
result = __copy_to_user(p, rinfo,
result = __copy_to_user(p, rinfo,
SZ_SG_REQ_INFO * SG_MAX_QUEUE);
result = result ? -EFAULT : 0;
kfree(rinfo);
@ -1127,14 +1134,14 @@ static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned lon
return -ENXIO;
sdev = sdp->device;
if (sdev->host->hostt->compat_ioctl) {
if (sdev->host->hostt->compat_ioctl) {
int ret;
ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
return ret;
}
return -ENOIOCTLCMD;
}
#endif
@ -1415,6 +1422,7 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp)
disk->first_minor = k;
sdp->disk = disk;
sdp->device = scsidp;
mutex_init(&sdp->open_rel_lock);
INIT_LIST_HEAD(&sdp->sfds);
init_waitqueue_head(&sdp->o_excl_wait);
sdp->sg_tablesize = queue_max_segments(q);
@ -1594,7 +1602,7 @@ init_sg(void)
else
def_reserved_size = sg_big_buff;
rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
SG_MAX_DEVS, "sg");
if (rc)
return rc;
@ -2231,7 +2239,7 @@ static const struct file_operations adio_fops = {
};
static int sg_proc_single_open_dressz(struct inode *inode, struct file *file);
static ssize_t sg_proc_write_dressz(struct file *filp,
static ssize_t sg_proc_write_dressz(struct file *filp,
const char __user *buffer, size_t count, loff_t *off);
static const struct file_operations dressz_fops = {
.owner = THIS_MODULE,
@ -2371,7 +2379,7 @@ static int sg_proc_single_open_adio(struct inode *inode, struct file *file)
return single_open(file, sg_proc_seq_show_int, &sg_allow_dio);
}
static ssize_t
static ssize_t
sg_proc_write_adio(struct file *filp, const char __user *buffer,
size_t count, loff_t *off)
{
@ -2392,7 +2400,7 @@ static int sg_proc_single_open_dressz(struct inode *inode, struct file *file)
return single_open(file, sg_proc_seq_show_int, &sg_big_buff);
}
static ssize_t
static ssize_t
sg_proc_write_dressz(struct file *filp, const char __user *buffer,
size_t count, loff_t *off)
{
@ -2549,7 +2557,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
hp = &srp->header;
new_interface = (hp->interface_id == '\0') ? 0 : 1;
if (srp->res_used) {
if (new_interface &&
if (new_interface &&
(SG_FLAG_MMAP_IO & hp->flags))
cp = " mmap>> ";
else
@ -2563,7 +2571,7 @@ static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
seq_printf(s, cp);
blen = srp->data.bufflen;
usg = srp->data.k_use_sg;
seq_printf(s, srp->done ?
seq_printf(s, srp->done ?
((1 == srp->done) ? "rcv:" : "fin:")
: "act:");
seq_printf(s, " id=%d blen=%d",