All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf()
@ 2013-01-22 19:20 Cong Ding
  2013-01-23 16:48 ` Alex Elder
  0 siblings, 1 reply; 4+ messages in thread
From: Cong Ding @ 2013-01-22 19:20 UTC (permalink / raw)
  To: Sage Weil, David S. Miller, ceph-devel, netdev, linux-kernel; +Cc: Cong Ding

The variable "str" is used as both the source and destination in function
snprintf(), which is undefined behavior based on C11. The original description
in C11 is:
	"If copying takes place between objects that
	overlap, the behavior is undefined."

And, the function of ceph_osdmap_state_str() is to return the osdmap state, so
it should return "doesn't exist" when all the conditions are not satisfied. I
fix it in this patch.

Based on C11, snprintf() does nothing if n==0:
	"If n is zero, nothing is written, and s may be a
	null pointer. Otherwise, output characters beyond
	the n-1st are discarded rather than being written to
	the array, and a null character is written at the
	end of the characters actually written into the
	array."
so I remove the unnecessary check of len (because it is not a busy path and
saves a few lines of code).

Signed-off-by: Cong Ding <dinggnu@gmail.com>
---
 net/ceph/osdmap.c |   27 ++++++++-------------------
 1 file changed, 8 insertions(+), 19 deletions(-)

diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index de73214..3131a99d3 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -13,26 +13,15 @@
 
 char *ceph_osdmap_state_str(char *str, int len, int state)
 {
-	int flag = 0;
-
-	if (!len)
-		goto done;
-
-	*str = '\0';
-	if (state) {
-		if (state & CEPH_OSD_EXISTS) {
-			snprintf(str, len, "exists");
-			flag = 1;
-		}
-		if (state & CEPH_OSD_UP) {
-			snprintf(str, len, "%s%s%s", str, (flag ? ", " : ""),
-				 "up");
-			flag = 1;
-		}
-	} else {
+	if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
+		snprintf(str, len, "exists, up");
+	else if (state & CEPH_OSD_EXISTS)
+		snprintf(str, len, "exists");
+	else if (state & CEPH_OSD_UP)
+		snprintf(str, len, "up");
+	else
 		snprintf(str, len, "doesn't exist");
-	}
-done:
+
 	return str;
 }
 
-- 
1.7.10.4


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

* Re: [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf()
  2013-01-22 19:20 [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf() Cong Ding
@ 2013-01-23 16:48 ` Alex Elder
  2013-01-23 17:41   ` Cong Ding
  0 siblings, 1 reply; 4+ messages in thread
From: Alex Elder @ 2013-01-23 16:48 UTC (permalink / raw)
  To: Cong Ding; +Cc: Sage Weil, David S. Miller, ceph-devel, netdev, linux-kernel

On 01/22/2013 01:20 PM, Cong Ding wrote:
> The variable "str" is used as both the source and destination in function
> snprintf(), which is undefined behavior based on C11. The original description
> in C11 is:
> 	"If copying takes place between objects that
> 	overlap, the behavior is undefined."

Yes, this was an ill-advised thing to do in this function.

In fact, the only place this function is used (in osdmap_show()),
the non-static buffer was not initialized before the call.  (It
might happen to work because the same stack space was getting
reused each time through the loop.  Eeeeew!)

This is just an awful couple of functions.

> And, the function of ceph_osdmap_state_str() is to return the osdmap state, so
> it should return "doesn't exist" when all the conditions are not satisfied. I
> fix it in this patch.
> 
> Based on C11, snprintf() does nothing if n==0:
> 	"If n is zero, nothing is written, and s may be a
> 	null pointer. Otherwise, output characters beyond
> 	the n-1st are discarded rather than being written to
> 	the array, and a null character is written at the
> 	end of the characters actually written into the
> 	array."
> so I remove the unnecessary check of len (because it is not a busy path and
> saves a few lines of code).

True.  But since you know it's not going to do anything why
not only make the call if len is non-zero?  I.e.:

	else if (len)
		snprintf(str, len, "doesn't exist");

With your permission I'll make this change and will commit
this for you.  OK?

> Signed-off-by: Cong Ding <dinggnu@gmail.com>

Reviewed-by: Alex Elder <elder@inktank.com>

> ---
>  net/ceph/osdmap.c |   27 ++++++++-------------------
>  1 file changed, 8 insertions(+), 19 deletions(-)
> 
> diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
> index de73214..3131a99d3 100644
> --- a/net/ceph/osdmap.c
> +++ b/net/ceph/osdmap.c
> @@ -13,26 +13,15 @@
>  
>  char *ceph_osdmap_state_str(char *str, int len, int state)
>  {
> -	int flag = 0;
> -
> -	if (!len)
> -		goto done;
> -
> -	*str = '\0';
> -	if (state) {
> -		if (state & CEPH_OSD_EXISTS) {
> -			snprintf(str, len, "exists");
> -			flag = 1;
> -		}
> -		if (state & CEPH_OSD_UP) {
> -			snprintf(str, len, "%s%s%s", str, (flag ? ", " : ""),
> -				 "up");
> -			flag = 1;
> -		}
> -	} else {
> +	if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
> +		snprintf(str, len, "exists, up");
> +	else if (state & CEPH_OSD_EXISTS)
> +		snprintf(str, len, "exists");
> +	else if (state & CEPH_OSD_UP)
> +		snprintf(str, len, "up");
> +	else
>  		snprintf(str, len, "doesn't exist");
> -	}
> -done:
> +
>  	return str;
>  }
>  
> 


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

* Re: [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf()
  2013-01-23 16:48 ` Alex Elder
@ 2013-01-23 17:41   ` Cong Ding
  2013-01-23 17:47     ` Alex Elder
  0 siblings, 1 reply; 4+ messages in thread
From: Cong Ding @ 2013-01-23 17:41 UTC (permalink / raw)
  To: Alex Elder; +Cc: Sage Weil, David S. Miller, ceph-devel, netdev, linux-kernel

On Wed, Jan 23, 2013 at 10:48:07AM -0600, Alex Elder wrote:
> On 01/22/2013 01:20 PM, Cong Ding wrote:
> > The variable "str" is used as both the source and destination in function
> > snprintf(), which is undefined behavior based on C11. The original description
> > in C11 is:
> > 	"If copying takes place between objects that
> > 	overlap, the behavior is undefined."
> 
> Yes, this was an ill-advised thing to do in this function.
> 
> In fact, the only place this function is used (in osdmap_show()),
> the non-static buffer was not initialized before the call.  (It
> might happen to work because the same stack space was getting
> reused each time through the loop.  Eeeeew!)
> 
> This is just an awful couple of functions.
> 
> > And, the function of ceph_osdmap_state_str() is to return the osdmap state, so
> > it should return "doesn't exist" when all the conditions are not satisfied. I
> > fix it in this patch.
> > 
> > Based on C11, snprintf() does nothing if n==0:
> > 	"If n is zero, nothing is written, and s may be a
> > 	null pointer. Otherwise, output characters beyond
> > 	the n-1st are discarded rather than being written to
> > 	the array, and a null character is written at the
> > 	end of the characters actually written into the
> > 	array."
> > so I remove the unnecessary check of len (because it is not a busy path and
> > saves a few lines of code).
> 
> True.  But since you know it's not going to do anything why
> not only make the call if len is non-zero?  I.e.:
> 
> 	else if (len)
> 		snprintf(str, len, "doesn't exist");
> 
> With your permission I'll make this change and will commit
> this for you.  OK?
It's fine, thanks. But I think it's better to check len in the beginning
because other conditions also call snprintf with parameter len. Like this:

	if (!len)
		return str;

	if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
		snprintf(str, len, "exists, up");
	else if (state & CEPH_OSD_EXISTS)
		snprintf(str, len, "exists");
	else if (state & CEPH_OSD_UP)
		snprintf(str, len, "up");
	else
		snprintf(str, len, "doesn't exist");

	return str;

or like this:

	if (len) {
		if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
			snprintf(str, len, "exists, up");
		else if (state & CEPH_OSD_EXISTS)
			snprintf(str, len, "exists");
		else if (state & CEPH_OSD_UP)
			snprintf(str, len, "up");
		else
			snprintf(str, len, "doesn't exist");
	}
	return str;

Thanks,
- cong


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

* Re: [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf()
  2013-01-23 17:41   ` Cong Ding
@ 2013-01-23 17:47     ` Alex Elder
  0 siblings, 0 replies; 4+ messages in thread
From: Alex Elder @ 2013-01-23 17:47 UTC (permalink / raw)
  To: Cong Ding; +Cc: Sage Weil, David S. Miller, ceph-devel, netdev, linux-kernel

On 01/23/2013 11:41 AM, Cong Ding wrote:
> On Wed, Jan 23, 2013 at 10:48:07AM -0600, Alex Elder wrote:
>> On 01/22/2013 01:20 PM, Cong Ding wrote:
>>> The variable "str" is used as both the source and destination in function
>>> snprintf(), which is undefined behavior based on C11. The original description
>>> in C11 is:
>>> 	"If copying takes place between objects that
>>> 	overlap, the behavior is undefined."
>>
>> Yes, this was an ill-advised thing to do in this function.
>>
>> In fact, the only place this function is used (in osdmap_show()),
>> the non-static buffer was not initialized before the call.  (It
>> might happen to work because the same stack space was getting
>> reused each time through the loop.  Eeeeew!)
>>
>> This is just an awful couple of functions.
>>
>>> And, the function of ceph_osdmap_state_str() is to return the osdmap state, so
>>> it should return "doesn't exist" when all the conditions are not satisfied. I
>>> fix it in this patch.
>>>
>>> Based on C11, snprintf() does nothing if n==0:
>>> 	"If n is zero, nothing is written, and s may be a
>>> 	null pointer. Otherwise, output characters beyond
>>> 	the n-1st are discarded rather than being written to
>>> 	the array, and a null character is written at the
>>> 	end of the characters actually written into the
>>> 	array."
>>> so I remove the unnecessary check of len (because it is not a busy path and
>>> saves a few lines of code).
>>
>> True.  But since you know it's not going to do anything why
>> not only make the call if len is non-zero?  I.e.:
>>
>> 	else if (len)
>> 		snprintf(str, len, "doesn't exist");
>>
>> With your permission I'll make this change and will commit
>> this for you.  OK?
> It's fine, thanks. But I think it's better to check len in the beginning
> because other conditions also call snprintf with parameter len. Like this:

OK.  I'll do this.  Thank you.		-Alex


> 	if (!len)
> 		return str;
> 
> 	if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
> 		snprintf(str, len, "exists, up");
> 	else if (state & CEPH_OSD_EXISTS)
> 		snprintf(str, len, "exists");
> 	else if (state & CEPH_OSD_UP)
> 		snprintf(str, len, "up");
> 	else
> 		snprintf(str, len, "doesn't exist");
> 
> 	return str;
> 
> or like this:
> 
> 	if (len) {
> 		if ((state & CEPH_OSD_EXISTS) && (state & CEPH_OSD_UP))
> 			snprintf(str, len, "exists, up");
> 		else if (state & CEPH_OSD_EXISTS)
> 			snprintf(str, len, "exists");
> 		else if (state & CEPH_OSD_UP)
> 			snprintf(str, len, "up");
> 		else
> 			snprintf(str, len, "doesn't exist");
> 	}
> 	return str;
> 
> Thanks,
> - cong
> 


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

end of thread, other threads:[~2013-01-23 17:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-22 19:20 [PATCH] net/ceph/osdmap.c: fix undefined behavior when using snprintf() Cong Ding
2013-01-23 16:48 ` Alex Elder
2013-01-23 17:41   ` Cong Ding
2013-01-23 17:47     ` Alex Elder

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.