Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d793b0b authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds
Browse files

i2o: Fix 32/64bit DMA locking



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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 673c0c00
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -5,7 +5,7 @@
# In the future, some of these should be built conditionally.
# 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_bus-y		+= bus-osm.o
i2o_config-y		+= config-osm.o
i2o_config-y		+= config-osm.o
obj-$(CONFIG_I2O)	+= i2o_core.o
obj-$(CONFIG_I2O)	+= i2o_core.o
+1 −1
Original line number Original line Diff line number Diff line
@@ -467,7 +467,7 @@ int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,


	res.virt = NULL;
	res.virt = NULL;


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


	msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
	msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+2 −2
Original line number Original line Diff line number Diff line
@@ -388,8 +388,8 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)


	dev = &c->pdev->dev;
	dev = &c->pdev->dev;


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


	msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
	msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+14 −17
Original line number Original line Diff line number Diff line
@@ -260,7 +260,7 @@ static int i2o_cfg_swdl(unsigned long arg)
	if (IS_ERR(msg))
	if (IS_ERR(msg))
		return PTR_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);
		i2o_msg_nop(c, msg);
		return -ENOMEM;
		return -ENOMEM;
	}
	}
@@ -339,7 +339,7 @@ static int i2o_cfg_swul(unsigned long arg)
	if (IS_ERR(msg))
	if (IS_ERR(msg))
		return PTR_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);
		i2o_msg_nop(c, msg);
		return -ENOMEM;
		return -ENOMEM;
	}
	}
@@ -634,9 +634,7 @@ static int i2o_cfg_passthru32(struct file *file, unsigned cmnd,
			sg_size = sg[i].flag_count & 0xffffff;
			sg_size = sg[i].flag_count & 0xffffff;
			p = &(sg_list[sg_index]);
			p = &(sg_list[sg_index]);
			/* Allocate memory for the transfer */
			/* Allocate memory for the transfer */
			if (i2o_dma_alloc
			if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
			    (&c->pdev->dev, p, sg_size,
			     PCI_DMA_BIDIRECTIONAL)) {
				printk(KERN_DEBUG
				printk(KERN_DEBUG
				       "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
				       "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
				       c->name, sg_size, i, sg_count);
				       c->name, sg_size, i, sg_count);
@@ -780,12 +778,11 @@ static int i2o_cfg_passthru(unsigned long arg)
	u32 size = 0;
	u32 size = 0;
	u32 reply_size = 0;
	u32 reply_size = 0;
	u32 rcode = 0;
	u32 rcode = 0;
	void *sg_list[SG_TABLESIZE];
	struct i2o_dma sg_list[SG_TABLESIZE];
	u32 sg_offset = 0;
	u32 sg_offset = 0;
	u32 sg_count = 0;
	u32 sg_count = 0;
	int sg_index = 0;
	int sg_index = 0;
	u32 i = 0;
	u32 i = 0;
	void *p = NULL;
	i2o_status_block *sb;
	i2o_status_block *sb;
	struct i2o_message *msg;
	struct i2o_message *msg;
	unsigned int iop;
	unsigned int iop;
@@ -842,6 +839,7 @@ static int i2o_cfg_passthru(unsigned long arg)
	memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
	memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
	if (sg_offset) {
	if (sg_offset) {
		struct sg_simple_element *sg;
		struct sg_simple_element *sg;
		struct i2o_dma *p;


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


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


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


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


cleanup:
cleanup:
	kfree(reply);
	kfree(reply);
+1 −1
Original line number Original line Diff line number Diff line
@@ -1004,7 +1004,7 @@ static int i2o_hrt_get(struct i2o_controller *c)


		size = hrt->num_entries * hrt->entry_len << 2;
		size = hrt->num_entries * hrt->entry_len << 2;
		if (size > c->hrt.len) {
		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;
				return -ENOMEM;
			else
			else
				hrt = c->hrt.virt;
				hrt = c->hrt.virt;
Loading