git: 691ff1832e09 - main - lib/libc/tests/string: add memrchr unit tests

From: Robert Clausecker <fuz_at_FreeBSD.org>
Date: Mon, 25 Dec 2023 14:26:05 UTC
The branch main has been updated by fuz:

URL: https://cgit.FreeBSD.org/src/commit/?id=691ff1832e09a6ccbc8f5b04c88cafc7452b3ce6

commit 691ff1832e09a6ccbc8f5b04c88cafc7452b3ce6
Author:     Robert Clausecker <fuz@FreeBSD.org>
AuthorDate: 2023-12-06 09:11:40 +0000
Commit:     Robert Clausecker <fuz@FreeBSD.org>
CommitDate: 2023-12-25 13:59:58 +0000

    lib/libc/tests/string: add memrchr unit tests
    
    The "values" test case is specifically crafted to detect the off-by-one
    error previous discovered in the scalar strchrnul implementation.
    
    Tested by:      developers@, exp-run
    Approved by:    mjg
    MFC after:      1 month
    MFC to:         stable/14
    PR:             275785
    Differential Revision:  https://reviews.freebsd.org/D42925
---
 lib/libc/tests/string/Makefile       |   1 +
 lib/libc/tests/string/memrchr_test.c | 116 +++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+)

diff --git a/lib/libc/tests/string/Makefile b/lib/libc/tests/string/Makefile
index a6e8eb18075a..4fce79685c0e 100644
--- a/lib/libc/tests/string/Makefile
+++ b/lib/libc/tests/string/Makefile
@@ -11,6 +11,7 @@ ATF_TESTS_C+=		flsl_test
 ATF_TESTS_C+=		flsll_test
 ATF_TESTS_C+=		memccpy_test
 ATF_TESTS_C+=		memcmp_test
+ATF_TESTS_C+=		memrchr_test
 ATF_TESTS_C+=		memset_s_test
 ATF_TESTS_C+=		strncmp_test
 ATF_TESTS_C+=		stpncpy_test
diff --git a/lib/libc/tests/string/memrchr_test.c b/lib/libc/tests/string/memrchr_test.c
new file mode 100644
index 000000000000..12f696c9dc1e
--- /dev/null
+++ b/lib/libc/tests/string/memrchr_test.c
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Robert Clausecker
+ */
+
+#include <sys/cdefs.h>
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static void *(*memrchr_fn)(const void *, int, size_t);
+
+ATF_TC_WITHOUT_HEAD(null);
+ATF_TC_BODY(null, tc)
+{
+	ATF_CHECK_EQ(memrchr_fn(NULL, 42, 0), NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(not_found);
+ATF_TC_BODY(not_found, tc)
+{
+	size_t i, j;
+	char buf[1+15+64+1]; /* offset [0..15] + 64 buffer bytes + sentinels */
+
+	buf[0] = 'X';
+	memset(buf + 1, '-', sizeof(buf) - 1);
+
+	for (i = 0; i < 16; i++)
+		for (j = 0; j < 64; j++) {
+			buf[i + j + 1] = 'X';
+			ATF_CHECK_EQ(memrchr_fn(buf + i + 1, 'X', j), NULL);
+			buf[i + j + 1] = '-';
+		}
+}
+
+static void
+do_found_test(char buf[], size_t len, size_t first, size_t second)
+{
+	/* invariant: first <= second */
+
+	buf[first] = 'X';
+	buf[second] = 'X';
+	ATF_CHECK_EQ(memrchr_fn(buf, 'X', len), buf + second);
+	buf[first] = '-';
+	buf[second] = '-';
+}
+
+ATF_TC_WITHOUT_HEAD(found);
+ATF_TC_BODY(found, tc)
+{
+	size_t i, j, k, l;
+	char buf[1+15+64+1];
+
+	buf[0] = 'X';
+	memset(buf + 1, '-', sizeof(buf) - 1);
+
+	for (i = 0; i < 16; i++)
+		for (j = 0; j < 64; j++)
+			for (k = 0; k < j; k++)
+				for (l = 0; l <= k; l++) {
+					buf[i + j + 1] = 'X';
+					do_found_test(buf + i + 1, j, l, k);
+					buf[i + j + 1] = '-';
+				}
+}
+
+/* check that the right character is found */
+static void
+do_values_test(unsigned char buf[], size_t len, size_t i, int c)
+{
+	/* sentinels */
+	buf[-1] = c;
+	buf[len] = c;
+	memset(buf, c + 1, len);
+
+	if (i < len) {
+		buf[i] = c;
+		ATF_CHECK_EQ(memrchr_fn(buf, c, len), buf + i);
+	} else
+		ATF_CHECK_EQ(memrchr_fn(buf, c, len), NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(values);
+ATF_TC_BODY(values, tc)
+{
+	size_t i, j, k;
+	int c;
+	unsigned char buf[1+15+64+1];
+
+	for (i = 0; i < 16; i++)
+		for (j = 0; j < 64; j++)
+			for (k = 0; k <= j; k++)
+				for (c = 0; c <= UCHAR_MAX; c++)
+					do_values_test(buf + i + 1, j, k, c);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	void *dl_handle;
+
+	dl_handle = dlopen(NULL, RTLD_LAZY);
+	memrchr_fn = dlsym(dl_handle, "test_memrchr");
+	if (memrchr_fn == NULL)
+		memrchr_fn = memrchr;
+
+	ATF_TP_ADD_TC(tp, null);
+	ATF_TP_ADD_TC(tp, not_found);
+	ATF_TP_ADD_TC(tp, found);
+	ATF_TP_ADD_TC(tp, values);
+
+	return (atf_no_error());
+}