Skip to contentSkip to conversation

sunrpc - fixup userspace buffer possible overrun v2

PATCH In review Cyrill Gorcunov 3 participants 5 messages Aug 31, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 sunrpc - fixup userspace buffer possible overrun v2

Vegard Nossum reported
----------------------

Quoted 11 lines

I noticed that something weird is going on with /proc/sys/sunrpc/transports. This file is generated in net/sunrpc/sysctl.c, function proc_do_xprt(). When I "cat" this file, I get the expected output:

   $ cat /proc/sys/sunrpc/transports
   tcp 1048576
   udp 32768

But I think that it does not check the length of the buffer supplied by userspace to read(). With my original program, I found that the stack was being overwritten by the characters above, even when the length given to read() was just 1.

David Wagner added (among other things) that copy_to_user could be probably used here.

The conclusion is that proc_do_xprt doesn't check for userside buffer size indeed so fix. Also set lenp to number of bytes were really written.

---

Please review.

net/sunrpc/sysctl.c +9 -6
Index: linux-2.6.git/net/sunrpc/sysctl.c
===================================================================
@@ -60,23 +60,26 @@ static int proc_do_xprt(ctl_table *table
 			void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char tmpbuf[256];
-	int len;
+	size_t len;
+
 	if ((*ppos && !write) || !*lenp) {
 		*lenp = 0;
 		return 0;
 	}
+
 	if (write)
 		return -EINVAL;
 	else {
 		len = svc_print_xprts(tmpbuf, sizeof(tmpbuf));
-		if (!access_ok(VERIFY_WRITE, buffer, len))
-			return -EFAULT;
-
-		if (__copy_to_user(buffer, tmpbuf, len))
+		if (len > *lenp)
+			len = *lenp;
+		if (copy_to_user(buffer, tmpbuf, len))
 			return -EFAULT;
 	}
-	*lenp -= len;
+
+	*lenp = len;
 	*ppos += len;
+
 	return 0;
 }
 
Tested-by on v1 1/1
#1 lore raw
Quoted 20 lines · Cyrill Gorcunov

Vegard Nossum reported
----------------------

Quoted 11 lines

I noticed that something weird is going on with /proc/sys/sunrpc/transports. This file is generated in net/sunrpc/sysctl.c, function proc_do_xprt(). When I "cat" this file, I get the expected output:

   $ cat /proc/sys/sunrpc/transports
   tcp 1048576
   udp 32768

But I think that it does not check the length of the buffer supplied by userspace to read(). With my original program, I found that the stack was being overwritten by the characters above, even when the length given to read() was just 1.

David Wagner added (among other things) that copy_to_user could be probably used here.

The conclusion is that proc_do_xprt doesn't check for userside buffer size indeed so fix. Also set lenp to number of bytes were really written.

Quoted 6 lines

---

Please review.

read() returns the correct number of bytes written in to the buffer. read() does not overwrite the buffer past the length that the user supplied. Too small buffer results in a partial data.

But trying to call read() twice results in first a partial buffer, then EOF:

open("/proc/sys/sunrpc/transports", O_RDONLY) = 3
read(3, "tc", 2) = 2
read(3, "", 2) = 0

Maybe this can be fixed later.

Vegard

Signature · 4 lines
"The animistic metaphor of the bug that maliciously sneaked in while
the programmer was not looking is intellectually dishonest as it
disguises that the error is the programmer's own creation."
	-- E. W. Dijkstra, EWD1036
#2 lore raw
In reply to Cyrill Gorcunov

Hi Cyrill,

Quoted 2 lines · Cyrill Gorcunov

The conclusion is that proc_do_xprt doesn't check for userside buffer size indeed so fix. Also set lenp to number of bytes were really written.

Why not use simple_read_from_buffer() for the read case and keep the -EINVAL for the write case.

Quoted 12 lines

---

Please review.

Index: linux-2.6.git/net/sunrpc/sysctl.c
===================================================================

net/sunrpc/sysctl.c static int proc_do_xprt(ctl_table *table · line 64

Quoted 6 lines
net/sunrpc/sysctl.c +2 -1
@@ -60,23 +60,26 @@ static int proc_do_xprt(ctl_table *table
 			void __user *buffer, size_t *lenp, loff_t *ppos)
 {
 	char tmpbuf[256];
-	int len;
+	size_t len;
+

+ ssize_t ret;

net/sunrpc/sysctl.c static int proc_do_xprt(ctl_table *table · line 71

Quoted 7 lines
net/sunrpc/sysctl.c +1 -0
 	if ((*ppos && !write) || !*lenp) {
 		*lenp = 0;
 		return 0;
 	}
+
 	if (write)
 		return -EINVAL;
        len = svc_print_xprts(tmpbuf, sizeof(tmpbuf));
	ret = simple_read_from_buffer(buffer, ppos, tmpbuf, len);
	if (ret >= 0) {
		*lenp = ret;
		ret = 0;
	}

	return ret;

}

Best Regards

Ingo Oeser

#3 lore raw

[Ingo Oeser - Sun, Aug 31, 2008 at 04:09:10PM +0200]

| Hi Cyrill,
| 
| On Sunday 31 August 2008, Cyrill Gorcunov wrote:
| > The conclusion is that proc_do_xprt doesn't check for userside buffer
| > size indeed so fix. Also set lenp to number of bytes were really written.
| 
| Why not use  simple_read_from_buffer() for the read case and
| keep the -EINVAL for the write case.

Ah, thanks Ingo - good idea. Btw does libfs.c depends on anything?

| 
| > Reported-by: Vegard Nossum <[email protected]>
| > Signed-off-by: Cyrill Gorcunov <[email protected]>
| > CC: David Wagner <[email protected]>
| > ---
| > 
| > Please review.
| > 
| > Index: linux-2.6.git/net/sunrpc/sysctl.c
| > ===================================================================
| > --- linux-2.6.git.orig/net/sunrpc/sysctl.c	2008-08-31 13:43:46.000000000 +0400
| > +++ linux-2.6.git/net/sunrpc/sysctl.c	2008-08-31 13:58:14.000000000 +0400
| > @@ -60,23 +60,26 @@ static int proc_do_xprt(ctl_table *table
| >  			void __user *buffer, size_t *lenp, loff_t *ppos)
| >  {
| >  	char tmpbuf[256];
| > -	int len;
| > +	size_t len;
| > +
| + ssize_t ret;
| >  	if ((*ppos && !write) || !*lenp) {
| >  		*lenp = 0;
| >  		return 0;
| >  	}
| > +
| >  	if (write)
| >  		return -EINVAL;
| 
|         len = svc_print_xprts(tmpbuf, sizeof(tmpbuf));
| 	ret = simple_read_from_buffer(buffer, ppos, tmpbuf, len);
| 	if (ret >= 0) {
| 		*lenp = ret;
| 		ret = 0;
| 	}
| 
| 	return ret;
|    }
| 
| 
| Best Regards
| 
| Ingo Oeser
| 

- Cyrill -

#4 lore raw

[Cyrill Gorcunov - Sun, Aug 31, 2008 at 06:41:29PM +0400]

| [Ingo Oeser - Sun, Aug 31, 2008 at 04:09:10PM +0200]
| | Hi Cyrill,
| | 
| | On Sunday 31 August 2008, Cyrill Gorcunov wrote:
| | > The conclusion is that proc_do_xprt doesn't check for userside buffer
| | > size indeed so fix. Also set lenp to number of bytes were really written.
| | 
| | Why not use  simple_read_from_buffer() for the read case and
| | keep the -EINVAL for the write case.
| 
| Ah, thanks Ingo - good idea. Btw does libfs.c depends
| on anything?
|

...

yes, it's always compiled in.

- Cyrill -