scsi: dpt_i2o: Remove broken pass-through ioctl (I2OUSERCMD)
adpt_i2o_passthru() takes a user-provided message and passes it through to the hardware with appropriate translation of addresses and message IDs. It has a number of bugs: - When a message requires scatter/gather, it doesn't verify that the offset to the scatter/gather list is less than the message size. - When a message requires scatter/gather, it overwrites the DMA addresses with the user-space virtual addresses before unmapping the DMA buffers. - It reads the message from user memory multiple times. This allows user-space to change the message and bypass validation. - It assumes that the message is at least 4 words long, but doesn't check that. I tried fixing these, but even the maintainer of the corresponding user-space in Debian doesn't have the hardware any more. Instead, remove the pass-through ioctl (I2OUSRCMD) and supporting code. There is no corresponding upstream commit, because this driver was removed upstream. Fixes:1da177e4c3
("Linux-2.6.12-rc2") Fixes:67af2b060e
("[SCSI] dpt_i2o: move from virt_to_bus/bus_to_virt ...") Signed-off-by: Ben Hutchings <benh@debian.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
56a4a9dc5e
commit
e2897f133a
@ -585,51 +585,6 @@ static int adpt_show_info(struct seq_file *m, struct Scsi_Host *host)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn a pointer to ioctl reply data into an u32 'context'
|
||||
*/
|
||||
static u32 adpt_ioctl_to_context(adpt_hba * pHba, void *reply)
|
||||
{
|
||||
#if BITS_PER_LONG == 32
|
||||
return (u32)(unsigned long)reply;
|
||||
#else
|
||||
ulong flags = 0;
|
||||
u32 nr, i;
|
||||
|
||||
spin_lock_irqsave(pHba->host->host_lock, flags);
|
||||
nr = ARRAY_SIZE(pHba->ioctl_reply_context);
|
||||
for (i = 0; i < nr; i++) {
|
||||
if (pHba->ioctl_reply_context[i] == NULL) {
|
||||
pHba->ioctl_reply_context[i] = reply;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(pHba->host->host_lock, flags);
|
||||
if (i >= nr) {
|
||||
printk(KERN_WARNING"%s: Too many outstanding "
|
||||
"ioctl commands\n", pHba->name);
|
||||
return (u32)-1;
|
||||
}
|
||||
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Go from an u32 'context' to a pointer to ioctl reply data.
|
||||
*/
|
||||
static void *adpt_ioctl_from_context(adpt_hba *pHba, u32 context)
|
||||
{
|
||||
#if BITS_PER_LONG == 32
|
||||
return (void *)(unsigned long)context;
|
||||
#else
|
||||
void *p = pHba->ioctl_reply_context[context];
|
||||
pHba->ioctl_reply_context[context] = NULL;
|
||||
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*===========================================================================
|
||||
* Error Handling routines
|
||||
*===========================================================================
|
||||
@ -1652,208 +1607,6 @@ static int adpt_close(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
|
||||
{
|
||||
u32 msg[MAX_MESSAGE_SIZE];
|
||||
u32* reply = NULL;
|
||||
u32 size = 0;
|
||||
u32 reply_size = 0;
|
||||
u32 __user *user_msg = arg;
|
||||
u32 __user * user_reply = NULL;
|
||||
void **sg_list = NULL;
|
||||
u32 sg_offset = 0;
|
||||
u32 sg_count = 0;
|
||||
int sg_index = 0;
|
||||
u32 i = 0;
|
||||
u32 rcode = 0;
|
||||
void *p = NULL;
|
||||
dma_addr_t addr;
|
||||
ulong flags = 0;
|
||||
|
||||
memset(&msg, 0, MAX_MESSAGE_SIZE*4);
|
||||
// get user msg size in u32s
|
||||
if(get_user(size, &user_msg[0])){
|
||||
return -EFAULT;
|
||||
}
|
||||
size = size>>16;
|
||||
|
||||
user_reply = &user_msg[size];
|
||||
if(size > MAX_MESSAGE_SIZE){
|
||||
return -EFAULT;
|
||||
}
|
||||
size *= 4; // Convert to bytes
|
||||
|
||||
/* Copy in the user's I2O command */
|
||||
if(copy_from_user(msg, user_msg, size)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
get_user(reply_size, &user_reply[0]);
|
||||
reply_size = reply_size>>16;
|
||||
if(reply_size > REPLY_FRAME_SIZE){
|
||||
reply_size = REPLY_FRAME_SIZE;
|
||||
}
|
||||
reply_size *= 4;
|
||||
reply = kzalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL);
|
||||
if(reply == NULL) {
|
||||
printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
sg_offset = (msg[0]>>4)&0xf;
|
||||
msg[2] = 0x40000000; // IOCTL context
|
||||
msg[3] = adpt_ioctl_to_context(pHba, reply);
|
||||
if (msg[3] == (u32)-1) {
|
||||
rcode = -EBUSY;
|
||||
goto free;
|
||||
}
|
||||
|
||||
sg_list = kcalloc(pHba->sg_tablesize, sizeof(*sg_list), GFP_KERNEL);
|
||||
if (!sg_list) {
|
||||
rcode = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
if(sg_offset) {
|
||||
// TODO add 64 bit API
|
||||
struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset);
|
||||
sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
|
||||
if (sg_count > pHba->sg_tablesize){
|
||||
printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
|
||||
rcode = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
|
||||
for(i = 0; i < sg_count; i++) {
|
||||
int sg_size;
|
||||
|
||||
if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) {
|
||||
printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i, sg[i].flag_count);
|
||||
rcode = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
sg_size = sg[i].flag_count & 0xffffff;
|
||||
/* Allocate memory for the transfer */
|
||||
p = dma_alloc_coherent(&pHba->pDev->dev, sg_size, &addr, GFP_KERNEL);
|
||||
if(!p) {
|
||||
printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
|
||||
pHba->name,sg_size,i,sg_count);
|
||||
rcode = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
|
||||
/* Copy in the user's SG buffer if necessary */
|
||||
if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
|
||||
// sg_simple_element API is 32 bit
|
||||
if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) {
|
||||
printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
|
||||
rcode = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* sg_simple_element API is 32 bit, but addr < 4GB */
|
||||
sg[i].addr_bus = addr;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* Stop any new commands from enterring the
|
||||
* controller while processing the ioctl
|
||||
*/
|
||||
if (pHba->host) {
|
||||
scsi_block_requests(pHba->host);
|
||||
spin_lock_irqsave(pHba->host->host_lock, flags);
|
||||
}
|
||||
rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER);
|
||||
if (rcode != 0)
|
||||
printk("adpt_i2o_passthru: post wait failed %d %p\n",
|
||||
rcode, reply);
|
||||
if (pHba->host) {
|
||||
spin_unlock_irqrestore(pHba->host->host_lock, flags);
|
||||
scsi_unblock_requests(pHba->host);
|
||||
}
|
||||
} while (rcode == -ETIMEDOUT);
|
||||
|
||||
if(rcode){
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if(sg_offset) {
|
||||
/* Copy back the Scatter Gather buffers back to user space */
|
||||
u32 j;
|
||||
// TODO add 64 bit API
|
||||
struct sg_simple_element* sg;
|
||||
int sg_size;
|
||||
|
||||
// re-acquire the original message to handle correctly the sg copy operation
|
||||
memset(&msg, 0, MAX_MESSAGE_SIZE*4);
|
||||
// get user msg size in u32s
|
||||
if(get_user(size, &user_msg[0])){
|
||||
rcode = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
size = size>>16;
|
||||
size *= 4;
|
||||
if (size > MAX_MESSAGE_SIZE) {
|
||||
rcode = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
/* Copy in the user's I2O command */
|
||||
if (copy_from_user (msg, user_msg, size)) {
|
||||
rcode = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
|
||||
|
||||
// TODO add 64 bit API
|
||||
sg = (struct sg_simple_element*)(msg + sg_offset);
|
||||
for (j = 0; j < sg_count; j++) {
|
||||
/* Copy out the SG list to user's buffer if necessary */
|
||||
if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
|
||||
sg_size = sg[j].flag_count & 0xffffff;
|
||||
// sg_simple_element API is 32 bit
|
||||
if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) {
|
||||
printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
|
||||
rcode = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy back the reply to user space */
|
||||
if (reply_size) {
|
||||
// we wrote our own values for context - now restore the user supplied ones
|
||||
if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) {
|
||||
printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name);
|
||||
rcode = -EFAULT;
|
||||
}
|
||||
if(copy_to_user(user_reply, reply, reply_size)) {
|
||||
printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name);
|
||||
rcode = -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cleanup:
|
||||
if (rcode != -ETIME && rcode != -EINTR) {
|
||||
struct sg_simple_element *sg =
|
||||
(struct sg_simple_element*) (msg +sg_offset);
|
||||
while(sg_index) {
|
||||
if(sg_list[--sg_index]) {
|
||||
dma_free_coherent(&pHba->pDev->dev,
|
||||
sg[sg_index].flag_count & 0xffffff,
|
||||
sg_list[sg_index],
|
||||
sg[sg_index].addr_bus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free:
|
||||
kfree(sg_list);
|
||||
kfree(reply);
|
||||
return rcode;
|
||||
}
|
||||
|
||||
#if defined __ia64__
|
||||
static void adpt_ia64_info(sysInfo_S* si)
|
||||
{
|
||||
@ -1980,8 +1733,6 @@ static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong ar
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
case I2OUSRCMD:
|
||||
return adpt_i2o_passthru(pHba, argp);
|
||||
|
||||
case DPT_CTRLINFO:{
|
||||
drvrHBAinfo_S HbaInfo;
|
||||
@ -2138,13 +1889,6 @@ static irqreturn_t adpt_isr(int irq, void *dev_id)
|
||||
adpt_send_nop(pHba, old_m);
|
||||
}
|
||||
context = readl(reply+8);
|
||||
if(context & 0x40000000){ // IOCTL
|
||||
void *p = adpt_ioctl_from_context(pHba, readl(reply+12));
|
||||
if( p != NULL) {
|
||||
memcpy_fromio(p, reply, REPLY_FRAME_SIZE * 4);
|
||||
}
|
||||
// All IOCTLs will also be post wait
|
||||
}
|
||||
if(context & 0x80000000){ // Post wait message
|
||||
status = readl(reply+16);
|
||||
if(status >> 24){
|
||||
@ -2152,16 +1896,14 @@ static irqreturn_t adpt_isr(int irq, void *dev_id)
|
||||
} else {
|
||||
status = I2O_POST_WAIT_OK;
|
||||
}
|
||||
if(!(context & 0x40000000)) {
|
||||
/*
|
||||
* The request tag is one less than the command tag
|
||||
* as the firmware might treat a 0 tag as invalid
|
||||
*/
|
||||
cmd = scsi_host_find_tag(pHba->host,
|
||||
readl(reply + 12) - 1);
|
||||
if(cmd != NULL) {
|
||||
printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
|
||||
}
|
||||
/*
|
||||
* The request tag is one less than the command tag
|
||||
* as the firmware might treat a 0 tag as invalid
|
||||
*/
|
||||
cmd = scsi_host_find_tag(pHba->host,
|
||||
readl(reply + 12) - 1);
|
||||
if(cmd != NULL) {
|
||||
printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
|
||||
}
|
||||
adpt_i2o_post_wait_complete(context, status);
|
||||
} else { // SCSI message
|
||||
|
@ -248,7 +248,6 @@ typedef struct _adpt_hba {
|
||||
void __iomem *FwDebugBLEDflag_P;// Virtual Addr Of FW Debug BLED
|
||||
void __iomem *FwDebugBLEDvalue_P;// Virtual Addr Of FW Debug BLED
|
||||
u32 FwDebugFlags;
|
||||
u32 *ioctl_reply_context[4];
|
||||
} adpt_hba;
|
||||
|
||||
struct sg_simple_element {
|
||||
|
Loading…
Reference in New Issue
Block a user