From 2e448748146dd37b178925f17d3c1f47298a7542 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 3 Nov 2025 10:09:13 +0100 Subject: lib/vsprintf: Improve vsprintf + sprintf function comments Clarify that the return values of vsprintf() and sprintf() exclude the trailing NUL character. Signed-off-by: Thorsten Blum Reviewed-by: Petr Mladek Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20251103090913.2066-2-thorsten.blum@linux.dev Signed-off-by: Petr Mladek --- lib/vsprintf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index eb0cb11d0d12..e49f350ee549 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -3054,8 +3054,8 @@ EXPORT_SYMBOL(scnprintf); * @fmt: The format string to use * @args: Arguments for the format string * - * The function returns the number of characters written - * into @buf. Use vsnprintf() or vscnprintf() in order to avoid + * The return value is the number of characters written into @buf not including + * the trailing '\0'. Use vsnprintf() or vscnprintf() in order to avoid * buffer overflows. * * If you're not already dealing with a va_list consider using sprintf(). @@ -3074,8 +3074,8 @@ EXPORT_SYMBOL(vsprintf); * @fmt: The format string to use * @...: Arguments for the format string * - * The function returns the number of characters written - * into @buf. Use snprintf() or scnprintf() in order to avoid + * The return value is the number of characters written into @buf not including + * the trailing '\0'. Use snprintf() or scnprintf() in order to avoid * buffer overflows. * * See the vsnprintf() documentation for format string extensions over C99. -- cgit v1.2.3 From 372a12bd5df0199aa234eaf8ef31ed7ecd61d40f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 Nov 2025 14:21:18 +0100 Subject: lib/vsprintf: Check pointer before dereferencing in time_and_date() The pointer may be invalid when gets to the printf(). In particular the time_and_date() dereferencing it in some cases without checking. Move the check from rtc_str() to time_and_date() to cover all cases. Fixes: 7daac5b2fdf8 ("lib/vsprintf: Print time64_t in human readable format") Signed-off-by: Andy Shevchenko Reviewed-by: Petr Mladek Link: https://patch.msgid.link/20251110132118.4113976-1-andriy.shevchenko@linux.intel.com Signed-off-by: Petr Mladek --- lib/vsprintf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e49f350ee549..3f99834fd788 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1928,9 +1928,6 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm, bool found = true; int count = 2; - if (check_pointer(&buf, end, tm, spec)) - return buf; - switch (fmt[count]) { case 'd': have_t = false; @@ -1996,6 +1993,9 @@ static noinline_for_stack char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, const char *fmt) { + if (check_pointer(&buf, end, ptr, spec)) + return buf; + switch (fmt[1]) { case 'R': return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt); -- cgit v1.2.3 From 376c18f30e226854062ff0a6eebc6af4ea62456b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Nov 2025 16:02:59 +0100 Subject: lib/vsprintf: Deduplicate special hex number specifier data Two functions use the same specifier data for the special hex number. Almost the same as the field width is calculated on the size of the given type. Due to that, make a compound literal macro in order to deduplicate the rest. Signed-off-by: Andy Shevchenko Reviewed-by: Petr Mladek Tested-by: Petr Mladek Link: https://patch.msgid.link/20251113150313.3030700-1-andriy.shevchenko@linux.intel.com Signed-off-by: Petr Mladek --- lib/vsprintf.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 3f99834fd788..11dbf1023391 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -582,17 +582,18 @@ char *number(char *buf, char *end, unsigned long long num, return buf; } +#define special_hex_spec(size) \ +(struct printf_spec) { \ + .field_width = 2 + 2 * (size), /* 0x + hex */ \ + .flags = SPECIAL | SMALL | ZEROPAD, \ + .base = 16, \ + .precision = -1, \ +} + static noinline_for_stack char *special_hex_number(char *buf, char *end, unsigned long long num, int size) { - struct printf_spec spec; - - spec.field_width = 2 + 2 * size; /* 0x + hex */ - spec.flags = SPECIAL | SMALL | ZEROPAD; - spec.base = 16; - spec.precision = -1; - - return number(buf, end, num, spec); + return number(buf, end, num, special_hex_spec(size)); } static void move_right(char *buf, char *end, unsigned len, unsigned spaces) @@ -1164,18 +1165,11 @@ char *range_string(char *buf, char *end, const struct range *range, char sym[sizeof("[range 0x0123456789abcdef-0x0123456789abcdef]")]; char *p = sym, *pend = sym + sizeof(sym); - struct printf_spec range_spec = { - .field_width = 2 + 2 * sizeof(range->start), /* 0x + 2 * 8 */ - .flags = SPECIAL | SMALL | ZEROPAD, - .base = 16, - .precision = -1, - }; - if (check_pointer(&buf, end, range, spec)) return buf; p = string_nocheck(p, pend, "[range ", default_str_spec); - p = hex_range(p, pend, range->start, range->end, range_spec); + p = hex_range(p, pend, range->start, range->end, special_hex_spec(sizeof(range->start))); *p++ = ']'; *p = '\0'; -- cgit v1.2.3 From bccd5937447f8a2c9db0e4a92e5ecb641b8ce7de Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Nov 2025 15:32:15 +0100 Subject: lib/vsprintf: Add specifier for printing struct timespec64 A handful drivers want to print a content of the struct timespec64 in a format of %lld:%09ld. In order to make their lives easier, add the respecting specifier directly to the printf() implementation. Signed-off-by: Andy Shevchenko Reviewed-by: Petr Mladek Tested-by: Petr Mladek Link: https://patch.msgid.link/20251113150217.3030010-2-andriy.shevchenko@linux.intel.com Signed-off-by: Petr Mladek --- lib/vsprintf.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index eb0cb11d0d12..cdfb9224e0ad 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1992,6 +1992,28 @@ char *time64_str(char *buf, char *end, const time64_t time, return rtc_str(buf, end, &rtc_time, spec, fmt); } +static noinline_for_stack +char *timespec64_str(char *buf, char *end, const struct timespec64 *ts, + struct printf_spec spec, const char *fmt) +{ + static const struct printf_spec default_dec09_spec = { + .base = 10, + .field_width = 9, + .precision = -1, + .flags = ZEROPAD, + }; + + if (fmt[2] == 'p') + buf = number(buf, end, ts->tv_sec, default_dec_spec); + else + buf = time64_str(buf, end, ts->tv_sec, spec, fmt); + if (buf < end) + *buf = '.'; + buf++; + + return number(buf, end, ts->tv_nsec, default_dec09_spec); +} + static noinline_for_stack char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, const char *fmt) @@ -1999,6 +2021,8 @@ char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec, switch (fmt[1]) { case 'R': return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt); + case 'S': + return timespec64_str(buf, end, (const struct timespec64 *)ptr, spec, fmt); case 'T': return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt); default: @@ -2462,9 +2486,11 @@ early_param("no_hash_pointers", no_hash_pointers_enable); * - 'd[234]' For a dentry name (optionally 2-4 last components) * - 'D[234]' Same as 'd' but for a struct file * - 'g' For block_device name (gendisk + partition number) - * - 't[RT][dt][r][s]' For time and date as represented by: + * - 't[RST][dt][r][s]' For time and date as represented by: * R struct rtc_time + * S struct timespec64 * T time64_t + * - 'tSp' For time represented by struct timespec64 printed as . * - 'C' For a clock, it prints the name (Common Clock Framework) or address * (legacy clock framework) of the clock * - 'G' For flags to be printed as a collection of symbolic strings that would -- cgit v1.2.3 From a9f349e3c0bebe7ae97750b32a72f452bdf707e2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 20 Nov 2025 09:31:40 +0100 Subject: lib/vsprintf: Unify FORMAT_STATE_NUM handlers We have two almost identical pieces that handle FORMAT_STATE_NUM case. The differences are: - redundant {} for one-line if-else conditional - missing blank line after variable definitions - inverted conditional Unify the style of two. Signed-off-by: Andy Shevchenko Reviewed-by: Petr Mladek Link: https://patch.msgid.link/20251120083140.3478507-1-andriy.shevchenko@linux.intel.com Signed-off-by: Petr Mladek --- lib/vsprintf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/vsprintf.c') diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 11dbf1023391..0dffc7224d41 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -2877,10 +2877,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt_str, va_list args) case FORMAT_STATE_NUM: { unsigned long long num; - if (fmt.size <= sizeof(int)) - num = convert_num_spec(va_arg(args, int), fmt.size, spec); - else + + if (fmt.size > sizeof(int)) num = va_arg(args, long long); + else + num = convert_num_spec(va_arg(args, int), fmt.size, spec); str = number(str, end, num, spec); continue; } @@ -3388,11 +3389,10 @@ int bstr_printf(char *buf, size_t size, const char *fmt_str, const u32 *bin_buf) goto out; case FORMAT_STATE_NUM: - if (fmt.size > sizeof(int)) { + if (fmt.size > sizeof(int)) num = get_arg(long long); - } else { + else num = convert_num_spec(get_arg(int), fmt.size, spec); - } str = number(str, end, num, spec); continue; } -- cgit v1.2.3