git: c91cfb7f9e7d - stable/14 - lib/libc/tests/string: expand memcmp test to bcmp, timingsafe_{b,mem}cmp

From: Robert Clausecker <fuz_at_FreeBSD.org>
Date: Thu, 28 Dec 2023 17:20:12 UTC
The branch stable/14 has been updated by fuz:

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

commit c91cfb7f9e7de392ca47b3092e981bf7c2fca86b
Author:     Robert Clausecker <fuz@FreeBSD.org>
AuthorDate: 2023-08-30 15:30:24 +0000
Commit:     Robert Clausecker <fuz@FreeBSD.org>
CommitDate: 2023-12-28 17:02:41 +0000

    lib/libc/tests/string: expand memcmp test to bcmp, timingsafe_{b,mem}cmp
    
    The four functions more or less perform the same operation.
    Reuse the same unit test with slight changes so we can cover
    them all.  Constant-time operation is not verified for the
    timingsafe_* functions.
    
    Sponsored by:   The FreeBSD Foundation
    Approved by:    ngie
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D41528
    
    (cherry picked from commit c6cc06d465a98270ef734ffec7c9012ec3a1bd17)
---
 lib/libc/tests/string/Makefile                 |  3 +
 lib/libc/tests/string/bcmp_test.c              | 32 ++++++++++
 lib/libc/tests/string/memcmp_test.c            | 82 ++++++++++++++++++--------
 lib/libc/tests/string/timingsafe_bcmp_test.c   | 32 ++++++++++
 lib/libc/tests/string/timingsafe_memcmp_test.c | 32 ++++++++++
 5 files changed, 155 insertions(+), 26 deletions(-)

diff --git a/lib/libc/tests/string/Makefile b/lib/libc/tests/string/Makefile
index e9b066cacac7..a090e1bd3463 100644
--- a/lib/libc/tests/string/Makefile
+++ b/lib/libc/tests/string/Makefile
@@ -2,6 +2,7 @@
 # ensure libc functions are tested, not clang's builtins
 CFLAGS+=	-fno-builtin
 
+ATF_TESTS_C+=		bcmp_test
 ATF_TESTS_C+=		ffs_test
 ATF_TESTS_C+=		ffsl_test
 ATF_TESTS_C+=		ffsll_test
@@ -17,6 +18,8 @@ ATF_TESTS_C+=		strerror2_test
 ATF_TESTS_C+=		strspn_test
 ATF_TESTS_C+=		strverscmp_test
 ATF_TESTS_C+=		strxfrm_test
+ATF_TESTS_C+=		timingsafe_bcmp_test
+ATF_TESTS_C+=		timingsafe_memcmp_test
 ATF_TESTS_C+=		wcscasecmp_test
 ATF_TESTS_C+=		wcscoll_test
 ATF_TESTS_C+=		wcsnlen_test
diff --git a/lib/libc/tests/string/bcmp_test.c b/lib/libc/tests/string/bcmp_test.c
new file mode 100644
index 000000000000..fdf5e48b3eb4
--- /dev/null
+++ b/lib/libc/tests/string/bcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+#define MEMCMP bcmp
+#define RES(x) ((x) != 0)
+
+#include "memcmp_test.c"
diff --git a/lib/libc/tests/string/memcmp_test.c b/lib/libc/tests/string/memcmp_test.c
index 824e0d27ac0e..5286a0b994f3 100644
--- a/lib/libc/tests/string/memcmp_test.c
+++ b/lib/libc/tests/string/memcmp_test.c
@@ -1,7 +1,11 @@
 /*-
  * Copyright (c) 2016 Jilles Tjoelker <jilles@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
  * All rights reserved.
  *
+ * Portions of this software were developed by Robert Clausecker
+ * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -32,14 +36,40 @@
 
 #include <atf-c.h>
 
+#ifndef MEMCMP
+#define MEMCMP memcmp
+#endif
+
+/*
+ * On FreeBSD we demand that memcmp returns the difference between the
+ * characters at the first site of mismatch.  However, ISO/IEC 9899:1990
+ * only specifies that a number greater than, equal to, or less than
+ * zero shall be returned.  If a unit test for this less strict
+ * behaviour is desired, define RES(x) to be (((x) > 0) - ((x) < 0)).
+ */
+#ifndef RES
+#define RES(x) (x)
+#endif
+
 static int (*memcmp_fn)(const void *, const void *, size_t);
 
+static void
+check_memcmp(const char *a, const char *b, size_t len, int expected)
+{
+	int got;
+
+	got = memcmp_fn(a, b, len);
+	ATF_CHECK_EQ_MSG(RES(expected), RES(got),
+	    "%s(%p, %p, %zu) gave %d, but wanted %d",
+	    __XSTRING(MEMCMP), a, b, len, got, expected);
+}
+
 ATF_TC_WITHOUT_HEAD(zero);
 ATF_TC_BODY(zero, tc)
 {
 
-	assert(memcmp_fn("a", "b", 0) == 0);
-	assert(memcmp_fn("", "", 0) == 0);
+	check_memcmp("a", "b", 0, 0);
+	check_memcmp("", "", 0, 0);
 }
 
 ATF_TC_WITHOUT_HEAD(eq);
@@ -51,9 +81,9 @@ ATF_TC_BODY(eq, tc)
 	for (i = 0; i < 256; i++)
 		data1[i] = data2[i] = i ^ 0x55;
 	for (i = 1; i < 256; i++)
-		assert(memcmp_fn(data1, data2, i) == 0);
+		check_memcmp(data1, data2, i, 0);
 	for (i = 1; i < 256; i++)
-		assert(memcmp_fn(data1 + i, data2 + i, 256 - i) == 0);
+		check_memcmp(data1 + i, data2 + i, 256 - i, 0);
 }
 
 ATF_TC_WITHOUT_HEAD(neq);
@@ -67,9 +97,9 @@ ATF_TC_BODY(neq, tc)
 		data2[i] = i ^ 0x55;
 	}
 	for (i = 1; i < 256; i++)
-		assert(memcmp_fn(data1, data2, i) != 0);
+		check_memcmp(data1, data2, i, -0x55);
 	for (i = 1; i < 256; i++)
-		assert(memcmp_fn(data1 + i, data2 + i, 256 - i) != 0);
+		check_memcmp(data1 + i, data2 + i, 256 - i, i - (i ^ 0x55));
 }
 
 ATF_TC_WITHOUT_HEAD(diff);
@@ -83,32 +113,32 @@ ATF_TC_BODY(diff, tc)
 	data1[128] = 255;
 	data2[128] = 0;
 	for (i = 1; i < 66; i++) {
-		assert(memcmp_fn(data1 + 128, data2 + 128, i) == 255);
-		assert(memcmp_fn(data2 + 128, data1 + 128, i) == -255);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i) == 255);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i) == -255);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i * 2) == 255);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i * 2) == -255);
+		check_memcmp(data1 + 128, data2 + 128, i, 255);
+		check_memcmp(data2 + 128, data1 + 128, i, -255);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i, 255);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i, -255);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, 255);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, -255);
 	}
 	data1[128] = 'c';
 	data2[128] = 'e';
 	for (i = 1; i < 66; i++) {
-		assert(memcmp_fn(data1 + 128, data2 + 128, i) == -2);
-		assert(memcmp_fn(data2 + 128, data1 + 128, i) == 2);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i) == -2);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i) == 2);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i * 2) == -2);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i * 2) == 2);
+		check_memcmp(data1 + 128, data2 + 128, i, -2);
+		check_memcmp(data2 + 128, data1 + 128, i, 2);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2);
 	}
 	memset(data1 + 129, 'A', sizeof(data1) - 129);
 	memset(data2 + 129, 'Z', sizeof(data2) - 129);
 	for (i = 1; i < 66; i++) {
-		assert(memcmp_fn(data1 + 128, data2 + 128, i) == -2);
-		assert(memcmp_fn(data2 + 128, data1 + 128, i) == 2);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i) == -2);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i) == 2);
-		assert(memcmp_fn(data1 + 129 - i, data2 + 129 - i, i * 2) == -2);
-		assert(memcmp_fn(data2 + 129 - i, data1 + 129 - i, i * 2) == 2);
+		check_memcmp(data1 + 128, data2 + 128, i, -2);
+		check_memcmp(data2 + 128, data1 + 128, i, 2);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2);
+		check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2);
+		check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2);
 	}
 }
 
@@ -117,9 +147,9 @@ ATF_TP_ADD_TCS(tp)
 	void *dl_handle;
 
 	dl_handle = dlopen(NULL, RTLD_LAZY);
-	memcmp_fn = dlsym(dl_handle, "test_memcmp");
+	memcmp_fn = dlsym(dl_handle, "test_" __XSTRING(MEMCMP));
 	if (memcmp_fn == NULL)
-		memcmp_fn = memcmp;
+		memcmp_fn = MEMCMP;
 
 	ATF_TP_ADD_TC(tp, zero);
 	ATF_TP_ADD_TC(tp, eq);
diff --git a/lib/libc/tests/string/timingsafe_bcmp_test.c b/lib/libc/tests/string/timingsafe_bcmp_test.c
new file mode 100644
index 000000000000..96bf789633f2
--- /dev/null
+++ b/lib/libc/tests/string/timingsafe_bcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+#define MEMCMP timingsafe_bcmp
+#define RES(x) ((x) != 0)
+
+#include "memcmp_test.c"
diff --git a/lib/libc/tests/string/timingsafe_memcmp_test.c b/lib/libc/tests/string/timingsafe_memcmp_test.c
new file mode 100644
index 000000000000..5f97e41fcf8a
--- /dev/null
+++ b/lib/libc/tests/string/timingsafe_memcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+#define MEMCMP timingsafe_memcmp
+#define RES(x) (((x) > 0) - ((x) < 0))
+
+#include "memcmp_test.c"