netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
@ 2010-10-02 13:17 Eric Dumazet
  2010-10-04  3:09 ` Américo Wang
  2010-10-04  8:59 ` Robin Holt
  0 siblings, 2 replies; 18+ messages in thread
From: Eric Dumazet @ 2010-10-02 13:17 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-kernel, Robin Holt, Willy Tarreau, David S. Miller, netdev,
	James Morris, Hideaki YOSHIFUJI, Pekka Savola (ipv6), netdev,
	James Morris, Hideaki YOSHIFUJI, Pekka Savola (ipv6),
	Patrick McHardy, Alexey Kuznetsov

When proc_doulongvec_minmax() is used with an array of longs,
and no min/max check requested (.extra1 or .extra2 being NULL), we
dereference a NULL pointer for the second element of the array.

Noticed while doing some changes in network stack for the "16TB problem"

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 kernel/sysctl.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..4fba86d 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
 				break;
 			if (neg)
 				continue;
-			if ((min && val < *min) || (max && val > *max))
+			if ((table->extra1 && val < *min) ||
+			    (table->extra2 && val > *max))
 				continue;
 			*i = val;
 		} else {



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-02 13:17 [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax() Eric Dumazet
@ 2010-10-04  3:09 ` Américo Wang
  2010-10-04  8:59 ` Robin Holt
  1 sibling, 0 replies; 18+ messages in thread
From: Américo Wang @ 2010-10-04  3:09 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Andrew Morton, linux-kernel, Robin Holt, Willy Tarreau,
	David S. Miller, netdev, James Morris, Hideaki YOSHIFUJI,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
>When proc_doulongvec_minmax() is used with an array of longs,
>and no min/max check requested (.extra1 or .extra2 being NULL), we
>dereference a NULL pointer for the second element of the array.
>
>Noticed while doing some changes in network stack for the "16TB problem"
>
>Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>---
> kernel/sysctl.c |    3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
>diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>index f88552c..4fba86d 100644
>--- a/kernel/sysctl.c
>+++ b/kernel/sysctl.c
>@@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
> 				break;
> 			if (neg)
> 				continue;
>-			if ((min && val < *min) || (max && val > *max))
>+			if ((table->extra1 && val < *min) ||
>+			    (table->extra2 && val > *max))
> 				continue;


Yes? This is wrong for me, min and max are pointers to ->extra{1,2},
they are increased within the for loop, you are only checking the
the pointer to the first element of ->extra{1,2}.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-02 13:17 [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax() Eric Dumazet
  2010-10-04  3:09 ` Américo Wang
@ 2010-10-04  8:59 ` Robin Holt
  2010-10-04  9:04   ` Eric Dumazet
  1 sibling, 1 reply; 18+ messages in thread
From: Robin Holt @ 2010-10-04  8:59 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Andrew Morton, linux-kernel, Robin Holt, Willy Tarreau,
	David S. Miller, netdev, James Morris, Hideaki YOSHIFUJI,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
> When proc_doulongvec_minmax() is used with an array of longs,
> and no min/max check requested (.extra1 or .extra2 being NULL), we
> dereference a NULL pointer for the second element of the array.
> 
> Noticed while doing some changes in network stack for the "16TB problem"
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
>  kernel/sysctl.c |    3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index f88552c..4fba86d 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>  				break;
>  			if (neg)
>  				continue;
> -			if ((min && val < *min) || (max && val > *max))
> +			if ((table->extra1 && val < *min) ||
> +			    (table->extra2 && val > *max))

How about changing:
        for (; left && vleft--; i++, min++, max++, first=0) {
into:
        for (; left && vleft--; i++, min = min ? min + 1 : NULL, max = max ? max + 1: NULL, first=0) {

That would make min and max correct and reduce the chances somebody in
the future overlooks the fact they are currently filled with garbage.

Robin

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04  8:59 ` Robin Holt
@ 2010-10-04  9:04   ` Eric Dumazet
  2010-10-04  9:34     ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Eric Dumazet @ 2010-10-04  9:04 UTC (permalink / raw)
  To: Robin Holt
  Cc: Andrew Morton, linux-kernel, Willy Tarreau, David S. Miller,
	netdev, James Morris, Hideaki YOSHIFUJI, Pekka Savola (ipv6),
	Patrick McHardy, Alexey Kuznetsov

Le lundi 04 octobre 2010 à 03:59 -0500, Robin Holt a écrit :
> On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
> > When proc_doulongvec_minmax() is used with an array of longs,
> > and no min/max check requested (.extra1 or .extra2 being NULL), we
> > dereference a NULL pointer for the second element of the array.
> > 
> > Noticed while doing some changes in network stack for the "16TB problem"
> > 
> > Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> > ---
> >  kernel/sysctl.c |    3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> > 
> > diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> > index f88552c..4fba86d 100644
> > --- a/kernel/sysctl.c
> > +++ b/kernel/sysctl.c
> > @@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
> >  				break;
> >  			if (neg)
> >  				continue;
> > -			if ((min && val < *min) || (max && val > *max))
> > +			if ((table->extra1 && val < *min) ||
> > +			    (table->extra2 && val > *max))
> 
> How about changing:
>         for (; left && vleft--; i++, min++, max++, first=0) {
> into:
>         for (; left && vleft--; i++, min = min ? min + 1 : NULL, max = max ? max + 1: NULL, first=0) {
> 
> That would make min and max correct and reduce the chances somebody in
> the future overlooks the fact they are currently filled with garbage.

I prefer my solution, because the check is done only in the 'write'
case, while its done also for 'read' in your solution, not counting the
for (;;) is really ugly...

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04  9:04   ` Eric Dumazet
@ 2010-10-04  9:34     ` Américo Wang
  2010-10-04 10:10       ` Eric Dumazet
  0 siblings, 1 reply; 18+ messages in thread
From: Américo Wang @ 2010-10-04  9:34 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Robin Holt, Andrew Morton, linux-kernel, Willy Tarreau,
	David S. Miller, netdev, James Morris, Hideaki YOSHIFUJI,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

On Mon, Oct 04, 2010 at 11:04:18AM +0200, Eric Dumazet wrote:
>Le lundi 04 octobre 2010 à 03:59 -0500, Robin Holt a écrit :
>> On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
>> > When proc_doulongvec_minmax() is used with an array of longs,
>> > and no min/max check requested (.extra1 or .extra2 being NULL), we
>> > dereference a NULL pointer for the second element of the array.
>> > 
>> > Noticed while doing some changes in network stack for the "16TB problem"
>> > 
>> > Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>> > ---
>> >  kernel/sysctl.c |    3 ++-
>> >  1 file changed, 2 insertions(+), 1 deletion(-)
>> > 
>> > diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>> > index f88552c..4fba86d 100644
>> > --- a/kernel/sysctl.c
>> > +++ b/kernel/sysctl.c
>> > @@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>> >  				break;
>> >  			if (neg)
>> >  				continue;
>> > -			if ((min && val < *min) || (max && val > *max))
>> > +			if ((table->extra1 && val < *min) ||
>> > +			    (table->extra2 && val > *max))
>> 
>> How about changing:
>>         for (; left && vleft--; i++, min++, max++, first=0) {
>> into:
>>         for (; left && vleft--; i++, min = min ? min + 1 : NULL, max = max ? max + 1: NULL, first=0) {
>> 
>> That would make min and max correct and reduce the chances somebody in
>> the future overlooks the fact they are currently filled with garbage.
>
>I prefer my solution, because the check is done only in the 'write'
>case, while its done also for 'read' in your solution, not counting the
>for (;;) is really ugly...
>

Sorry, I still don't get the point here, min and max
are pointers, they are already checked before dereferenced.
After your patch, min and max will be still increased, while
you are still checking ->extra{1,2} which is wrong.

I see no problem with the original code, or I must have missed something?


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04  9:34     ` Américo Wang
@ 2010-10-04 10:10       ` Eric Dumazet
  2010-10-04 10:35         ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Eric Dumazet @ 2010-10-04 10:10 UTC (permalink / raw)
  To: Américo Wang
  Cc: Robin Holt, Andrew Morton, linux-kernel, Willy Tarreau,
	David S. Miller, netdev, James Morris, Hideaki YOSHIFUJI,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

Le lundi 04 octobre 2010 à 17:34 +0800, Américo Wang a écrit :
> On Mon, Oct 04, 2010 at 11:04:18AM +0200, Eric Dumazet wrote:
> >Le lundi 04 octobre 2010 à 03:59 -0500, Robin Holt a écrit :
> >> On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
> >> > When proc_doulongvec_minmax() is used with an array of longs,
> >> > and no min/max check requested (.extra1 or .extra2 being NULL), we
> >> > dereference a NULL pointer for the second element of the array.
> >> > 
> >> > Noticed while doing some changes in network stack for the "16TB problem"
> >> > 
> >> > Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> >> > ---
> >> >  kernel/sysctl.c |    3 ++-
> >> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >> > 
> >> > diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> >> > index f88552c..4fba86d 100644
> >> > --- a/kernel/sysctl.c
> >> > +++ b/kernel/sysctl.c
> >> > @@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
> >> >  				break;
> >> >  			if (neg)
> >> >  				continue;
> >> > -			if ((min && val < *min) || (max && val > *max))
> >> > +			if ((table->extra1 && val < *min) ||
> >> > +			    (table->extra2 && val > *max))
> >> 
> >> How about changing:
> >>         for (; left && vleft--; i++, min++, max++, first=0) {
> >> into:
> >>         for (; left && vleft--; i++, min = min ? min + 1 : NULL, max = max ? max + 1: NULL, first=0) {
> >> 
> >> That would make min and max correct and reduce the chances somebody in
> >> the future overlooks the fact they are currently filled with garbage.
> >
> >I prefer my solution, because the check is done only in the 'write'
> >case, while its done also for 'read' in your solution, not counting the
> >for (;;) is really ugly...
> >
> 
> Sorry, I still don't get the point here, min and max
> are pointers, they are already checked before dereferenced.
> After your patch, min and max will be still increased, while
> you are still checking ->extra{1,2} which is wrong.
> 
> I see no problem with the original code, or I must have missed something?

Please re-read again. I had crashes, so original code is bugyy.

Say you call __do_proc_doulongvec_minmax() with an array of three
elements. And .extra1 = NULL, .extra2 = NULL (no range checks, this is a
valid use case)

First element, min = NULL OK. no problem so far.

Second element, min = (long *)(NULL + sizeof(long))   -> BUG 

Third element, min = (long *)(NULL + 2*sizeof(long)) -> BUG 

After my patch, min/max increases normally (they are only pointers after
all)

But they are _dereferenced_ only if they were _not_ NULL at the
beginning (extra1 not NULL for *min, extra2 not NULL for *max)




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04 10:10       ` Eric Dumazet
@ 2010-10-04 10:35         ` Américo Wang
  2010-10-04 10:38           ` Eric Dumazet
  0 siblings, 1 reply; 18+ messages in thread
From: Américo Wang @ 2010-10-04 10:35 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Américo Wang, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Hideaki YOSHIFUJI, Pekka Savola (ipv6), Patrick McHardy,
	Alexey Kuznetsov

On Mon, Oct 04, 2010 at 12:10:30PM +0200, Eric Dumazet wrote:
>Le lundi 04 octobre 2010 à 17:34 +0800, Américo Wang a écrit :
>> On Mon, Oct 04, 2010 at 11:04:18AM +0200, Eric Dumazet wrote:
>> >Le lundi 04 octobre 2010 à 03:59 -0500, Robin Holt a écrit :
>> >> On Sat, Oct 02, 2010 at 03:17:49PM +0200, Eric Dumazet wrote:
>> >> > When proc_doulongvec_minmax() is used with an array of longs,
>> >> > and no min/max check requested (.extra1 or .extra2 being NULL), we
>> >> > dereference a NULL pointer for the second element of the array.
>> >> > 
>> >> > Noticed while doing some changes in network stack for the "16TB problem"
>> >> > 
>> >> > Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>> >> > ---
>> >> >  kernel/sysctl.c |    3 ++-
>> >> >  1 file changed, 2 insertions(+), 1 deletion(-)
>> >> > 
>> >> > diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>> >> > index f88552c..4fba86d 100644
>> >> > --- a/kernel/sysctl.c
>> >> > +++ b/kernel/sysctl.c
>> >> > @@ -2500,7 +2500,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>> >> >  				break;
>> >> >  			if (neg)
>> >> >  				continue;
>> >> > -			if ((min && val < *min) || (max && val > *max))
>> >> > +			if ((table->extra1 && val < *min) ||
>> >> > +			    (table->extra2 && val > *max))
>> >> 
>> >> How about changing:
>> >>         for (; left && vleft--; i++, min++, max++, first=0) {
>> >> into:
>> >>         for (; left && vleft--; i++, min = min ? min + 1 : NULL, max = max ? max + 1: NULL, first=0) {
>> >> 
>> >> That would make min and max correct and reduce the chances somebody in
>> >> the future overlooks the fact they are currently filled with garbage.
>> >
>> >I prefer my solution, because the check is done only in the 'write'
>> >case, while its done also for 'read' in your solution, not counting the
>> >for (;;) is really ugly...
>> >
>> 
>> Sorry, I still don't get the point here, min and max
>> are pointers, they are already checked before dereferenced.
>> After your patch, min and max will be still increased, while
>> you are still checking ->extra{1,2} which is wrong.
>> 
>> I see no problem with the original code, or I must have missed something?
>
>Please re-read again. I had crashes, so original code is bugyy.
>
>Say you call __do_proc_doulongvec_minmax() with an array of three
>elements. And .extra1 = NULL, .extra2 = NULL (no range checks, this is a
>valid use case)
>
>First element, min = NULL OK. no problem so far.
>
>Second element, min = (long *)(NULL + sizeof(long))   -> BUG 
>
>Third element, min = (long *)(NULL + 2*sizeof(long)) -> BUG 
>
>After my patch, min/max increases normally (they are only pointers after
>all)
>
>But they are _dereferenced_ only if they were _not_ NULL at the
>beginning (extra1 not NULL for *min, extra2 not NULL for *max)
>

Hmm, I see, thanks for explanation.

Your patch does fix the problem, but seems not a good solution,
we should skip all min/max checking if ->extra(1|2) is NULL,
instead of checking it every time within the loop.

Thanks.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04 10:35         ` Américo Wang
@ 2010-10-04 10:38           ` Eric Dumazet
  2010-10-05 13:01             ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Eric Dumazet @ 2010-10-04 10:38 UTC (permalink / raw)
  To: Américo Wang
  Cc: Robin Holt, Andrew Morton, linux-kernel, Willy Tarreau,
	David S. Miller, netdev, James Morris, Hideaki YOSHIFUJI,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

Le lundi 04 octobre 2010 à 18:35 +0800, Américo Wang a écrit :

> Your patch does fix the problem, but seems not a good solution,
> we should skip all min/max checking if ->extra(1|2) is NULL,
> instead of checking it every time within the loop.

Please do submit a patch, we'll see if you come to a better solution,
with no added code size (this is slow path, I dont care for checking it
'every time winthin the loop')








^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-04 10:38           ` Eric Dumazet
@ 2010-10-05 13:01             ` Américo Wang
  2010-10-07  7:18               ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Américo Wang @ 2010-10-05 13:01 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Américo Wang, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Hideaki YOSHIFUJI, Pekka Savola (ipv6), Patrick McHardy,
	Alexey Kuznetsov

On Mon, Oct 04, 2010 at 12:38:21PM +0200, Eric Dumazet wrote:
>Le lundi 04 octobre 2010 à 18:35 +0800, Américo Wang a écrit :
>
>> Your patch does fix the problem, but seems not a good solution,
>> we should skip all min/max checking if ->extra(1|2) is NULL,
>> instead of checking it every time within the loop.
>
>Please do submit a patch, we'll see if you come to a better solution,
>with no added code size (this is slow path, I dont care for checking it
>'every time winthin the loop')
>
>

I have one, but just did compile test. :)
I will test it tomorrow.

NOT-Signed-off-by: WANG Cong <xiyou.wangcong@gmail.com>

---

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..345a193 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2448,86 +2448,119 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
 				do_proc_dointvec_minmax_conv, &param);
 }
 
-static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
-				     void __user *buffer,
+static int __doulongvec_minmax_read(void *data, void __user *buffer,
 				     size_t *lenp, loff_t *ppos,
 				     unsigned long convmul,
 				     unsigned long convdiv)
 {
-	unsigned long *i, *min, *max;
-	int vleft, first = 1, err = 0;
-	unsigned long page = 0;
-	size_t left;
-	char *kbuf;
+	unsigned long *i = (unsigned long *) data;
+	int err = 0;
+	bool first = true;
+	size_t left = *lenp;
 
-	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
-		*lenp = 0;
-		return 0;
+	for (; left; i++, first=false) {
+		unsigned long val;
+
+		val = convdiv * (*i) / convmul;
+		if (!first)
+			err = proc_put_char(&buffer, &left, '\t');
+		err = proc_put_long(&buffer, &left, val, false);
+		if (err)
+			break;
 	}
 
-	i = (unsigned long *) data;
-	min = (unsigned long *) table->extra1;
-	max = (unsigned long *) table->extra2;
-	vleft = table->maxlen / sizeof(unsigned long);
-	left = *lenp;
+	if (!first && left && !err)
+		err = proc_put_char(&buffer, &left, '\n');
 
-	if (write) {
-		if (left > PAGE_SIZE - 1)
-			left = PAGE_SIZE - 1;
-		page = __get_free_page(GFP_TEMPORARY);
-		kbuf = (char *) page;
-		if (!kbuf)
-			return -ENOMEM;
-		if (copy_from_user(kbuf, buffer, left)) {
-			err = -EFAULT;
-			goto free;
-		}
-		kbuf[left] = 0;
+	*lenp -= left;
+	*ppos += *lenp;
+	return err;
+}
+
+static int __doulongvec_minmax_write(void *data, void __user *buffer,
+				     size_t *lenp, loff_t *ppos, int vleft,
+				     unsigned long min, unsigned long max)
+{
+	char *kbuf;
+	size_t left = *lenp;
+	unsigned long page = 0;
+	unsigned long *i = (unsigned long *) data;
+	int err = 0;
+	bool first = true;
+
+	if (left > PAGE_SIZE - 1)
+		left = PAGE_SIZE - 1;
+	page = __get_free_page(GFP_TEMPORARY);
+	kbuf = (char *) page;
+	if (!kbuf)
+		return -ENOMEM;
+	if (copy_from_user(kbuf, buffer, left)) {
+		err = -EFAULT;
+		goto free;
 	}
+	kbuf[left] = 0;
 
-	for (; left && vleft--; i++, min++, max++, first=0) {
+	for (; left && vleft--; i++, min++, max++, first=false) {
 		unsigned long val;
+		bool neg;
 
-		if (write) {
-			bool neg;
-
-			left -= proc_skip_spaces(&kbuf);
+		left -= proc_skip_spaces(&kbuf);
 
-			err = proc_get_long(&kbuf, &left, &val, &neg,
-					     proc_wspace_sep,
-					     sizeof(proc_wspace_sep), NULL);
-			if (err)
-				break;
-			if (neg)
-				continue;
-			if ((min && val < *min) || (max && val > *max))
-				continue;
-			*i = val;
-		} else {
-			val = convdiv * (*i) / convmul;
-			if (!first)
-				err = proc_put_char(&buffer, &left, '\t');
-			err = proc_put_long(&buffer, &left, val, false);
-			if (err)
-				break;
-		}
+		err = proc_get_long(&kbuf, &left, &val, &neg,
+				     proc_wspace_sep,
+				     sizeof(proc_wspace_sep), NULL);
+		if (err)
+			break;
+		if (neg)
+			continue;
+		if (val < min || val > max)
+			continue;
+		*i = val;
 	}
 
-	if (!write && !first && left && !err)
-		err = proc_put_char(&buffer, &left, '\n');
-	if (write && !err)
+	if (!err)
 		left -= proc_skip_spaces(&kbuf);
 free:
-	if (write) {
-		free_page(page);
-		if (first)
-			return err ? : -EINVAL;
-	}
+	free_page(page);
+	if (first)
+		return err ? : -EINVAL;
+
 	*lenp -= left;
 	*ppos += *lenp;
 	return err;
 }
 
+static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
+				     void __user *buffer,
+				     size_t *lenp, loff_t *ppos,
+				     unsigned long convmul,
+				     unsigned long convdiv)
+{
+	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+		unsigned long min, max;
+		int vleft;
+
+		vleft = table->maxlen / sizeof(unsigned long);
+		if (table->extra1)
+			min = *(unsigned long *) table->extra1;
+		else
+			min = 0;
+		if (table->extra2)
+			max = *(unsigned long *) table->extra2;
+		else
+			max = ULONG_MAX;
+		return __doulongvec_minmax_write(data, buffer, lenp,
+						  ppos, vleft, min, max);
+	} else
+		return __doulongvec_minmax_read(data, buffer, lenp,
+						 ppos, convmul, convdiv);
+}
+
 static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
 				     void __user *buffer,
 				     size_t *lenp, loff_t *ppos,

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-05 13:01             ` Américo Wang
@ 2010-10-07  7:18               ` Américo Wang
  2010-10-07  9:25                 ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Américo Wang @ 2010-10-07  7:18 UTC (permalink / raw)
  To: Américo Wang
  Cc: Eric Dumazet, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov, ebiederm

On Tue, Oct 05, 2010 at 09:01:17PM +0800, Américo Wang wrote:
>On Mon, Oct 04, 2010 at 12:38:21PM +0200, Eric Dumazet wrote:
>>Le lundi 04 octobre 2010 à 18:35 +0800, Américo Wang a écrit :
>>
>>> Your patch does fix the problem, but seems not a good solution,
>>> we should skip all min/max checking if ->extra(1|2) is NULL,
>>> instead of checking it every time within the loop.
>>
>>Please do submit a patch, we'll see if you come to a better solution,
>>with no added code size (this is slow path, I dont care for checking it
>>'every time winthin the loop')
>>
>>
>
>I have one, but just did compile test. :)
>I will test it tomorrow.
>

Here is the final one.

--------------->

Eric D. noticed that we may trigger an OOPS if we leave ->extra{1,2}
to NULL when we use proc_doulongvec_minmax().

Actually, we don't need to store min/max values in a vector,
because all the elements in the vector should share the same min/max
value, like what proc_dointvec_minmax() does.

This also shrinks the binary size a little bit.

   text    data     bss     dec     hex filename
  12419    8744    4016   25179    625b kernel/sysctl.o.BEFORE
  12395    8744    4024   25163    624b kernel/sysctl.o.AFTER

Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Eric W. <ebiederm@xmission.com>
Signed-off-by: WANG Cong <xiyou.wangcong@gmail.com>

---
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..ba5e511 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2448,86 +2448,119 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
 				do_proc_dointvec_minmax_conv, &param);
 }
 
-static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
-				     void __user *buffer,
+static int __doulongvec_minmax_read(void *data, void __user *buffer,
 				     size_t *lenp, loff_t *ppos,
 				     unsigned long convmul,
 				     unsigned long convdiv)
 {
-	unsigned long *i, *min, *max;
-	int vleft, first = 1, err = 0;
-	unsigned long page = 0;
-	size_t left;
-	char *kbuf;
+	unsigned long *i = (unsigned long *) data;
+	int err = 0;
+	bool first = true;
+	size_t left = *lenp;
 
-	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
-		*lenp = 0;
-		return 0;
+	for (; left; i++, first=false) {
+		unsigned long val;
+
+		val = convdiv * (*i) / convmul;
+		if (!first)
+			err = proc_put_char(&buffer, &left, '\t');
+		err = proc_put_long(&buffer, &left, val, false);
+		if (err)
+			break;
 	}
 
-	i = (unsigned long *) data;
-	min = (unsigned long *) table->extra1;
-	max = (unsigned long *) table->extra2;
-	vleft = table->maxlen / sizeof(unsigned long);
-	left = *lenp;
+	if (!first && left && !err)
+		err = proc_put_char(&buffer, &left, '\n');
 
-	if (write) {
-		if (left > PAGE_SIZE - 1)
-			left = PAGE_SIZE - 1;
-		page = __get_free_page(GFP_TEMPORARY);
-		kbuf = (char *) page;
-		if (!kbuf)
-			return -ENOMEM;
-		if (copy_from_user(kbuf, buffer, left)) {
-			err = -EFAULT;
-			goto free;
-		}
-		kbuf[left] = 0;
+	*lenp -= left;
+	*ppos += *lenp;
+	return err;
+}
+
+static int __doulongvec_minmax_write(void *data, void __user *buffer,
+				     size_t *lenp, loff_t *ppos, int vleft,
+				     unsigned long min, unsigned long max)
+{
+	char *kbuf;
+	size_t left = *lenp;
+	unsigned long page = 0;
+	unsigned long *i = (unsigned long *) data;
+	int err = 0;
+	bool first = true;
+
+	if (left > PAGE_SIZE - 1)
+		left = PAGE_SIZE - 1;
+	page = __get_free_page(GFP_TEMPORARY);
+	kbuf = (char *) page;
+	if (!kbuf)
+		return -ENOMEM;
+	if (copy_from_user(kbuf, buffer, left)) {
+		err = -EFAULT;
+		goto free;
 	}
+	kbuf[left] = 0;
 
-	for (; left && vleft--; i++, min++, max++, first=0) {
+	for (; left && vleft--; i++, first=false) {
 		unsigned long val;
+		bool neg;
 
-		if (write) {
-			bool neg;
-
-			left -= proc_skip_spaces(&kbuf);
+		left -= proc_skip_spaces(&kbuf);
 
-			err = proc_get_long(&kbuf, &left, &val, &neg,
-					     proc_wspace_sep,
-					     sizeof(proc_wspace_sep), NULL);
-			if (err)
-				break;
-			if (neg)
-				continue;
-			if ((min && val < *min) || (max && val > *max))
-				continue;
-			*i = val;
-		} else {
-			val = convdiv * (*i) / convmul;
-			if (!first)
-				err = proc_put_char(&buffer, &left, '\t');
-			err = proc_put_long(&buffer, &left, val, false);
-			if (err)
-				break;
-		}
+		err = proc_get_long(&kbuf, &left, &val, &neg,
+				     proc_wspace_sep,
+				     sizeof(proc_wspace_sep), NULL);
+		if (err)
+			break;
+		if (neg)
+			continue;
+		if (val < min || val > max)
+			continue;
+		*i = val;
 	}
 
-	if (!write && !first && left && !err)
-		err = proc_put_char(&buffer, &left, '\n');
-	if (write && !err)
+	if (!err)
 		left -= proc_skip_spaces(&kbuf);
 free:
-	if (write) {
-		free_page(page);
-		if (first)
-			return err ? : -EINVAL;
-	}
+	free_page(page);
+	if (first)
+		return err ? : -EINVAL;
+
 	*lenp -= left;
 	*ppos += *lenp;
 	return err;
 }
 
+static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
+				     void __user *buffer,
+				     size_t *lenp, loff_t *ppos,
+				     unsigned long convmul,
+				     unsigned long convdiv)
+{
+	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+		unsigned long min, max;
+		int vleft;
+
+		vleft = table->maxlen / sizeof(unsigned long);
+		if (table->extra1)
+			min = *(unsigned long *) table->extra1;
+		else
+			min = 0;
+		if (table->extra2)
+			max = *(unsigned long *) table->extra2;
+		else
+			max = ULONG_MAX;
+		return __doulongvec_minmax_write(data, buffer, lenp,
+						  ppos, vleft, min, max);
+	} else
+		return __doulongvec_minmax_read(data, buffer, lenp,
+						 ppos, convmul, convdiv);
+}
+
 static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
 				     void __user *buffer,
 				     size_t *lenp, loff_t *ppos,

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07  7:18               ` Américo Wang
@ 2010-10-07  9:25                 ` Américo Wang
  2010-10-07  9:51                   ` Eric Dumazet
  0 siblings, 1 reply; 18+ messages in thread
From: Américo Wang @ 2010-10-07  9:25 UTC (permalink / raw)
  To: Américo Wang
  Cc: Eric Dumazet, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov, ebiederm

>>
>
>Here is the final one.

Oops, that one is not correct. Hopefully this one
is correct.

--------------->

Eric D. noticed that we may trigger an OOPS if we leave ->extra{1,2}
to NULL when we use proc_doulongvec_minmax().

Actually, we don't need to store min/max values in a vector,
because all the elements in the vector should share the same min/max
value, like what proc_dointvec_minmax() does.

Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Eric W. <ebiederm@xmission.com>
Signed-off-by: WANG Cong <xiyou.wangcong@gmail.com>

---
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..fad9208 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2448,86 +2448,119 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
 				do_proc_dointvec_minmax_conv, &param);
 }
 
-static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
-				     void __user *buffer,
-				     size_t *lenp, loff_t *ppos,
+static int __doulongvec_minmax_read(void *data, void __user *buffer,
+				     size_t *lenp, loff_t *ppos, int vleft,
 				     unsigned long convmul,
 				     unsigned long convdiv)
 {
-	unsigned long *i, *min, *max;
-	int vleft, first = 1, err = 0;
-	unsigned long page = 0;
-	size_t left;
-	char *kbuf;
+	unsigned long *i = data;
+	int err = 0;
+	bool first = true;
+	size_t left = *lenp;
 
-	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
-		*lenp = 0;
-		return 0;
+	for (; left && vleft--; i++, first=false) {
+		unsigned long val;
+
+		val = convdiv * (*i) / convmul;
+		if (!first)
+			err = proc_put_char(&buffer, &left, '\t');
+		err = proc_put_long(&buffer, &left, val, false);
+		if (err)
+			break;
 	}
 
-	i = (unsigned long *) data;
-	min = (unsigned long *) table->extra1;
-	max = (unsigned long *) table->extra2;
-	vleft = table->maxlen / sizeof(unsigned long);
-	left = *lenp;
+	if (!first && left && !err)
+		err = proc_put_char(&buffer, &left, '\n');
 
-	if (write) {
-		if (left > PAGE_SIZE - 1)
-			left = PAGE_SIZE - 1;
-		page = __get_free_page(GFP_TEMPORARY);
-		kbuf = (char *) page;
-		if (!kbuf)
-			return -ENOMEM;
-		if (copy_from_user(kbuf, buffer, left)) {
-			err = -EFAULT;
-			goto free;
-		}
-		kbuf[left] = 0;
+	*lenp -= left;
+	*ppos += *lenp;
+	return err;
+}
+
+static int __doulongvec_minmax_write(void *data, void __user *buffer,
+				     size_t *lenp, loff_t *ppos, int vleft,
+				     unsigned long min, unsigned long max)
+{
+	char *kbuf;
+	size_t left = *lenp;
+	unsigned long page = 0;
+	unsigned long *i = (unsigned long *) data;
+	int err = 0;
+	bool first = true;
+
+	if (left > PAGE_SIZE - 1)
+		left = PAGE_SIZE - 1;
+	page = __get_free_page(GFP_TEMPORARY);
+	kbuf = (char *) page;
+	if (!kbuf)
+		return -ENOMEM;
+	if (copy_from_user(kbuf, buffer, left)) {
+		err = -EFAULT;
+		goto free;
 	}
+	kbuf[left] = 0;
 
-	for (; left && vleft--; i++, min++, max++, first=0) {
+	for (; left && vleft--; i++, first=false) {
 		unsigned long val;
+		bool neg;
 
-		if (write) {
-			bool neg;
-
-			left -= proc_skip_spaces(&kbuf);
+		left -= proc_skip_spaces(&kbuf);
 
-			err = proc_get_long(&kbuf, &left, &val, &neg,
-					     proc_wspace_sep,
-					     sizeof(proc_wspace_sep), NULL);
-			if (err)
-				break;
-			if (neg)
-				continue;
-			if ((min && val < *min) || (max && val > *max))
-				continue;
-			*i = val;
-		} else {
-			val = convdiv * (*i) / convmul;
-			if (!first)
-				err = proc_put_char(&buffer, &left, '\t');
-			err = proc_put_long(&buffer, &left, val, false);
-			if (err)
-				break;
-		}
+		err = proc_get_long(&kbuf, &left, &val, &neg,
+				     proc_wspace_sep,
+				     sizeof(proc_wspace_sep), NULL);
+		if (err)
+			break;
+		if (neg)
+			continue;
+		if (val < min || val > max)
+			continue;
+		*i = val;
 	}
 
-	if (!write && !first && left && !err)
-		err = proc_put_char(&buffer, &left, '\n');
-	if (write && !err)
+	if (!err)
 		left -= proc_skip_spaces(&kbuf);
 free:
-	if (write) {
-		free_page(page);
-		if (first)
-			return err ? : -EINVAL;
-	}
+	free_page(page);
+	if (first)
+		return err ? : -EINVAL;
+
 	*lenp -= left;
 	*ppos += *lenp;
 	return err;
 }
 
+static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
+				     void __user *buffer,
+				     size_t *lenp, loff_t *ppos,
+				     unsigned long convmul,
+				     unsigned long convdiv)
+{
+	int vleft;
+	if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	vleft = table->maxlen / sizeof(unsigned long);
+	if (write) {
+		unsigned long min, max;
+
+		if (table->extra1)
+			min = *(unsigned long *) table->extra1;
+		else
+			min = 0;
+		if (table->extra2)
+			max = *(unsigned long *) table->extra2;
+		else
+			max = ULONG_MAX;
+		return __doulongvec_minmax_write(data, buffer, lenp,
+						  ppos, vleft, min, max);
+	} else
+		return __doulongvec_minmax_read(data, buffer, lenp,
+						 ppos, vleft, convmul, convdiv);
+}
+
 static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
 				     void __user *buffer,
 				     size_t *lenp, loff_t *ppos,

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07  9:25                 ` Américo Wang
@ 2010-10-07  9:51                   ` Eric Dumazet
  2010-10-07 16:37                     ` Eric W. Biederman
  2010-10-08 16:13                     ` Américo Wang
  0 siblings, 2 replies; 18+ messages in thread
From: Eric Dumazet @ 2010-10-07  9:51 UTC (permalink / raw)
  To: Américo Wang
  Cc: Robin Holt, Andrew Morton, linux-kernel, Willy Tarreau,
	David S. Miller, netdev, James Morris, Pekka Savola (ipv6),
	Patrick McHardy, Alexey Kuznetsov, ebiederm

Le jeudi 07 octobre 2010 à 17:25 +0800, Américo Wang a écrit :
> >>
> >
> >Here is the final one.
> 
> Oops, that one is not correct. Hopefully this one
> is correct.
> 
> --------------->
> 
> Eric D. noticed that we may trigger an OOPS if we leave ->extra{1,2}
> to NULL when we use proc_doulongvec_minmax().
> 
> Actually, we don't need to store min/max values in a vector,
> because all the elements in the vector should share the same min/max
> value, like what proc_dointvec_minmax() does.
> 

If we assert same min/max limits are to be applied to all elements,
a much simpler fix than yours would be :

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..8e45451 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
 		kbuf[left] = 0;
 	}
 
-	for (; left && vleft--; i++, min++, max++, first=0) {
+	for (; left && vleft--; i++, first=0) {
 		unsigned long val;
 
 		if (write) {


Please dont send huge patches like this to 'fix' a bug,
especially on slow path.

First we fix the bug, _then_ we can try to make code more 
efficient or more pretty or shorter.

So the _real_ question is :

Should the min/max limits should be a single pair,
shared by all elements, or a vector of limits.



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07  9:51                   ` Eric Dumazet
@ 2010-10-07 16:37                     ` Eric W. Biederman
  2010-10-07 16:59                       ` Eric Dumazet
  2010-10-08 16:13                     ` Américo Wang
  1 sibling, 1 reply; 18+ messages in thread
From: Eric W. Biederman @ 2010-10-07 16:37 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Américo Wang, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

Eric Dumazet <eric.dumazet@gmail.com> writes:

> Le jeudi 07 octobre 2010 à 17:25 +0800, Américo Wang a écrit :
>> >>
>> >
>> >Here is the final one.
>> 
>> Oops, that one is not correct. Hopefully this one
>> is correct.
>> 
>> --------------->
>> 
>> Eric D. noticed that we may trigger an OOPS if we leave ->extra{1,2}
>> to NULL when we use proc_doulongvec_minmax().
>> 
>> Actually, we don't need to store min/max values in a vector,
>> because all the elements in the vector should share the same min/max
>> value, like what proc_dointvec_minmax() does.
>> 
>
> If we assert same min/max limits are to be applied to all elements,
> a much simpler fix than yours would be :
>
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index f88552c..8e45451 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>  		kbuf[left] = 0;
>  	}
>  
> -	for (; left && vleft--; i++, min++, max++, first=0) {
> +	for (; left && vleft--; i++, first=0) {
>  		unsigned long val;
>  
>  		if (write) {
>
>
> Please dont send huge patches like this to 'fix' a bug,
> especially on slow path.
>
> First we fix the bug, _then_ we can try to make code more 
> efficient or more pretty or shorter.
>
> So the _real_ question is :
>
> Should the min/max limits should be a single pair,
> shared by all elements, or a vector of limits.

The difference between long handling and int handling is a
usability issue.  I don't expect we will be exporting new
vectors via sysctl, so the conversion of a handful of vectors
from int to long is where this is most likely to be used.

I skimmed through all of what I presume are the current users
aka linux-2.6.36-rcX and there don't appear to be any users
of proc_dounlongvec_minmax that use it's vector properties there.

Which doubly tells me that incrementing the min and max pointers
is not what we want to do.

Eric

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07 16:37                     ` Eric W. Biederman
@ 2010-10-07 16:59                       ` Eric Dumazet
  2010-10-07 19:18                         ` Andrew Morton
  0 siblings, 1 reply; 18+ messages in thread
From: Eric Dumazet @ 2010-10-07 16:59 UTC (permalink / raw)
  To: Eric W. Biederman, Andrew Morton
  Cc: Américo Wang, Robin Holt, linux-kernel, Willy Tarreau,
	David S. Miller, netdev, James Morris, Pekka Savola (ipv6),
	Patrick McHardy, Alexey Kuznetsov

Le jeudi 07 octobre 2010 à 09:37 -0700, Eric W. Biederman a écrit :

> The difference between long handling and int handling is a
> usability issue.  I don't expect we will be exporting new
> vectors via sysctl, so the conversion of a handful of vectors
> from int to long is where this is most likely to be used.
> 
> I skimmed through all of what I presume are the current users
> aka linux-2.6.36-rcX and there don't appear to be any users
> of proc_dounlongvec_minmax that use it's vector properties there.
> 
> Which doubly tells me that incrementing the min and max pointers
> is not what we want to do.
> 

Thats fine by me, thanks Eric.

Andrew, please remove previous patch from your tree and replace it by
following one :

[PATCH v2] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()

When proc_doulongvec_minmax() is used with an array of longs,
and no min/max check requested (.extra1 or .extra2 being NULL), we
dereference a NULL pointer for the second element of the array.

Noticed while doing some changes in network stack for the "16TB problem"

Fix is to not change min & max pointers in
__do_proc_doulongvec_minmax(), so that all elements of the vector share
an unique min/max limit, like proc_dointvec_minmax().

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 kernel/sysctl.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..8e45451 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
 		kbuf[left] = 0;
 	}
 
-	for (; left && vleft--; i++, min++, max++, first=0) {
+	for (; left && vleft--; i++, first=0) {
 		unsigned long val;
 
 		if (write) {

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07 16:59                       ` Eric Dumazet
@ 2010-10-07 19:18                         ` Andrew Morton
  2010-10-07 19:38                           ` Eric W. Biederman
  0 siblings, 1 reply; 18+ messages in thread
From: Andrew Morton @ 2010-10-07 19:18 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Eric W. Biederman, Américo Wang, Robin Holt, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

On Thu, 07 Oct 2010 18:59:03 +0200
Eric Dumazet <eric.dumazet@gmail.com> wrote:

> Le jeudi 07 octobre 2010 __ 09:37 -0700, Eric W. Biederman a __crit :
> 
> > The difference between long handling and int handling is a
> > usability issue.  I don't expect we will be exporting new
> > vectors via sysctl, so the conversion of a handful of vectors
> > from int to long is where this is most likely to be used.
> > 
> > I skimmed through all of what I presume are the current users
> > aka linux-2.6.36-rcX and there don't appear to be any users
> > of proc_dounlongvec_minmax that use it's vector properties there.
> > 
> > Which doubly tells me that incrementing the min and max pointers
> > is not what we want to do.
> > 
> 
> Thats fine by me, thanks Eric.
> 
> Andrew, please remove previous patch from your tree and replace it by
> following one :
> 
> [PATCH v2] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
> 
> When proc_doulongvec_minmax() is used with an array of longs,
> and no min/max check requested (.extra1 or .extra2 being NULL), we
> dereference a NULL pointer for the second element of the array.
> 
> Noticed while doing some changes in network stack for the "16TB problem"
> 
> Fix is to not change min & max pointers in
> __do_proc_doulongvec_minmax(), so that all elements of the vector share
> an unique min/max limit, like proc_dointvec_minmax().
> 
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
>  kernel/sysctl.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index f88552c..8e45451 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>  		kbuf[left] = 0;
>  	}
>  
> -	for (; left && vleft--; i++, min++, max++, first=0) {
> +	for (; left && vleft--; i++, first=0) {
>  		unsigned long val;
>  
>  		if (write) {

Did we check to see whether any present callers are passing in pointers
to arrays of min/max values?

I wonder if there's any documentation for this interface which just
became wrong.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07 19:18                         ` Andrew Morton
@ 2010-10-07 19:38                           ` Eric W. Biederman
  2010-10-08 16:22                             ` Américo Wang
  0 siblings, 1 reply; 18+ messages in thread
From: Eric W. Biederman @ 2010-10-07 19:38 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Eric Dumazet, Américo Wang, Robin Holt, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov

Andrew Morton <akpm@linux-foundation.org> writes:

> On Thu, 07 Oct 2010 18:59:03 +0200
> Eric Dumazet <eric.dumazet@gmail.com> wrote:

>> Thats fine by me, thanks Eric.
>> 
>> Andrew, please remove previous patch from your tree and replace it by
>> following one :
>> 
>> [PATCH v2] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
>> 
>> When proc_doulongvec_minmax() is used with an array of longs,
>> and no min/max check requested (.extra1 or .extra2 being NULL), we
>> dereference a NULL pointer for the second element of the array.
>> 
>> Noticed while doing some changes in network stack for the "16TB problem"
>> 
>> Fix is to not change min & max pointers in
>> __do_proc_doulongvec_minmax(), so that all elements of the vector share
>> an unique min/max limit, like proc_dointvec_minmax().
>> 
>> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>> ---
>>  kernel/sysctl.c |    2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>> index f88552c..8e45451 100644
>> --- a/kernel/sysctl.c
>> +++ b/kernel/sysctl.c
>> @@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>>  		kbuf[left] = 0;
>>  	}
>>  
>> -	for (; left && vleft--; i++, min++, max++, first=0) {
>> +	for (; left && vleft--; i++, first=0) {
>>  		unsigned long val;
>>  
>>  		if (write) {
>
> Did we check to see whether any present callers are passing in pointers
> to arrays of min/max values?

In 2.6.36 there are not any callers that pass in a vector of anything, I
don't know about linux-next.  It looks to me like incrementing min and
max was simply a bug.

> I wonder if there's any documentation for this interface which just
> became wrong.

Or it just became right.  Clearly no one has been expecting min
and max to be vectors.

Eric

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07  9:51                   ` Eric Dumazet
  2010-10-07 16:37                     ` Eric W. Biederman
@ 2010-10-08 16:13                     ` Américo Wang
  1 sibling, 0 replies; 18+ messages in thread
From: Américo Wang @ 2010-10-08 16:13 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: Américo Wang, Robin Holt, Andrew Morton, linux-kernel,
	Willy Tarreau, David S. Miller, netdev, James Morris,
	Pekka Savola (ipv6), Patrick McHardy, Alexey Kuznetsov, ebiederm

On Thu, Oct 07, 2010 at 11:51:21AM +0200, Eric Dumazet wrote:
>Le jeudi 07 octobre 2010 à 17:25 +0800, Américo Wang a écrit :
>> >>
>> >
>> >Here is the final one.
>> 
>> Oops, that one is not correct. Hopefully this one
>> is correct.
>> 
>> --------------->
>> 
>> Eric D. noticed that we may trigger an OOPS if we leave ->extra{1,2}
>> to NULL when we use proc_doulongvec_minmax().
>> 
>> Actually, we don't need to store min/max values in a vector,
>> because all the elements in the vector should share the same min/max
>> value, like what proc_dointvec_minmax() does.
>> 
>
>If we assert same min/max limits are to be applied to all elements,
>a much simpler fix than yours would be :
>
>diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>index f88552c..8e45451 100644
>--- a/kernel/sysctl.c
>+++ b/kernel/sysctl.c
>@@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
> 		kbuf[left] = 0;
> 	}
> 
>-	for (; left && vleft--; i++, min++, max++, first=0) {
>+	for (; left && vleft--; i++, first=0) {
> 		unsigned long val;
> 
> 		if (write) {
>
>
>Please dont send huge patches like this to 'fix' a bug,
>especially on slow path.

Well, my patch makes that horrible code a little better. :)

>
>First we fix the bug, _then_ we can try to make code more 
>efficient or more pretty or shorter.
>
>So the _real_ question is :
>
>Should the min/max limits should be a single pair,
>shared by all elements, or a vector of limits.
>

Yes, actually I talked with Eric W. about this before
sending the patch.

I also checked the users of proc_doulongvec_minmax(),
none of them are using more than one limit, so it is
safe to remove that.


-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
  2010-10-07 19:38                           ` Eric W. Biederman
@ 2010-10-08 16:22                             ` Américo Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Américo Wang @ 2010-10-08 16:22 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Andrew Morton, Eric Dumazet, Américo Wang, Robin Holt,
	linux-kernel, Willy Tarreau, David S. Miller, netdev,
	James Morris, Pekka Savola (ipv6), Patrick McHardy,
	Alexey Kuznetsov

On Thu, Oct 07, 2010 at 12:38:22PM -0700, Eric W. Biederman wrote:
>Andrew Morton <akpm@linux-foundation.org> writes:
>
>> On Thu, 07 Oct 2010 18:59:03 +0200
>> Eric Dumazet <eric.dumazet@gmail.com> wrote:
>
>>> Thats fine by me, thanks Eric.
>>> 
>>> Andrew, please remove previous patch from your tree and replace it by
>>> following one :
>>> 
>>> [PATCH v2] sysctl: fix min/max handling in __do_proc_doulongvec_minmax()
>>> 
>>> When proc_doulongvec_minmax() is used with an array of longs,
>>> and no min/max check requested (.extra1 or .extra2 being NULL), we
>>> dereference a NULL pointer for the second element of the array.
>>> 
>>> Noticed while doing some changes in network stack for the "16TB problem"
>>> 
>>> Fix is to not change min & max pointers in
>>> __do_proc_doulongvec_minmax(), so that all elements of the vector share
>>> an unique min/max limit, like proc_dointvec_minmax().
>>> 
>>> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
>>> ---
>>>  kernel/sysctl.c |    2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>> 
>>> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>>> index f88552c..8e45451 100644
>>> --- a/kernel/sysctl.c
>>> +++ b/kernel/sysctl.c
>>> @@ -2485,7 +2485,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
>>>  		kbuf[left] = 0;
>>>  	}
>>>  
>>> -	for (; left && vleft--; i++, min++, max++, first=0) {
>>> +	for (; left && vleft--; i++, first=0) {
>>>  		unsigned long val;
>>>  
>>>  		if (write) {
>>
>> Did we check to see whether any present callers are passing in pointers
>> to arrays of min/max values?
>
>In 2.6.36 there are not any callers that pass in a vector of anything, I
>don't know about linux-next.  It looks to me like incrementing min and
>max was simply a bug.
>

Agreed, I checked them too.

>> I wonder if there's any documentation for this interface which just
>> became wrong.
>
>Or it just became right.  Clearly no one has been expecting min
>and max to be vectors.
>

I think we need to document this before we rewrite the code.

-- 
Live like a child, think like the god.
 

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2010-10-08 16:20 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-02 13:17 [PATCH] sysctl: fix min/max handling in __do_proc_doulongvec_minmax() Eric Dumazet
2010-10-04  3:09 ` Américo Wang
2010-10-04  8:59 ` Robin Holt
2010-10-04  9:04   ` Eric Dumazet
2010-10-04  9:34     ` Américo Wang
2010-10-04 10:10       ` Eric Dumazet
2010-10-04 10:35         ` Américo Wang
2010-10-04 10:38           ` Eric Dumazet
2010-10-05 13:01             ` Américo Wang
2010-10-07  7:18               ` Américo Wang
2010-10-07  9:25                 ` Américo Wang
2010-10-07  9:51                   ` Eric Dumazet
2010-10-07 16:37                     ` Eric W. Biederman
2010-10-07 16:59                       ` Eric Dumazet
2010-10-07 19:18                         ` Andrew Morton
2010-10-07 19:38                           ` Eric W. Biederman
2010-10-08 16:22                             ` Américo Wang
2010-10-08 16:13                     ` Américo Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).