From c1f5a0925ddf84981d9e176d146bfddb48eb45d1 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Tue, 21 Dec 2021 12:35:54 +0000 Subject: [PATCH 1/3] fix(libc): fix snprintf corner cases The number formatting routine in snprintf was trying to be clever with the buffer handling, but tripped over its own feet: snprintf() users expect output to be emitted, even if not everything fits into the buffer. The current code gives up completely when the buffer is too small. Fix those issues and simplify the code on the way, by consequently using the CHECK_AND_PUT_CHAR() macro, which both checks for the buffer size correctly, but also keeps track of the number of should-be-printed characters for the return value. Change-Id: Ifd2b03b9a73f9279abed53081a2d88720ecbdbc1 Signed-off-by: Andre Przywara --- lib/libc/snprintf.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c index 3b175ed6a..16611435b 100644 --- a/lib/libc/snprintf.c +++ b/lib/libc/snprintf.c @@ -52,31 +52,14 @@ static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, } while (unum > 0U); width = i; - if (padn > width) { - (*chars_printed) += (size_t)padn; - } else { - (*chars_printed) += (size_t)width; + for (i = padn - width; i > 0; i--) { + CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); } - - if (*chars_printed < n) { - - if (padn > 0) { - while (width < padn) { - *(*s)++ = padc; - padn--; - } - } - - while (--i >= 0) { - *(*s)++ = num_buf[i]; - } - - if (padn < 0) { - while (width < -padn) { - *(*s)++ = padc; - padn++; - } - } + for (i = width; i > 0; i--) { + CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]); + } + for (i = width + padn; i < 0; i++) { + CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc); } } From b30dd4030dcef950eac05393013ee019c3cb3205 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 24 Jan 2022 18:16:10 +0000 Subject: [PATCH 2/3] fix(libc): limit snprintf radix value In our unsigned_num_print() function we first print the integer into a local buffer, then put this through alignment and padding and output the result. For this we use a local buffer, sized by the maximum possible length of the largest possible number. However this assumes that the radix is not smaller than 10, which is indeed the smallest value we pass into this static function at the moment. To prevent accidents in the future, should we add support for other radices, add an assert to enforce our assumption. Unfortunately this cannot be a static assert (CASSERT), since the compiler is not smart enough to see that the argument is always coming from a literal. Change-Id: Ic204462600d9f4c281d899cf9f2c698a0a33a874 Signed-off-by: Andre Przywara --- lib/libc/snprintf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c index 16611435b..7141091bb 100644 --- a/lib/libc/snprintf.c +++ b/lib/libc/snprintf.c @@ -40,6 +40,12 @@ static void unsigned_num_print(char **s, size_t n, size_t *chars_printed, unsigned int rem; char ascii_a = capitalise ? 'A' : 'a'; + if (radix < 10) { + ERROR("snprintf: unsupported radix '%d'.", radix); + plat_panic_handler(); + assert(0); /* Unreachable */ + } + do { rem = unum % radix; if (rem < 10U) { From 410c925ab31693dc74d654ff9167c8eed3ec5a62 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Thu, 27 Jan 2022 17:47:55 +0000 Subject: [PATCH 3/3] fix(libc): snprintf: include stdint.h The snprintf code uses the uintptr_t type, which is defined in stdint.h. We do not include this header explicitly, but get the definition indirectly through some other header doing so. However this breaks when snprintf is compiled in isolation (for instance for unit-testing), so let's add this #include to make things right. Change-Id: I1299767ee482f5cf1af30c4df2e8f7e596969b41 Signed-off-by: Andre Przywara --- lib/libc/snprintf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c index 7141091bb..f34cefd6a 100644 --- a/lib/libc/snprintf.c +++ b/lib/libc/snprintf.c @@ -6,6 +6,7 @@ #include #include +#include #include #include