[PATCH] lib/test_printf: Clean up basic pointer testing

From: Petr Mladek
Date: Thu Feb 20 2020 - 06:53:43 EST


The pointer testing has been originally split by the %p modifiers,
for example, the function dentry() tested %pd and %pD handling.

There were recently added tests that do not really fit into
the existing structure, namely:

+ hashed pointers tested by a maze of functions
+ null and invalid pointer handling with various modifiers

The hash pointer test is really special because the hash depends
on a random key that is generated during boot. Though, it is
still possible to check some aspects:

+ output string length
+ hash differs from the original pointer value
+ top half bites are zeroed on 64-bit systems

Let's put all these checks into test_hashed() function that has
the same behavior as the test() functions for well-defined output.
It increments the number of tests and eventual failures. It prints
warnings/errors when some aspects of the output are not as expected.

Most of these checks were there even before. The only addition is
the check whether hash differs from the original pointer value.
There is a small chance of a false error. It might be reduced
by checking more pointers but let's keep it simple for now.

The existing null_pointer() and invalid_pointer() checks are
newly split per-format modifier. And there is also fixed
difference between invalid pointer in the IS_ERR() range
and invalid pointer that looks like a valid one.

The invalid pointer Oxdeaddead00000000 should work on most
architectures. But I am not able to check it everywhere.
So there is a small chance of a false error. It might get
fixed when anyone reports a problem.

Signed-off-by: Petr Mladek <pmladek@xxxxxxxx>
---
lib/test_printf.c | 162 ++++++++++++++++++++----------------------------------
1 file changed, 59 insertions(+), 103 deletions(-)

diff --git a/lib/test_printf.c b/lib/test_printf.c
index 2d9f520d2f27..4e89b508def6 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -206,146 +206,101 @@ test_string(void)
}

#define PLAIN_BUF_SIZE 64 /* leave some space so we don't oops */
+#define PTR_ERROR ERR_PTR(-EFAULT)
+#define PTR_VAL_ERROR "fffffff2"

#if BITS_PER_LONG == 64

#define PTR_WIDTH 16
#define PTR ((void *)0xffff0123456789abUL)
#define PTR_STR "ffff0123456789ab"
+#define PTR_INVALID ((void *)0xdeaddead000000ab)
+#define PTR_VAL_INVALID "deaddead000000ab"
#define PTR_VAL_NO_CRNG "(____ptrval____)"
+#define ONES "ffffffff" /* hex 32 one bits */
#define ZEROS "00000000" /* hex 32 zero bits */

-static int __init
-plain_format(void)
-{
- char buf[PLAIN_BUF_SIZE];
- int nchars;
-
- nchars = snprintf(buf, PLAIN_BUF_SIZE, "%p", PTR);
-
- if (nchars != PTR_WIDTH)
- return -1;
-
- if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
- pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"",
- PTR_VAL_NO_CRNG);
- return 0;
- }
-
- if (strncmp(buf, ZEROS, strlen(ZEROS)) != 0)
- return -1;
-
- return 0;
-}
-
#else

#define PTR_WIDTH 8
#define PTR ((void *)0x456789ab)
#define PTR_STR "456789ab"
+#define PTR_INVALID ((void *)0x000000ab)
+#define PTR_VAL_INVALID "000000ab"
#define PTR_VAL_NO_CRNG "(ptrval)"
+#define ONES ""
#define ZEROS ""

-static int __init
-plain_format(void)
-{
- /* Format is implicitly tested for 32 bit machines by plain_hash() */
- return 0;
-}
-
#endif /* BITS_PER_LONG == 64 */

-static int __init
-plain_hash_to_buffer(const void *p, char *buf, size_t len)
+static void __init
+test_hashed(const char *fmt, const void *p)
{
+ char pointer[PLAIN_BUF_SIZE];
+ char hash[PLAIN_BUF_SIZE];
int nchars;

- nchars = snprintf(buf, len, "%p", p);
-
- if (nchars != PTR_WIDTH)
- return -1;
+ total_tests++;

- if (strncmp(buf, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
- pr_warn("crng possibly not yet initialized. plain 'p' buffer contains \"%s\"",
- PTR_VAL_NO_CRNG);
- return 0;
+ nchars = snprintf(pointer, sizeof(pointer), "%px", p);
+ if (nchars != PTR_WIDTH) {
+ pr_err("error in test suite: vsprintf(\"%%px\", p) returned number of characters %d, expected %d\n",
+ nchars, PTR_WIDTH);
+ goto err;
}

- return 0;
-}
-
-static int __init
-plain_hash(void)
-{
- char buf[PLAIN_BUF_SIZE];
- int ret;
-
- ret = plain_hash_to_buffer(PTR, buf, PLAIN_BUF_SIZE);
- if (ret)
- return ret;
-
- if (strncmp(buf, PTR_STR, PTR_WIDTH) == 0)
- return -1;
-
- return 0;
-}
-
-/*
- * We can't use test() to test %p because we don't know what output to expect
- * after an address is hashed.
- */
-static void __init
-plain(void)
-{
- int err;
-
- err = plain_hash();
- if (err) {
- pr_warn("plain 'p' does not appear to be hashed\n");
- failed_tests++;
- return;
+ nchars = snprintf(hash, sizeof(hash), fmt, p);
+ if (nchars != PTR_WIDTH) {
+ pr_warn("vsprintf(\"%s\", p) returned number of characters %d, expected %d\n",
+ fmt, nchars, PTR_WIDTH);
+ goto err;
}

- err = plain_format();
- if (err) {
- pr_warn("hashing plain 'p' has unexpected format\n");
- failed_tests++;
+ if (strncmp(hash, PTR_VAL_NO_CRNG, PTR_WIDTH) == 0) {
+ pr_warn_once("crng possibly not yet initialized. vsprinf(\"%s\", p) printed \"%s\"",
+ fmt, hash);
+ total_tests--;
+ return;
}
-}
-
-static void __init
-test_hashed(const char *fmt, const void *p)
-{
- char buf[PLAIN_BUF_SIZE];
- int ret;

/*
- * No need to increase failed test counter since this is assumed
- * to be called after plain().
+ * There is a small chance of a false negative on 32-bit systems
+ * when the hash is the same as the pointer value.
*/
- ret = plain_hash_to_buffer(p, buf, PLAIN_BUF_SIZE);
- if (ret)
- return;
+ if (strncmp(hash, pointer, PTR_WIDTH) == 0) {
+ pr_warn("vsprintf(\"%s\", p) returned %s, expected hashed pointer\n",
+ fmt, hash);
+ goto err;
+ }
+
+#if BITS_PER_LONG == 64
+ if (strncmp(hash, ZEROS, PTR_WIDTH / 2) != 0) {
+ pr_warn("vsprintf(\"%s\", p) returned %s, expected %s in the top half bits\n",
+ fmt, hash, ZEROS);
+ goto err;
+ }
+#endif
+ return;

- test(buf, fmt, p);
+err:
+ failed_tests++;
}

static void __init
-null_pointer(void)
+plain_pointer(void)
{
test_hashed("%p", NULL);
- test(ZEROS "00000000", "%px", NULL);
- test("(null)", "%pE", NULL);
+ test_hashed("%p", PTR_ERROR);
+ test_hashed("%p", PTR_INVALID);
}

-#define PTR_INVALID ((void *)0x000000ab)

static void __init
-invalid_pointer(void)
+real_pointer(void)
{
- test_hashed("%p", PTR_INVALID);
- test(ZEROS "000000ab", "%px", PTR_INVALID);
- test("(efault)", "%pE", PTR_INVALID);
+ test(ZEROS "00000000", "%px", NULL);
+ test(ONES PTR_VAL_ERROR, "%px", PTR_ERROR);
+ test(PTR_VAL_INVALID, "%px", PTR_INVALID);
}

static void __init
@@ -372,6 +327,8 @@ addr(void)
static void __init
escaped_str(void)
{
+ test("(null)", "%pE", NULL);
+ test("(efault)", "%pE", PTR_ERROR);
}

static void __init
@@ -458,9 +415,9 @@ dentry(void)
test("foo", "%pd2", &test_dentry[0]);

test("(null)", "%pd", NULL);
- test("(efault)", "%pd", PTR_INVALID);
+ test("(efault)", "%pd", PTR_ERROR);
test("(null)", "%pD", NULL);
- test("(efault)", "%pD", PTR_INVALID);
+ test("(efault)", "%pD", PTR_ERROR);

test("romeo", "%pd", &test_dentry[3]);
test("alfa/romeo", "%pd2", &test_dentry[3]);
@@ -647,9 +604,8 @@ errptr(void)
static void __init
test_pointer(void)
{
- plain();
- null_pointer();
- invalid_pointer();
+ plain_pointer();
+ real_pointer();
symbol_ptr();
kernel_ptr();
struct_resource();
--
2.16.4