diff options
| author | Tobias Stoeckmann <tobias@stoeckmann.org> | 2020-05-21 14:15:06 +0200 |
|---|---|---|
| committer | Michael Kerrisk <mtk.manpages@gmail.com> | 2020-05-25 15:59:42 +0200 |
| commit | 7577e4074b375acb55fb95d799adb800807933e3 (patch) | |
| tree | 161f63d185349d53cda41fcb6e099c2cd2351af1 /man3/printf.3 | |
| parent | 9a766452249b3a9e2e1252bd563e3a04186df68c (diff) | |
| download | man-pages-7577e4074b375acb55fb95d799adb800807933e3.tar.gz | |
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
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
Diffstat (limited to 'man3/printf.3')
| -rw-r--r-- | man3/printf.3 | 15 |
1 files changed, 9 insertions, 6 deletions
diff --git a/man3/printf.3 b/man3/printf.3 index 50e136ba60..827d9cbae6 100644 --- a/man3/printf.3 +++ b/man3/printf.3 @@ -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; } |
