From: david.laight.linux@gmail.com
To: "Willy Tarreau" <w@1wt.eu>,
"Thomas Weißschuh" <linux@weissschuh.net>,
linux-kernel@vger.kernel.org, "Cheng Li" <lechain@gmail.com>
Cc: David Laight <david.laight.linux@gmail.com>
Subject: [PATCH v3 next 04/17] selftests/nolibc: Improve reporting of vfprintf() errors
Date: Mon, 23 Feb 2026 10:17:22 +0000 [thread overview]
Message-ID: <20260223101735.2922-5-david.laight.linux@gmail.com> (raw)
In-Reply-To: <20260223101735.2922-1-david.laight.linux@gmail.com>
From: David Laight <david.laight.linux@gmail.com>
Check the string matches before checking the returned length.
Only print the string once when it matches.
Allow for skipping tests that check non-standard behaviour.
Since the 'return value' from snprintf() is really the output buffer
there is no reason for the test cases to specify the numeric return value
(the length of the extected output) as well as the expected output.
The expected output needs to be the 'untruncated' output for tests that
generate output that gets truncatedb because expect_vfprintf() uses
a short buffer.
This puts all the knowledge of any truncation to expect_vfprintf().
It also easier to change the maximum size, and saves counting
the length of the expected string by hand when adding tests.
Increase the size limit from 20 to 25 characters, changing the tests to
match. This is needed to test octal conversions later on.
Append a '+' to the printed output (after the final ") when the output
is truncated.
Additionally check that nothing beyond the end is written.
The "width_trunc" test (#14) now fails because e90ce42e81381
("tools/nolibc: implement width padding in printf()") doesn't
correctly update the space in the buffer when adding pad characters.
This will be addressed in a later patch.
Also correctly return 1 (the number of errors) when strcmp()
fails rather than the return value from strncmp() which is the
signed difference between the mismatching characters.
Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
For v3:
- Patch 9 in v2, patch 5 in v1..
- Increase the size limit to 25 in preparation for testing octal.
- Change the way truncated fprintf() are handled.
- Allow for tests being skipped.
- Use a fixed value (0xa5) for the canary when detecting overwrites.
tools/testing/selftests/nolibc/nolibc-test.c | 91 ++++++++++++++------
1 file changed, 64 insertions(+), 27 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 0e8b3b9a86ef..029ed63e1ae4 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1660,33 +1660,70 @@ int run_stdlib(int min, int max)
return ret;
}
-#define EXPECT_VFPRINTF(c, expected, fmt, ...) \
- ret += expect_vfprintf(llen, c, expected, fmt, ##__VA_ARGS__)
+#define EXPECT_VFPRINTF(cond, expected, fmt, ...) \
+ ret += expect_vfprintf(llen, cond, expected, fmt, ##__VA_ARGS__)
-static int expect_vfprintf(int llen, int c, const char *expected, const char *fmt, ...)
+#define VFPRINTF_LEN 25
+
+static int expect_vfprintf(int llen, int cond, const char *expected, const char *fmt, ...)
{
- char buf[100];
+ ssize_t written, expected_len;
+ char buf[VFPRINTF_LEN + 80];
+ unsigned int cmp_len;
va_list args;
- ssize_t w;
- int ret;
+ if (!cond) {
+ result(llen, SKIPPED);
+ return 0;
+ }
+
+ /* Fill and terminate buffer to check for overlong/absent writes */
+ memset(buf, 0xa5, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
va_start(args, fmt);
- /* Only allow writing 21 bytes, to test truncation */
- w = vsnprintf(buf, 21, fmt, args);
+ /* Limit the buffer length to test truncation */
+ written = vsnprintf(buf, VFPRINTF_LEN + 1, fmt, args);
va_end(args);
- if (w != c) {
- llen += printf(" written(%d) != %d", (int)w, c);
+ llen += printf(" \"%s\"", buf);
+
+ /* If the expected output is long it will have been truncated. */
+ expected_len = strlen(expected);
+ if (expected_len > VFPRINTF_LEN) {
+ /* Indicate truncated in test output */
+ llen += printf("+");
+ cmp_len = VFPRINTF_LEN;
+ } else {
+ cmp_len = expected_len;
+ }
+
+ if (memcmp(expected, buf, cmp_len) || buf[cmp_len]) {
+ /* Copy and truncate until "%.*s" supported */
+ memcpy(buf, expected, cmp_len);
+ buf[cmp_len] = 0;
+ llen += printf(" should be \"%s\"", buf);
result(llen, FAIL);
return 1;
}
- llen += printf(" \"%s\" = \"%s\"", expected, buf);
- ret = strncmp(expected, buf, c);
+ if (written != expected_len) {
+ llen += printf(" written(%d) != %d", (int)written, (int)expected_len);
+ result(llen, FAIL);
+ return 1;
+ }
- result(llen, ret ? FAIL : OK);
- return ret;
+ /* Check for any overwrites after the actual data. */
+ while (++cmp_len < sizeof(buf) - 1) {
+ if ((unsigned char)buf[cmp_len] != 0xa5) {
+ llen += printf(" overwrote buf[%d] with 0x%x", cmp_len, buf[cmp_len]);
+ result(llen, FAIL);
+ return 1;
+ }
+ }
+
+ result(llen, OK);
+ return 0;
}
static int test_scanf(void)
@@ -1807,21 +1844,21 @@ static int run_printf(int min, int max)
* test numbers.
*/
switch (test + __LINE__ + 1) {
- CASE_TEST(empty); EXPECT_VFPRINTF(0, "", ""); break;
- CASE_TEST(simple); EXPECT_VFPRINTF(3, "foo", "foo"); break;
- CASE_TEST(string); EXPECT_VFPRINTF(3, "foo", "%s", "foo"); break;
- CASE_TEST(number); EXPECT_VFPRINTF(4, "1234", "%d", 1234); break;
- CASE_TEST(negnumber); EXPECT_VFPRINTF(5, "-1234", "%d", -1234); break;
- CASE_TEST(unsigned); EXPECT_VFPRINTF(5, "12345", "%u", 12345); break;
+ CASE_TEST(empty); EXPECT_VFPRINTF(1, "", ""); break;
+ CASE_TEST(simple); EXPECT_VFPRINTF(1, "foo", "foo"); break;
+ CASE_TEST(string); EXPECT_VFPRINTF(1, "foo", "%s", "foo"); break;
+ CASE_TEST(number); EXPECT_VFPRINTF(1, "1234", "%d", 1234); break;
+ CASE_TEST(negnumber); EXPECT_VFPRINTF(1, "-1234", "%d", -1234); break;
+ CASE_TEST(unsigned); EXPECT_VFPRINTF(1, "12345", "%u", 12345); break;
CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break;
CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break;
- CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); break;
- CASE_TEST(uintmax_t); EXPECT_VFPRINTF(20, "18446744073709551615", "%ju", 0xffffffffffffffffULL); break;
- CASE_TEST(intmax_t); EXPECT_VFPRINTF(20, "-9223372036854775807", "%jd", 0x8000000000000001LL); break;
- CASE_TEST(truncation); EXPECT_VFPRINTF(25, "01234567890123456789", "%s", "0123456789012345678901234"); break;
- CASE_TEST(string_width); EXPECT_VFPRINTF(10, " 1", "%10s", "1"); break;
- CASE_TEST(number_width); EXPECT_VFPRINTF(10, " 1", "%10d", 1); break;
- CASE_TEST(width_trunc); EXPECT_VFPRINTF(25, " ", "%25d", 1); break;
+ CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); break;
+ CASE_TEST(uintmax_t); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju", 0xffffffffffffffffULL); break;
+ CASE_TEST(intmax_t); EXPECT_VFPRINTF(1, "-9223372036854775807", "%jd", 0x8000000000000001LL); break;
+ CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456789", "%s", "012345678901234567890123456789"); break;
+ CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); break;
+ CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); break;
+ CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " 1", "%30d", 1); break;
CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break;
CASE_TEST(strerror); EXPECT_ZR(1, test_strerror()); break;
CASE_TEST(printf_error); EXPECT_ZR(1, test_printf_error()); break;
--
2.39.5
next prev parent reply other threads:[~2026-02-23 11:10 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-23 10:17 [PATCH v3 next 00/17] Enhance printf() david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 01/17] tools/nolibc: Add _NOLIBC_OPTIMIZER_HIDE_VAR() to compiler.h david.laight.linux
2026-02-25 21:25 ` Thomas Weißschuh
2026-02-25 22:17 ` David Laight
2026-02-25 22:24 ` Thomas Weißschuh
2026-02-23 10:17 ` [PATCH v3 next 02/17] tools/nolibc: Optimise and common up the number to ascii functions david.laight.linux
2026-02-25 21:40 ` Thomas Weißschuh
2026-02-25 22:09 ` David Laight
2026-02-23 10:17 ` [PATCH v3 next 03/17] selftests/nolibc: Fix build with host headers and libc david.laight.linux
2026-02-25 21:24 ` Thomas Weißschuh
2026-02-23 10:17 ` david.laight.linux [this message]
2026-02-25 21:56 ` [PATCH v3 next 04/17] selftests/nolibc: Improve reporting of vfprintf() errors Thomas Weißschuh
2026-02-26 10:12 ` David Laight
2026-02-26 21:39 ` Thomas Weißschuh
2026-02-23 10:17 ` [PATCH v3 next 05/17] tools/nolibc: Implement strerror() in terms of strerror_r() david.laight.linux
2026-02-25 22:09 ` Thomas Weißschuh
2026-02-25 22:58 ` David Laight
2026-02-23 10:17 ` [PATCH v3 next 06/17] tools/nolibc/printf: Change variables 'c' to 'ch' and 'tmpbuf[]' to 'outbuf[]' david.laight.linux
2026-02-25 22:23 ` Thomas Weißschuh
2026-02-23 10:17 ` [PATCH v3 next 07/17] tools/nolibc/printf: Move snprintf length check to callback david.laight.linux
2026-02-25 22:37 ` Thomas Weißschuh
2026-02-25 23:12 ` David Laight
2026-02-26 21:29 ` Thomas Weißschuh
2026-02-26 22:11 ` David Laight
2026-02-23 10:17 ` [PATCH v3 next 08/17] tools/nolibc/printf: Output pad characters in 16 byte chunks david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 09/17] tools/nolibc/printf: Simplify __nolibc_printf() david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 10/17] tools/nolibc/printf: Use goto and reduce indentation david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 11/17] tools/nolibc/printf: Use bit-masks to hold requested flag, length and conversion chars david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 12/17] tools/nolibc/printf: Handle "%s" with the numeric formats david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 13/17] tools/nolibc/printf: Add support for conversion flags david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 14/17] tools/nolibc/printf: Add support for left aligning fields david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 15/17] tools/nolibc/printf: Add support for zero padding and field precision david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 16/17] tools/nolibc/printf: Add support for octal output david.laight.linux
2026-02-23 10:17 ` [PATCH v3 next 17/17] selftests/nolibc: Use printf variable field widths and precisions david.laight.linux
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260223101735.2922-5-david.laight.linux@gmail.com \
--to=david.laight.linux@gmail.com \
--cc=lechain@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@weissschuh.net \
--cc=w@1wt.eu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox