printf.3: Prevent signed integer overflow in example
PATCH 1/1 printf.3: Prevent signed integer overflow in example
The function make_message illustrates how to use vsnprintf to determine the required amount of memory for a specific format and its arguments.
If make_message is called with a format which will use exactly INT_MAX characters (excluding '\0'), then the size++ calculation will overflow the signed integer "size", which is an undefined behaviour in C.
Since malloc and vsnprintf rightfully take a size_t argument, I decided to use a size_t variable for size calculation. Therefore, this patched code uses variables of the same data types as expected by function arguments.
Proof of concept (tested on Linux/glibc amd64):
int main() { make_message("%647s%2147483000s", "", ""); }
If the code is compiled with address sanitizer (gcc -fsanitize=address) you can see the following line, assuming that a signed integer overflow simply leads to INT_MIN:
==3094==WARNING: AddressSanitizer failed to allocate 0xffffffff80000000 bytes
--- man3/printf.3 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
man3/printf.3 +9 -6
@@ -1132,29 +1132,32 @@ To allocate a sufficiently large string and print into it char * make_message(const char *fmt, ...) { - int size = 0; + int n = 0; + size_t size = 0; char *p = NULL; va_list ap; /* Determine required size */ va_start(ap, fmt); - size = vsnprintf(p, size, fmt, ap); + n = vsnprintf(p, size, fmt, ap); va_end(ap); - if (size < 0) + if (n < 0) return NULL; - size++; /* For '\e0' */ + /* One extra byte for '\e0' */ + + size = (size_t) n + 1; p = malloc(size); if (p == NULL) return NULL; va_start(ap, fmt); - size = vsnprintf(p, size, fmt, ap); + n = vsnprintf(p, size, fmt, ap); va_end(ap); - if (size < 0) { + if (n < 0) { free(p); return NULL; }
Signature · 1 line
2.26.2
Tobias,
Quoted 14 lines · Tobias Stoeckmann
The function
make_messageillustrates how to use vsnprintf to determine the required amount of memory for a specific format and its arguments.If
make_messageis called with a format which will use exactlyINT_MAXcharacters (excluding '\0'), then the size++ calculation will overflow the signed integer "size", which is an undefined behaviour in C.Since malloc and vsnprintf rightfully take a
size_targument, I decided to use asize_tvariable for size calculation. Therefore, this patched code uses variables of the same data types as expected by function arguments.Proof of concept (tested on Linux/glibc amd64):
Quoted 7 lines
int
main(){make_message("%647s%2147483000s", "", ""); }If the code is compiled with address sanitizer (gcc -fsanitize=address) you can see the following line, assuming that a signed integer overflow simply leads to
INT_MIN:==3094==WARNING: AddressSanitizer failed to allocate
0xffffffff80000000bytes
Thanks for the nicely documented patch. Applied.
Cheers,
Michael
Quoted 48 lines
--- man3/printf.3 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)man3/printf.3 +9
-6@@ -1132,29 +1132,32 @@ To allocate a sufficiently large string and print into it char * make_message(const char *fmt, ...) { - int size = 0; + int n = 0; + size_t size = 0; char *p = NULL; va_list ap; /* Determine required size */ va_start(ap, fmt); - size = vsnprintf(p, size, fmt, ap); + n = vsnprintf(p, size, fmt, ap); va_end(ap); - if (size < 0) + if (n < 0) return NULL; - size++; /* For '\e0' */ + /* One extra byte for '\e0' */ + + size = (size_t) n + 1; p = malloc(size); if (p == NULL) return NULL; va_start(ap, fmt); - size = vsnprintf(p, size, fmt, ap); + n = vsnprintf(p, size, fmt, ap); va_end(ap); - if (size < 0) { + if (n < 0) { free(p); return NULL; }
Signature · 3 lines
Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/