Commit f796937a authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Linus Torvalds

[PATCH] Fix bounds check in vsnprintf, to allow for a 0 size and NULL buffer

This change allows callers to use a 0-byte buffer and a NULL buffer pointer
with vsnprintf, so it can be used to determine how large the resulting
formatted string will be.

Previously the code effectively treated a size of 0 as a size of 4G (on
32-bit systems), with other checks preventing it from actually trying to
emit the string - but the terminal \0 would still be written, which would
crash if the buffer is NULL.

This change changes the boundary check so that 'end' points to the putative
location of the terminal '\0', which is only written if size > 0.

vsnprintf still allows the buffer size to be set very large, to allow
unbounded buffer sizes (to implement sprintf, etc).

[akpm@osdl.org: fix long-vs-longlong confusion]
Signed-off-by: default avatarJeremy Fitzhardinge <jeremy@xensource.com>
Signed-off-by: default avatarChris Wright <chrisw@sous-sol.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 891c668b
...@@ -187,49 +187,49 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i ...@@ -187,49 +187,49 @@ static char * number(char * buf, char * end, unsigned long long num, int base, i
size -= precision; size -= precision;
if (!(type&(ZEROPAD+LEFT))) { if (!(type&(ZEROPAD+LEFT))) {
while(size-->0) { while(size-->0) {
if (buf <= end) if (buf < end)
*buf = ' '; *buf = ' ';
++buf; ++buf;
} }
} }
if (sign) { if (sign) {
if (buf <= end) if (buf < end)
*buf = sign; *buf = sign;
++buf; ++buf;
} }
if (type & SPECIAL) { if (type & SPECIAL) {
if (base==8) { if (base==8) {
if (buf <= end) if (buf < end)
*buf = '0'; *buf = '0';
++buf; ++buf;
} else if (base==16) { } else if (base==16) {
if (buf <= end) if (buf < end)
*buf = '0'; *buf = '0';
++buf; ++buf;
if (buf <= end) if (buf < end)
*buf = digits[33]; *buf = digits[33];
++buf; ++buf;
} }
} }
if (!(type & LEFT)) { if (!(type & LEFT)) {
while (size-- > 0) { while (size-- > 0) {
if (buf <= end) if (buf < end)
*buf = c; *buf = c;
++buf; ++buf;
} }
} }
while (i < precision--) { while (i < precision--) {
if (buf <= end) if (buf < end)
*buf = '0'; *buf = '0';
++buf; ++buf;
} }
while (i-- > 0) { while (i-- > 0) {
if (buf <= end) if (buf < end)
*buf = tmp[i]; *buf = tmp[i];
++buf; ++buf;
} }
while (size-- > 0) { while (size-- > 0) {
if (buf <= end) if (buf < end)
*buf = ' '; *buf = ' ';
++buf; ++buf;
} }
...@@ -272,7 +272,8 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -272,7 +272,8 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
/* 'z' changed to 'Z' --davidm 1/25/99 */ /* 'z' changed to 'Z' --davidm 1/25/99 */
/* 't' added for ptrdiff_t */ /* 't' added for ptrdiff_t */
/* Reject out-of-range values early */ /* Reject out-of-range values early. Large positive sizes are
used for unknown buffer sizes. */
if (unlikely((int) size < 0)) { if (unlikely((int) size < 0)) {
/* There can be only one.. */ /* There can be only one.. */
static int warn = 1; static int warn = 1;
...@@ -282,16 +283,17 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -282,16 +283,17 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
} }
str = buf; str = buf;
end = buf + size - 1; end = buf + size;
if (end < buf - 1) { /* Make sure end is always >= buf */
end = ((void *) -1); if (end < buf) {
size = end - buf + 1; end = ((void *)-1);
size = end - buf;
} }
for (; *fmt ; ++fmt) { for (; *fmt ; ++fmt) {
if (*fmt != '%') { if (*fmt != '%') {
if (str <= end) if (str < end)
*str = *fmt; *str = *fmt;
++str; ++str;
continue; continue;
...@@ -357,17 +359,17 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -357,17 +359,17 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
case 'c': case 'c':
if (!(flags & LEFT)) { if (!(flags & LEFT)) {
while (--field_width > 0) { while (--field_width > 0) {
if (str <= end) if (str < end)
*str = ' '; *str = ' ';
++str; ++str;
} }
} }
c = (unsigned char) va_arg(args, int); c = (unsigned char) va_arg(args, int);
if (str <= end) if (str < end)
*str = c; *str = c;
++str; ++str;
while (--field_width > 0) { while (--field_width > 0) {
if (str <= end) if (str < end)
*str = ' '; *str = ' ';
++str; ++str;
} }
...@@ -382,18 +384,18 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -382,18 +384,18 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
if (!(flags & LEFT)) { if (!(flags & LEFT)) {
while (len < field_width--) { while (len < field_width--) {
if (str <= end) if (str < end)
*str = ' '; *str = ' ';
++str; ++str;
} }
} }
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
if (str <= end) if (str < end)
*str = *s; *str = *s;
++str; ++s; ++str; ++s;
} }
while (len < field_width--) { while (len < field_width--) {
if (str <= end) if (str < end)
*str = ' '; *str = ' ';
++str; ++str;
} }
...@@ -426,7 +428,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -426,7 +428,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
continue; continue;
case '%': case '%':
if (str <= end) if (str < end)
*str = '%'; *str = '%';
++str; ++str;
continue; continue;
...@@ -449,11 +451,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -449,11 +451,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
break; break;
default: default:
if (str <= end) if (str < end)
*str = '%'; *str = '%';
++str; ++str;
if (*fmt) { if (*fmt) {
if (str <= end) if (str < end)
*str = *fmt; *str = *fmt;
++str; ++str;
} else { } else {
...@@ -483,14 +485,13 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) ...@@ -483,14 +485,13 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
str = number(str, end, num, base, str = number(str, end, num, base,
field_width, precision, flags); field_width, precision, flags);
} }
if (str <= end) if (size > 0) {
if (str < end)
*str = '\0'; *str = '\0';
else if (size > 0) else
/* don't write out a null byte if the buf size is zero */
*end = '\0'; *end = '\0';
/* the trailing null byte doesn't count towards the total }
* ++str; /* the trailing null byte doesn't count towards the total */
*/
return str-buf; return str-buf;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment