net/ceph/osdmap.c: fix undefined behavior when using snprintf()
Subsystems (3)
PATCH 1/1 net/ceph/osdmap.c: fix undefined behavior when using snprintf()
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).
--- net/ceph/osdmap.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-)
net/ceph/osdmap.c +8 -19
@@ -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; }
Signature · 1 line
1.7.10.4
Quoted 5 lines · 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."
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.
Quoted 4 lines
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.
Quoted 9 lines
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?
Quoted 1 line
Signed-off-by Cong Ding <[email protected]>
Quoted 44 lines
--- net/ceph/osdmap.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-)net/ceph/osdmap.c +8
-19@@ -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; }
Quoted 30 lines · Alex Elder
Quoted 5 lines · 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."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.
Quoted 4 lines
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.Quoted 9 lines
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).
Quoted 8 lines
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
Quoted 34 lines · Cong Ding
Quoted 33 lines · Alex Elder
Quoted 5 lines · 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."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.
Quoted 4 lines
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.Quoted 9 lines
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.:
Quoted 7 lines
Quoted 5 lines
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
Quoted 31 lines
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