[PATCH v3 next 04/17] selftests/nolibc: Improve reporting of vfprintf() errors
From: david . laight . linux
Date: Mon Feb 23 2026 - 06:10:48 EST
From: David Laight <david.laight.linux@xxxxxxxxx>
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@xxxxxxxxx>
---
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