git: c4aae5668c69 - stable/13 - geli tests: Add a regression test for PR 271766

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 19 Jun 2023 13:08:40 UTC
The branch stable/13 has been updated by markj:

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

commit c4aae5668c69df4205ab3df382056a71a0e23bb7
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-06-12 16:11:20 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-06-19 12:57:17 +0000

    geli tests: Add a regression test for PR 271766
    
    This test case catches both of the bugs reported there.
    
    PR:             271766
    Reviewed by:    imp
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D40469
    
    (cherry picked from commit 9c0467929abaab97f45fc07507b6f30c80211239)
---
 tests/sys/geom/class/eli/Makefile       |   3 +
 tests/sys/geom/class/eli/misc_test.sh   |  38 ++++++++-
 tests/sys/geom/class/eli/unaligned_io.c | 131 ++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/tests/sys/geom/class/eli/Makefile b/tests/sys/geom/class/eli/Makefile
index c7f1342f45ae..bdb1101ef059 100644
--- a/tests/sys/geom/class/eli/Makefile
+++ b/tests/sys/geom/class/eli/Makefile
@@ -37,6 +37,9 @@ SRCS.pbkdf2_test=	\
 
 LIBADD.pbkdf2_test= crypto
 
+PROGS+= unaligned_io
+BINDIR?= ${TESTSDIR}
+
 testvect.h:
 	python gentestvect.py > ${.TARGET}
 
diff --git a/tests/sys/geom/class/eli/misc_test.sh b/tests/sys/geom/class/eli/misc_test.sh
index 4dc7cb9bf367..420f47f786e6 100644
--- a/tests/sys/geom/class/eli/misc_test.sh
+++ b/tests/sys/geom/class/eli/misc_test.sh
@@ -135,17 +135,51 @@ physpath_cleanup()
 	true
 }
 
+unaligned_io_test()
+{
+	cipher=$1
+	secsize=$2
+	ealgo=${cipher%%:*}
+	keylen=${cipher##*:}
+
+	atf_check -s exit:0 -e ignore \
+		geli init -B none -e $ealgo -l $keylen -P -K keyfile \
+		-s $secsize ${md}
+	atf_check geli attach -p -k keyfile ${md}
+
+	atf_check $(atf_get_srcdir)/unaligned_io /dev/${md}.eli
+}
+
+atf_test_case unaligned_io cleanup
+unaligned_io_head()
+{
+	atf_set "descr" "regression test for PR 271766"
+	atf_set "require.user" "root"
+}
+unaligned_io_body()
+{
+	geli_test_setup
+
+	sectors=4
+
+	atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+	for_each_geli_config_nointegrity unaligned_io_test
+}
+unaligned_io_cleanup()
+{
+	geli_test_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case physpath
 	atf_add_test_case preserve_props
 	atf_add_test_case preserve_disk_props
+	atf_add_test_case unaligned_io
 }
 
-
 common_cleanup()
 {
-
 	if [ -f "$MD_DEVS" ]; then
 		while read test_md; do
 			gnop destroy -f ${test_md}.nop 2>/dev/null
diff --git a/tests/sys/geom/class/eli/unaligned_io.c b/tests/sys/geom/class/eli/unaligned_io.c
new file mode 100644
index 000000000000..707d15f40be4
--- /dev/null
+++ b/tests/sys/geom/class/eli/unaligned_io.c
@@ -0,0 +1,131 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston 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.
+ */
+
+/*
+ * Idea from a test case by Andrew "RhodiumToad" Gierth in Bugzilla PR 271766.
+ */
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/mman.h>
+
+#include <crypto/cryptodev.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+	const char *disk;
+	char *buf1, *buf2;
+	off_t disksz;
+	size_t bufsz, iosz;
+	ssize_t n;
+	unsigned int offsets, secsz;
+	int fd;
+
+	if (argc != 2)
+		errx(1, "Usage: %s <disk>", argv[0]);
+	disk = argv[1];
+
+	fd = open(disk, O_RDWR);
+	if (fd < 0)
+		err(1, "open(%s)", disk);
+
+	if (ioctl(fd, DIOCGSECTORSIZE, &secsz) != 0)
+		err(1, "ioctl(DIOCGSECTORSIZE)");
+	if (secsz == 0)
+		errx(1, "ioctl(DIOCGSECTORSIZE) returned 0");
+	if (ioctl(fd, DIOCGMEDIASIZE, &disksz) != 0)
+		err(1, "ioctl(DIOCGMEDIASIZE)");
+	if (disksz / secsz < 2)
+		errx(1, "disk needs to be at least 2 sectors in size");
+	iosz = 2 * secsz;
+
+	bufsz = iosz + secsz;
+	buf1 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+	    -1, 0);
+	if (buf1 == MAP_FAILED)
+		err(1, "mmap");
+	buf2 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+	    -1, 0);
+	if (buf2 == MAP_FAILED)
+		err(1, "mmap");
+
+	arc4random_buf(buf1, bufsz);
+	n = pwrite(fd, buf1, bufsz, 0);
+	if (n < 0 || (size_t)n != bufsz)
+		err(1, "pwrite");
+
+	/*
+	 * Limit the number of offsets we test with, to avoid spending too much
+	 * time when the sector size is large.
+	 */
+	offsets = MAX(EALG_MAX_BLOCK_LEN, HMAC_MAX_BLOCK_LEN) + 1;
+
+	/*
+	 * Read test: read the first 2 sectors into buf1, then do the same with
+	 * buf2, except at varying offsets into buf2.  After each read, compare
+	 * the buffers and make sure they're identical.  This exercises corner
+	 * cases in the crypto layer's buffer handling.
+	 */
+	n = pread(fd, buf1, iosz, 0);
+	if (n < 0 || (size_t)n != iosz)
+		err(1, "pread");
+	for (unsigned int i = 0; i < offsets; i++) {
+		n = pread(fd, buf2 + i, iosz, 0);
+		if (n < 0 || (size_t)n != iosz)
+			err(1, "pread");
+		if (memcmp(buf1, buf2 + i, iosz) != 0)
+			errx(1, "read mismatch at offset %u/%u", i, secsz);
+	}
+
+	/*
+	 * Write test.  Try writing buffers at various alignments, and verify
+	 * that we read back what we wrote.
+	 */
+	arc4random_buf(buf1, bufsz);
+	for (unsigned int i = 0; i < offsets; i++) {
+		n = pwrite(fd, buf1 + i, iosz, 0);
+		if (n < 0 || (size_t)n != iosz)
+			err(1, "pwrite");
+		n = pread(fd, buf2, iosz, 0);
+		if (n < 0 || (size_t)n != iosz)
+			err(1, "pread");
+		if (memcmp(buf1 + i, buf2, iosz) != 0)
+			errx(1, "write mismatch at offset %u/%u", i, secsz);
+	}
+
+	return (0);
+}