Skip to content

Commit

Permalink
i2o: Fix 32/64bit DMA locking
Browse files Browse the repository at this point in the history
The I2O ioctls assume 32bits.  In itself that is fine as they are old
cards and nobody uses 64bit.  However on LKML it was noted this
assumption is also made for allocated memory and is unsafe on 64bit
systems.

Fixing this is a mess.  It turns out there is tons of crap buried in a
header file that does racy 32/64bit filtering on the masks.

So we:
- Verify all callers of the racy code can sleep (i2o_dma_[re]alloc)
- Move the code into a new i2o/memory.c file
- Remove the gfp_mask argument so nobody can try and misuse the function
- Wrap a mutex around the problem area (a single mutex is easy to do and
  none of this is performance relevant)
- Switch the remaining problem kmalloc holdout to use i2o_dma_alloc

Cc: Markus Lidel <Markus.Lidel@shadowconnect.com>
Cc: Vasily Averin <vvs@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Alan Cox authored and torvalds committed Oct 16, 2008
1 parent 673c0c0 commit 9d793b0
Show file tree
Hide file tree
Showing 8 changed files with 351 additions and 311 deletions.
2 changes: 1 addition & 1 deletion drivers/message/i2o/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# In the future, some of these should be built conditionally.
#

i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o
i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o memory.o
i2o_bus-y += bus-osm.o
i2o_config-y += config-osm.o
obj-$(CONFIG_I2O) += i2o_core.o
Expand Down
2 changes: 1 addition & 1 deletion drivers/message/i2o/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,

res.virt = NULL;

if (i2o_dma_alloc(dev, &res, reslen, GFP_KERNEL))
if (i2o_dma_alloc(dev, &res, reslen))
return -ENOMEM;

msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
Expand Down
4 changes: 2 additions & 2 deletions drivers/message/i2o/exec-osm.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,8 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)

dev = &c->pdev->dev;

if (i2o_dma_realloc
(dev, &c->dlct, le32_to_cpu(sb->expected_lct_size), GFP_KERNEL))
if (i2o_dma_realloc(dev, &c->dlct,
le32_to_cpu(sb->expected_lct_size)))
return -ENOMEM;

msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
Expand Down
31 changes: 14 additions & 17 deletions drivers/message/i2o/i2o_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ static int i2o_cfg_swdl(unsigned long arg)
if (IS_ERR(msg))
return PTR_ERR(msg);

if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {
if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
i2o_msg_nop(c, msg);
return -ENOMEM;
}
Expand Down Expand Up @@ -339,7 +339,7 @@ static int i2o_cfg_swul(unsigned long arg)
if (IS_ERR(msg))
return PTR_ERR(msg);

if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {
if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
i2o_msg_nop(c, msg);
return -ENOMEM;
}
Expand Down Expand Up @@ -634,9 +634,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd,
sg_size = sg[i].flag_count & 0xffffff;
p = &(sg_list[sg_index]);
/* Allocate memory for the transfer */
if (i2o_dma_alloc
(&c->pdev->dev, p, sg_size,
PCI_DMA_BIDIRECTIONAL)) {
if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
printk(KERN_DEBUG
"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
c->name, sg_size, i, sg_count);
Expand Down Expand Up @@ -780,12 +778,11 @@ static int i2o_cfg_passthru(unsigned long arg)
u32 size = 0;
u32 reply_size = 0;
u32 rcode = 0;
void *sg_list[SG_TABLESIZE];
struct i2o_dma sg_list[SG_TABLESIZE];
u32 sg_offset = 0;
u32 sg_count = 0;
int sg_index = 0;
u32 i = 0;
void *p = NULL;
i2o_status_block *sb;
struct i2o_message *msg;
unsigned int iop;
Expand Down Expand Up @@ -842,6 +839,7 @@ static int i2o_cfg_passthru(unsigned long arg)
memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
if (sg_offset) {
struct sg_simple_element *sg;
struct i2o_dma *p;

if (sg_offset * 4 >= size) {
rcode = -EFAULT;
Expand Down Expand Up @@ -871,22 +869,22 @@ static int i2o_cfg_passthru(unsigned long arg)
goto sg_list_cleanup;
}
sg_size = sg[i].flag_count & 0xffffff;
p = &(sg_list[sg_index]);
if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
/* Allocate memory for the transfer */
p = kmalloc(sg_size, GFP_KERNEL);
if (!p) {
printk(KERN_DEBUG
"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
c->name, sg_size, i, sg_count);
rcode = -ENOMEM;
goto sg_list_cleanup;
}
sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
sg_index++;
/* Copy in the user's SG buffer if necessary */
if (sg[i].
flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
// TODO 64bit fix
if (copy_from_user
(p, (void __user *)sg[i].addr_bus,
(p->virt, (void __user *)sg[i].addr_bus,
sg_size)) {
printk(KERN_DEBUG
"%s: Could not copy SG buf %d FROM user\n",
Expand All @@ -895,8 +893,7 @@ static int i2o_cfg_passthru(unsigned long arg)
goto sg_list_cleanup;
}
}
//TODO 64bit fix
sg[i].addr_bus = virt_to_bus(p);
sg[i].addr_bus = p->phys;
}
}

Expand All @@ -908,7 +905,7 @@ static int i2o_cfg_passthru(unsigned long arg)
}

if (sg_offset) {
u32 rmsg[128];
u32 rmsg[I2O_OUTBOUND_MSG_FRAME_SIZE];
/* Copy back the Scatter Gather buffers back to user space */
u32 j;
// TODO 64bit fix
Expand Down Expand Up @@ -942,11 +939,11 @@ static int i2o_cfg_passthru(unsigned long arg)
sg_size = sg[j].flag_count & 0xffffff;
// TODO 64bit fix
if (copy_to_user
((void __user *)sg[j].addr_bus, sg_list[j],
((void __user *)sg[j].addr_bus, sg_list[j].virt,
sg_size)) {
printk(KERN_WARNING
"%s: Could not copy %p TO user %x\n",
c->name, sg_list[j],
c->name, sg_list[j].virt,
sg[j].addr_bus);
rcode = -EFAULT;
goto sg_list_cleanup;
Expand All @@ -973,7 +970,7 @@ static int i2o_cfg_passthru(unsigned long arg)
}

for (i = 0; i < sg_index; i++)
kfree(sg_list[i]);
i2o_dma_free(&c->pdev->dev, &sg_list[i]);

cleanup:
kfree(reply);
Expand Down
2 changes: 1 addition & 1 deletion drivers/message/i2o/iop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ static int i2o_hrt_get(struct i2o_controller *c)

size = hrt->num_entries * hrt->entry_len << 2;
if (size > c->hrt.len) {
if (i2o_dma_realloc(dev, &c->hrt, size, GFP_KERNEL))
if (i2o_dma_realloc(dev, &c->hrt, size))
return -ENOMEM;
else
hrt = c->hrt.virt;
Expand Down
Loading

0 comments on commit 9d793b0

Please sign in to comment.