[PATCH 2.4] i2c-dev user/kernel bug and mem leak

Robert T. Johnson rtjohnso at eecs.berkeley.edu
Thu Aug 14 20:44:08 CEST 2003


Thank you for looking at my bug report and proposed patch with such
careful scrutiny!  I think the mem leak fix you propose is fine, but I
had an ulterior motive for writing the user/kernel fix as I did. 

The user/kernel bug was discovered by our automatic bug-finding tool,
cqual, and my patch allowed i2c-dev.c to pass through cqual with no
warnings.  The new patch does not, because rdwr_pa[i].buf is sometimes a
a user pointer and sometimes a kernel pointer, e.g. on i2c-dev.c, line
248:


data_ptrs[i] = rdwr_pa[i].buf; // rdwr_pa[i].buf is user pointer
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); // now it's kernel


Cqual is not just a bug finder, it can verify the _absence_ of bugs.  I
think this is pretty cool.  Imagine a kernel that can be automatically
verified to have no user/kernel bugs.  You'd never have to worry about
user/kernel bugs again!

But like all automatic code verification tools, cqual imposes certain
limits on the types of code you can write.  For example, cqual doesn't
allow a variable to sometimes hold a user pointer and sometimes hold a
kernel pointer, like rdwr_pa[i].buf now does.  The original patch avoids
this, but the new patch doesn't for performance reasons.  FWIW, I think
Linus' checker will also fail to check the new patch. 

So there's a trade-off here between performance and automatic code
auditing.  Considering that 

1. The performance cost of the original patch is minor. 
2. i2c-dev.c has had user/kernel bugs in the past. 
3. This user/kernel bug was tricky and time consuming to understand. 

I propose that having code which can be automatically verified may be
more important than squeezing out every last cycle.  In other words,
user/kernel bugs have proven so tricky to detect and fix that it's worth
writing _slightly_ less efficient code in order to have code which can
be automatically verified. 

After looking at your rewritten patch, I thought of a possibly cleaner
way to make i2c-dev.c pass cqual without warnings.  I've included it
below.  I'd like to work with the i2c developers to find a solution
which can be automatically audited and that you like. 

Thanks for giving this so much thought. 

Best, 
Rob 

This patch is against i2c cvs.  The basic idea is this:

- move all the fields, except buf, of i2c_msg into a substructure, md
(i.e. metadata).
- lots of 1 line changes based on this
- Now the i2cdev_ioctl I2C_RDWR works as follows:

copy all the i2_msg structures from userspace onto tmp_pa
for i = 1 to number of messages
   rdwr_pa[i].md = tmp_pa[i].md;
   rdwr_pa[i].buf = kmalloc(...);
   copy_from_user (rdwr_pa[i].buf, tmp_pa[i].buf);

instead of

copy all the i2_msg structures from userspace onto rdwr_pa
for i = 1 to number of messages
   data_ptr[i] = rdwr_pa[i].buf;
   rdwr_pa[i].buf = kmalloc(...);
   copy_from_user (rdwr_pa[i].buf, data_ptr[i]);

The overhead versus the current version of the ioctl is
- the extra memory consumed by tmp_pa (versus data_ptr)
- the copy of the md field (versus the copy of rdwr_pa[i].buf).
The benefit is
- never have to worry about user/kernel bugs again.

diff -urN --exclude=CVS i2c.orig/kernel/i2c-algo-bit.c i2c.md/kernel/i2c-algo-bit.c
--- i2c.orig/kernel/i2c-algo-bit.c	Fri Jul 25 00:56:42 2003
+++ i2c.md/kernel/i2c-algo-bit.c	Wed Aug 13 16:02:16 2003
@@ -336,8 +336,8 @@
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 	char c;
 	const char *temp = msg->buf;
-	int count = msg->len;
-	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; 
+	int count = msg->md.len;
+	unsigned short nak_ok = msg->md.flags & I2C_M_IGNORE_NAK; 
 	int retval;
 	int wrcount=0;
 
@@ -371,7 +371,7 @@
 	int rdcount=0;   	/* counts bytes read */
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 	char *temp = msg->buf;
-	int count = msg->len;
+	int count = msg->md.len;
 
 	while (count > 0) {
 		inval = i2c_inb(i2c_adap);
@@ -414,8 +414,8 @@
  */
 static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) 
 {
-	unsigned short flags = msg->flags;
-	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
+	unsigned short flags = msg->md.flags;
+	unsigned short nak_ok = msg->md.flags & I2C_M_IGNORE_NAK;
 	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
 
 	unsigned char addr;
@@ -425,7 +425,7 @@
 	
 	if ( (flags & I2C_M_TEN)  ) { 
 		/* a ten bit address */
-		addr = 0xf0 | (( msg->addr >> 7) & 0x03);
+		addr = 0xf0 | (( msg->md.addr >> 7) & 0x03);
 		DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
 		/* try extended address code...*/
 		ret = try_address(i2c_adap, addr, retries);
@@ -434,7 +434,7 @@
 			return -EREMOTEIO;
 		}
 		/* the remaining 8 bit address */
-		ret = i2c_outb(i2c_adap,msg->addr & 0x7f);
+		ret = i2c_outb(i2c_adap,msg->md.addr & 0x7f);
 		if ((ret != 1) && !nak_ok) {
 			/* the chip did not ack / xmission error occurred */
 			printk(KERN_ERR "died at 2nd address code.\n");
@@ -451,7 +451,7 @@
 			}
 		}
 	} else {		/* normal 7bit address	*/
-		addr = ( msg->addr << 1 );
+		addr = ( msg->md.addr << 1 );
 		if (flags & I2C_M_RD )
 			addr |= 1;
 		if (flags & I2C_M_REV_DIR_ADDR )
@@ -476,30 +476,30 @@
 	i2c_start(adap);
 	for (i=0;i<num;i++) {
 		pmsg = &msgs[i];
-		nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; 
-		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		nak_ok = pmsg->md.flags & I2C_M_IGNORE_NAK; 
+		if (!(pmsg->md.flags & I2C_M_NOSTART)) {
 			if (i) {
 				i2c_repstart(adap);
 			}
 			ret = bit_doAddress(i2c_adap, pmsg);
 			if ((ret != 0) && !nak_ok) {
 			    DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: NAK from device addr %2.2x msg #%d\n"
-					,msgs[i].addr,i));
+					,msgs[i].md.addr,i));
 			    return (ret<0) ? ret : -EREMOTEIO;
 			}
 		}
-		if (pmsg->flags & I2C_M_RD ) {
+		if (pmsg->md.flags & I2C_M_RD ) {
 			/* read bytes into buffer*/
 			ret = readbytes(i2c_adap, pmsg);
 			DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: read %d bytes.\n",ret));
-			if (ret < pmsg->len ) {
+			if (ret < pmsg->md.len ) {
 				return (ret<0)? ret : -EREMOTEIO;
 			}
 		} else {
 			/* write bytes from buffer */
 			ret = sendbytes(i2c_adap, pmsg);
 			DEB2(printk(KERN_DEBUG "i2c-algo-bit.o: wrote %d bytes.\n",ret));
-			if (ret < pmsg->len ) {
+			if (ret < pmsg->md.len ) {
 				return (ret<0) ? ret : -EREMOTEIO;
 			}
 		}
diff -urN --exclude=CVS i2c.orig/kernel/i2c-algo-biths.c i2c.md/kernel/i2c-algo-biths.c
--- i2c.orig/kernel/i2c-algo-biths.c	Fri Jul 25 00:56:42 2003
+++ i2c.md/kernel/i2c-algo-biths.c	Wed Aug 13 16:04:36 2003
@@ -440,31 +440,31 @@
 	unsigned char addr[2];
 	int count;
 
-	if ( msg->flags & I2C_M_TEN ) { 
+	if ( msg->md.flags & I2C_M_TEN ) { 
 		/* a ten bit address */
 		count = 2;
-		addr[0] = 0xf0 | (( msg->addr >> 7) & 0x03);
-		addr[1] = msg->addr & 0x7f;
+		addr[0] = 0xf0 | (( msg->md.addr >> 7) & 0x03);
+		addr[1] = msg->md.addr & 0x7f;
 
 		/* try extended address code ... and the remaining 8 bit address */
-		TRY(i2c_outb(adap, msg->flags, addr, &count));
+		TRY(i2c_outb(adap, msg->md.flags, addr, &count));
 
-		if ( msg->flags & I2C_M_RD ) {
+		if ( msg->md.flags & I2C_M_RD ) {
 			TRY(i2c_start(adap));
 			
 			/* okay, now switch into reading mode */
 			count = 1;
 			addr[0] |= 0x01;
-			TRY(i2c_outb(adap, msg->flags, addr, &count));
+			TRY(i2c_outb(adap, msg->md.flags, addr, &count));
 		}
 	} else {		/* normal 7bit address	*/
 		count = 1;
-		addr[0] = ( msg->addr << 1 );
-		if (msg->flags & I2C_M_RD )
+		addr[0] = ( msg->md.addr << 1 );
+		if (msg->md.flags & I2C_M_RD )
 			addr[0] |= 1;
-		if (msg->flags & I2C_M_REV_DIR_ADDR )
+		if (msg->md.flags & I2C_M_REV_DIR_ADDR )
 			addr[0] ^= 1;
-		TRY(i2c_outb(adap, msg->flags, addr, &count));
+		TRY(i2c_outb(adap, msg->md.flags, addr, &count));
 	}
 }
 
@@ -498,13 +498,13 @@
 		}
 	}
 
-	for (j=0; j<num; j++) msgs[j].err = 0; // unprocessed
+	for (j=0; j<num; j++) msgs[j].md.err = 0; // unprocessed
 
 	do switch (state) {
 	    
 	    case MSG_INIT:
 		    msg = &msgs[mn];
-		    if ((msg->flags & I2C_M_NOSTART) && (mn)) {
+		    if ((msg->md.flags & I2C_M_NOSTART) && (mn)) {
 			    state = MSG_DATA;
 			    break;
 		    }
@@ -524,13 +524,13 @@
 		    }
 
 	    case MSG_DATA:
-		    j = msg->len;
-		    if ( msg->flags & I2C_M_RD ) {
-			    i2c_inb(adap, msg->flags, msg->buf, &j);
+		    j = msg->md.len;
+		    if ( msg->md.flags & I2C_M_RD ) {
+			    i2c_inb(adap, msg->md.flags, msg->buf, &j);
 		    } else {
-			    i2c_outb(adap, msg->flags, msg->buf, &j);
+			    i2c_outb(adap, msg->md.flags, msg->buf, &j);
 		    }
-		    msg->done = msg->len - j;
+		    msg->md.done = msg->md.len - j;
 		    if (adap->errors) {
 			    state = MSG_STOP;
 			    break;
@@ -539,30 +539,30 @@
 	    case MSG_READY:
 		    mn++;
 		    if (mn<num) {
-			    msg->err = errflag(adap->errors);
-			    debug_protocol(adap, msg->err);
+			    msg->md.err = errflag(adap->errors);
+			    debug_protocol(adap, msg->md.err);
 			    state = MSG_INIT;
 			    break;
 		    }
 
 	    case MSG_STOP:
-		    msg->err = errflag(adap->errors);
+		    msg->md.err = errflag(adap->errors);
 		    i2c_stop(adap);
 		    j = 0;
 		    while (adap->errors) {
 			    if ( ++j > 10) {
-				    msg->err = -ENODEV;
+				    msg->md.err = -ENODEV;
 				    break;
 			    }
 			    i2c_stop(adap);
 		    }
-		    debug_protocol(adap, msg->err);
+		    debug_protocol(adap, msg->md.err);
 		    state = MSG_EXIT;
 		    break;
 
 	    default: /* not reached */
 		    state = MSG_EXIT;
-		    msg->err = -EINVAL;
+		    msg->md.err = -EINVAL;
 		    break;
 		    		  
 	} while (state != MSG_EXIT);
@@ -570,9 +570,9 @@
 	if (adap->dstr) kfree(adap->dstr);
 
 	for (j=0; j<num; j++)
-		debug_printout(i2c_adap, j, msgs[j].err);
+		debug_printout(i2c_adap, j, msgs[j].md.err);
 
-	return (msg->err < 0) ? msg->err : mn;
+	return (msg->md.err < 0) ? msg->md.err : mn;
 }
 
 static u32 bit_func(struct i2c_adapter *i2c_adap)
diff -urN --exclude=CVS i2c.orig/kernel/i2c-algo-pcf.c i2c.md/kernel/i2c-algo-pcf.c
--- i2c.orig/kernel/i2c-algo-pcf.c	Fri Jul 25 00:56:42 2003
+++ i2c.md/kernel/i2c-algo-pcf.c	Wed Aug 13 16:05:59 2003
@@ -299,12 +299,12 @@
 static inline int pcf_doAddress(struct i2c_algo_pcf_data *adap,
                                 struct i2c_msg *msg, int retries) 
 {
-	unsigned short flags = msg->flags;
+	unsigned short flags = msg->md.flags;
 	unsigned char addr;
 	int ret;
 	if ( (flags & I2C_M_TEN)  ) { 
 		/* a ten bit address */
-		addr = 0xf0 | (( msg->addr >> 7) & 0x03);
+		addr = 0xf0 | (( msg->md.addr >> 7) & 0x03);
 		DEB2(printk(KERN_DEBUG "addr0: %d\n",addr));
 		/* try extended address code...*/
 		ret = try_address(adap, addr, retries);
@@ -313,7 +313,7 @@
 			return -EREMOTEIO;
 		}
 		/* the remaining 8 bit address */
-		i2c_outb(adap,msg->addr & 0x7f);
+		i2c_outb(adap,msg->md.addr & 0x7f);
 /* Status check comes here */
 		if (ret != 1) {
 			printk(KERN_ERR "died at 2nd address code.\n");
@@ -330,7 +330,7 @@
 			}
 		}
 	} else {		/* normal 7bit address	*/
-		addr = ( msg->addr << 1 );
+		addr = ( msg->md.addr << 1 );
 		if (flags & I2C_M_RD )
 			addr |= 1;
 		if (flags & I2C_M_REV_DIR_ADDR )
@@ -362,8 +362,8 @@
 		pmsg = &msgs[i];
 
 		DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: Doing %s %d bytes to 0x%02x - %d of %d messages\n",
-		     pmsg->flags & I2C_M_RD ? "read" : "write",
-                     pmsg->len, pmsg->addr, i + 1, num);)
+		     pmsg->md.flags & I2C_M_RD ? "read" : "write",
+                     pmsg->md.len, pmsg->md.addr, i + 1, num);)
     
 		ret = pcf_doAddress(adap, pmsg, i2c_adap->retries);
 
@@ -391,25 +391,25 @@
 #endif
     
 		DEB3(printk(KERN_DEBUG "i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
-			    i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
+			    i, msgs[i].md.addr, msgs[i].md.flags, msgs[i].md.len);)
     
 		/* Read */
-		if (pmsg->flags & I2C_M_RD) {
+		if (pmsg->md.flags & I2C_M_RD) {
 			/* read bytes into buffer*/
-			ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len,
+			ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->md.len,
                                             (i + 1 == num));
         
-			if (ret != pmsg->len) {
+			if (ret != pmsg->md.len) {
 				DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
 					    "only read %d bytes.\n",ret));
 			} else {
 				DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: read %d bytes.\n",ret));
 			}
 		} else { /* Write */
-			ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len,
+			ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->md.len,
                                             (i + 1 == num));
         
-			if (ret != pmsg->len) {
+			if (ret != pmsg->md.len) {
 				DEB2(printk(KERN_DEBUG "i2c-algo-pcf.o: fail: "
 					    "only wrote %d bytes.\n",ret));
 			} else {
diff -urN --exclude=CVS i2c.orig/kernel/i2c-core.c i2c.md/kernel/i2c-core.c
--- i2c.orig/kernel/i2c-core.c	Fri Jul 25 00:56:42 2003
+++ i2c.md/kernel/i2c-core.c	Wed Aug 13 15:59:06 2003
@@ -702,9 +702,9 @@
 	struct i2c_msg msg;
 
 	if (client->adapter->algo->master_xfer) {
-		msg.addr   = client->addr;
-		msg.flags = client->flags & I2C_M_TEN;
-		msg.len = count;
+		msg.md.addr   = client->addr;
+		msg.md.flags = client->flags & I2C_M_TEN;
+		msg.md.len = count;
 		(const char *)msg.buf = buf;
 	
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n",
@@ -731,10 +731,10 @@
 	struct i2c_msg msg;
 	int ret;
 	if (client->adapter->algo->master_xfer) {
-		msg.addr   = client->addr;
-		msg.flags = client->flags & I2C_M_TEN;
-		msg.flags |= I2C_M_RD;
-		msg.len = count;
+		msg.md.addr   = client->addr;
+		msg.md.flags = client->flags & I2C_M_TEN;
+		msg.md.flags |= I2C_M_RD;
+		msg.md.len = count;
 		msg.buf = buf;
 
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n",
@@ -1211,39 +1211,39 @@
 	unsigned char msgbuf0[34];
 	unsigned char msgbuf1[34];
 	int num = read_write == I2C_SMBUS_READ?2:1;
-	struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, 
-	                          { addr, flags | I2C_M_RD, 0, msgbuf1 }
+	struct i2c_msg msg[2] = { { { addr, flags, 1 }, msgbuf0 }, 
+	                          { { addr, flags | I2C_M_RD, 0 } , msgbuf1 }
 	                        };
 	int i;
 
 	msgbuf0[0] = command;
 	switch(size) {
 	case I2C_SMBUS_QUICK:
-		msg[0].len = 0;
+		msg[0].md.len = 0;
 		/* Special case: The read/write field is used as data */
-		msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
+		msg[0].md.flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
 		num = 1;
 		break;
 	case I2C_SMBUS_BYTE:
 		if (read_write == I2C_SMBUS_READ) {
 			/* Special case: only a read! */
-			msg[0].flags = I2C_M_RD | flags;
+			msg[0].md.flags = I2C_M_RD | flags;
 			num = 1;
 		}
 		break;
 	case I2C_SMBUS_BYTE_DATA:
 		if (read_write == I2C_SMBUS_READ)
-			msg[1].len = 1;
+			msg[1].md.len = 1;
 		else {
-			msg[0].len = 2;
+			msg[0].md.len = 2;
 			msgbuf0[1] = data->byte;
 		}
 		break;
 	case I2C_SMBUS_WORD_DATA:
 		if (read_write == I2C_SMBUS_READ)
-			msg[1].len = 2;
+			msg[1].md.len = 2;
 		else {
-			msg[0].len=3;
+			msg[0].md.len=3;
 			msgbuf0[1] = data->word & 0xff;
 			msgbuf0[2] = (data->word >> 8) & 0xff;
 		}
@@ -1251,8 +1251,8 @@
 	case I2C_SMBUS_PROC_CALL:
 		num = 2; /* Special case */
 		read_write = I2C_SMBUS_READ;
-		msg[0].len = 3;
-		msg[1].len = 2;
+		msg[0].md.len = 3;
+		msg[1].md.len = 2;
 		msgbuf0[1] = data->word & 0xff;
 		msgbuf0[2] = (data->word >> 8) & 0xff;
 		break;
@@ -1263,16 +1263,16 @@
 			       "under I2C emulation!\n");
 			return -1;
 		} else {
-			msg[0].len = data->block[0] + 2;
-			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
+			msg[0].md.len = data->block[0] + 2;
+			if (msg[0].md.len > I2C_SMBUS_BLOCK_MAX + 2) {
 				printk(KERN_ERR "i2c-core.o: smbus_access called with "
 				       "invalid block write size (%d)\n",
 				       data->block[0]);
 				return -1;
 			}
 			if(size == I2C_SMBUS_BLOCK_DATA_PEC)
-				(msg[0].len)++;
-			for (i = 1; i <= msg[0].len; i++)
+				(msg[0].md.len)++;
+			for (i = 1; i <= msg[0].md.len; i++)
 				msgbuf0[i] = data->block[i-1];
 		}
 		break;
@@ -1283,10 +1283,10 @@
 		return -1;
 	case I2C_SMBUS_I2C_BLOCK_DATA:
 		if (read_write == I2C_SMBUS_READ) {
-			msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX;
+			msg[1].md.len = I2C_SMBUS_I2C_BLOCK_MAX;
 		} else {
-			msg[0].len = data->block[0] + 1;
-			if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) {
+			msg[0].md.len = data->block[0] + 1;
+			if (msg[0].md.len > I2C_SMBUS_I2C_BLOCK_MAX + 1) {
 				printk("i2c-core.o: i2c_smbus_xfer_emulated called with "
 				       "invalid block write size (%d)\n",
 				       data->block[0]);
diff -urN --exclude=CVS i2c.orig/kernel/i2c-dev.c i2c.md/kernel/i2c-dev.c
--- i2c.orig/kernel/i2c-dev.c	Sat Aug  9 11:59:43 2003
+++ i2c.md/kernel/i2c-dev.c	Thu Aug 14 10:51:49 2003
@@ -170,8 +170,8 @@
 	struct i2c_rdwr_ioctl_data rdwr_arg;
 	struct i2c_smbus_ioctl_data data_arg;
 	union i2c_smbus_data temp;
+	struct i2c_msg *tmp_pa;
 	struct i2c_msg *rdwr_pa;
-	u8 **data_ptrs;
 	int i,datasize,res;
 	unsigned long funcs;
 
@@ -218,43 +218,46 @@
 		if (rdwr_arg.nmsgs > 42)
 			return -EINVAL;
                
-		rdwr_pa = (struct i2c_msg *)
+		tmp_pa = (struct i2c_msg *)
 			kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), 
 			GFP_KERNEL);
 
-		if (rdwr_pa == NULL) return -ENOMEM;
+		if (tmp_pa == NULL) return -ENOMEM;
 
-		if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
+		if (copy_from_user(tmp_pa, rdwr_arg.msgs,
 				   rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
-			kfree(rdwr_pa);
+			kfree(tmp_pa);
 			return -EFAULT;
 		}
 
-		data_ptrs = (u8 **) kmalloc(rdwr_arg.nmsgs * sizeof(u8 *),
-					    GFP_KERNEL);
-		if (data_ptrs == NULL) {
-			kfree(rdwr_pa);
+		rdwr_pa = (struct i2c_msg *)
+			kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), 
+			GFP_KERNEL);
+
+		if (rdwr_pa == NULL) {
+			kfree(tmp_pa);
 			return -ENOMEM;
 		}
 
 		res = 0;
 		for( i=0; i<rdwr_arg.nmsgs; i++ )
 		{
+			rdwr_pa[i].md = tmp_pa[i].md;
+
 			/* Limit the size of the message to a sane amount */
-			if (rdwr_pa[i].len > 8192) {
+			if (rdwr_pa[i].md.len > 8192) {
 				res = -EINVAL;
 				break;
 			}
-			data_ptrs[i] = rdwr_pa[i].buf;
-			rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
+			rdwr_pa[i].buf = kmalloc(rdwr_pa[i].md.len, GFP_KERNEL);
 			if(rdwr_pa[i].buf == NULL)
 			{
 				res = -ENOMEM;
 				break;
 			}
 			if(copy_from_user(rdwr_pa[i].buf,
-				data_ptrs[i],
-				rdwr_pa[i].len))
+				tmp_pa[i].buf,
+				rdwr_pa[i].md.len))
 			{
 				++i; /* Needs to be kfreed too */
 				res = -EFAULT;
@@ -265,8 +268,8 @@
 			int j;
 			for (j = 0; j < i; ++j)
 				kfree(rdwr_pa[j].buf);
-			kfree(data_ptrs);
 			kfree(rdwr_pa);
+			kfree(tmp_pa);
 			return res;
 		}
 
@@ -275,20 +278,20 @@
 			rdwr_arg.nmsgs);
 		while(i-- > 0)
 		{
-			if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD))
+			if( res>=0 && (rdwr_pa[i].md.flags & I2C_M_RD))
 			{
 				if(copy_to_user(
-					data_ptrs[i],
+					tmp_pa[i].buf,
 					rdwr_pa[i].buf,
-					rdwr_pa[i].len))
+					rdwr_pa[i].md.len))
 				{
 					res = -EFAULT;
 				}
 			}
 			kfree(rdwr_pa[i].buf);
 		}
-		kfree(data_ptrs);
 		kfree(rdwr_pa);
+		kfree(tmp_pa);
 		return res;
 
 	case I2C_SMBUS:
diff -urN --exclude=CVS i2c.orig/kernel/i2c.h i2c.md/kernel/i2c.h
--- i2c.orig/kernel/i2c.h	Sat Aug  2 22:07:09 2003
+++ i2c.md/kernel/i2c.h	Wed Aug 13 15:54:49 2003
@@ -355,18 +355,20 @@
  * I2C Message - used for pure i2c transaction, also from /dev interface
  */
 struct i2c_msg {
-	__u16 addr;	/* slave address			*/
-	__u16 flags;		
+	struct {
+		__u16 addr;	/* slave address			*/
+		__u16 flags;		
 #define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
 #define I2C_M_RD	0x01
 #define I2C_M_NOSTART	0x4000
 #define I2C_M_REV_DIR_ADDR	0x2000
 #define I2C_M_IGNORE_NAK	0x1000
 #define I2C_M_NO_RD_ACK		0x0800
-	__u16 len;		/* msg length				*/
+		__u16 len;		/* msg length				*/
+		int err;
+		short done;
+	} md;
 	__u8 *buf;		/* pointer to msg data			*/
-	int err;
-	short done;
 };
 
 /* To determine what functionality is present */



More information about the lm-sensors mailing list