1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 The FreeBSD Foundation 5 * 6 * This software was developed by Mark Johnston under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are 11 * met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Idea from a test case by Andrew "RhodiumToad" Gierth in Bugzilla PR 271766. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/disk.h> 37 #include <sys/mman.h> 38 39 #include <crypto/cryptodev.h> 40 41 #include <err.h> 42 #include <fcntl.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 int 48 main(int argc, char **argv) 49 { 50 const char *disk; 51 char *buf1, *buf2; 52 off_t disksz; 53 size_t bufsz, iosz; 54 ssize_t n; 55 unsigned int offsets, secsz; 56 int fd; 57 58 if (argc != 2) 59 errx(1, "Usage: %s <disk>", argv[0]); 60 disk = argv[1]; 61 62 fd = open(disk, O_RDWR); 63 if (fd < 0) 64 err(1, "open(%s)", disk); 65 66 if (ioctl(fd, DIOCGSECTORSIZE, &secsz) != 0) 67 err(1, "ioctl(DIOCGSECTORSIZE)"); 68 if (secsz == 0) 69 errx(1, "ioctl(DIOCGSECTORSIZE) returned 0"); 70 if (ioctl(fd, DIOCGMEDIASIZE, &disksz) != 0) 71 err(1, "ioctl(DIOCGMEDIASIZE)"); 72 if (disksz / secsz < 2) 73 errx(1, "disk needs to be at least 2 sectors in size"); 74 iosz = 2 * secsz; 75 76 bufsz = iosz + secsz; 77 buf1 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 78 -1, 0); 79 if (buf1 == MAP_FAILED) 80 err(1, "mmap"); 81 buf2 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 82 -1, 0); 83 if (buf2 == MAP_FAILED) 84 err(1, "mmap"); 85 86 arc4random_buf(buf1, bufsz); 87 n = pwrite(fd, buf1, bufsz, 0); 88 if (n < 0 || (size_t)n != bufsz) 89 err(1, "pwrite"); 90 91 /* 92 * Limit the number of offsets we test with, to avoid spending too much 93 * time when the sector size is large. 94 */ 95 offsets = MAX(EALG_MAX_BLOCK_LEN, HMAC_MAX_BLOCK_LEN) + 1; 96 97 /* 98 * Read test: read the first 2 sectors into buf1, then do the same with 99 * buf2, except at varying offsets into buf2. After each read, compare 100 * the buffers and make sure they're identical. This exercises corner 101 * cases in the crypto layer's buffer handling. 102 */ 103 n = pread(fd, buf1, iosz, 0); 104 if (n < 0 || (size_t)n != iosz) 105 err(1, "pread"); 106 for (unsigned int i = 0; i < offsets; i++) { 107 n = pread(fd, buf2 + i, iosz, 0); 108 if (n < 0 || (size_t)n != iosz) 109 err(1, "pread"); 110 if (memcmp(buf1, buf2 + i, iosz) != 0) 111 errx(1, "read mismatch at offset %u/%u", i, secsz); 112 } 113 114 /* 115 * Write test. Try writing buffers at various alignments, and verify 116 * that we read back what we wrote. 117 */ 118 arc4random_buf(buf1, bufsz); 119 for (unsigned int i = 0; i < offsets; i++) { 120 n = pwrite(fd, buf1 + i, iosz, 0); 121 if (n < 0 || (size_t)n != iosz) 122 err(1, "pwrite"); 123 n = pread(fd, buf2, iosz, 0); 124 if (n < 0 || (size_t)n != iosz) 125 err(1, "pread"); 126 if (memcmp(buf1 + i, buf2, iosz) != 0) 127 errx(1, "write mismatch at offset %u/%u", i, secsz); 128 } 129 130 return (0); 131 } 132