diff --git a/tools/test/stress2/misc/mapwrite.sh b/tools/test/stress2/misc/mapwrite.sh new file mode 100755 index 000000000000..1fef81942b64 --- /dev/null +++ b/tools/test/stress2/misc/mapwrite.sh @@ -0,0 +1,189 @@ +#!/bin/sh + +# File corruption scenario + +# Test program by Rob Norris +# Test program obtained from: https://gist.github.com/robn/9804c60cd0275086d26893d73e7af35c +# https://github.com/openzfs/zfs/issues/15654 + +[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 + +. ../default.cfg + +set -u +prog=$(basename "$0" .sh) +cat > /tmp/$prog.c < /sys/modules/zfs/parameters/zfs_bclone_enabled + * zpool create tank ... + * cd /tank + * mapwrite + * + * [ 7.666305] VERIFY(arc_released(db->db_buf)) failed + * [ 7.666443] PANIC at dbuf.c:2150:dbuf_redirty() + * [ 7.666489] Showing stack for process 608 + * [ 7.666534] CPU: 1 PID: 608 Comm: mapwrite Tainted: P O 5.10.170 #3 + * [ 7.666610] Call Trace: + * [ 7.666646] dump_stack+0x57/0x6e + * [ 7.666717] spl_panic+0xd3/0xfb [spl] + * [ 7.667113] ? zfs_btree_find+0x16a/0x300 [zfs] + * [ 7.667278] ? range_tree_find_impl+0x55/0xa0 [zfs] + * [ 7.667333] ? _cond_resched+0x1a/0x50 + * [ 7.667371] ? __kmalloc_node+0x14a/0x2b0 + * [ 7.667415] ? spl_kmem_alloc_impl+0xb0/0xd0 [spl] + * [ 7.667555] ? __list_add+0x12/0x30 [zfs] + * [ 7.667681] spl_assert+0x17/0x20 [zfs] + * [ 7.667807] dbuf_redirty+0xad/0xb0 [zfs] + * [ 7.667963] dbuf_dirty+0xe76/0x1310 [zfs] + * [ 7.668011] ? mutex_lock+0xe/0x30 + * [ 7.668133] ? dbuf_noread+0x112/0x240 [zfs] + * [ 7.668271] dmu_write_uio_dnode+0x101/0x1b0 [zfs] + * [ 7.668411] dmu_write_uio_dbuf+0x4a/0x70 [zfs] + * [ 7.668555] zfs_write+0x500/0xc80 [zfs] + * [ 7.668610] ? page_add_file_rmap+0xe/0xb0 + * [ 7.668740] zpl_iter_write+0xe4/0x130 [zfs] + * [ 7.668803] new_sync_write+0x119/0x1b0 + * [ 7.668843] vfs_write+0x1ce/0x260 + * [ 7.668880] __x64_sys_pwrite64+0x91/0xc0 + * [ 7.668918] do_syscall_64+0x30/0x40 + * [ 7.668957] entry_SYSCALL_64_after_hwframe+0x61/0xc6 + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATASIZE (1024*1024) +char data[DATASIZE]; + +#define NDATA (512) + +#define FILE_NAME "file" +#define CLONE_NAME "clone" + +static int +_create_file(void) +{ + memset(data, 0x5a, DATASIZE); + + int fd; + if ((fd = open(FILE_NAME, O_RDWR | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR)) < 0) { + perror("open '" FILE_NAME "'"); + abort(); + } + + for (int i = 0; i < NDATA; i++) { + int nwr = write(fd, data, DATASIZE); + if (nwr < 0) { + perror("write"); + abort(); + } + if (nwr < DATASIZE) { + fprintf(stderr, "short write\n"); + abort(); + } + } + + if (lseek(fd, 0, SEEK_SET) < 0) { + perror("lseek"); + abort(); + } + + sync(); + + return (fd); +} + +static int +_clone_file(int sfd) +{ + int dfd; + if ((dfd = open(CLONE_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + perror("open '" CLONE_NAME "'"); + abort(); + } + + if (copy_file_range(sfd, 0, dfd, 0, DATASIZE * NDATA, 0) < 0) { + perror("copy_file_range"); + abort(); + } + + return (dfd); +} + +static void * +_map_file(int fd) +{ + void *p = mmap(NULL, DATASIZE*NDATA, PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) { + perror("mmap"); + abort(); + } + + return (p); +} + +static void +_map_write(void *p, int fd) +{ + if (pwrite(fd, p, DATASIZE, 0) < 0) { + perror("pwrite"); + abort(); + } +} + +int +main(void) +{ + int sfd = _create_file(); + int dfd = _clone_file(sfd); + void *p = _map_file(dfd); + _map_write(p, dfd); + return (0); +} +EOF +mycc -o /tmp/$prog -Wall -Wextra -O2 /tmp/$prog.c || exit 1 + +mount | grep -q "on $mntpoint " && umount -f $mntpoint +mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart +mdconfig -s 5g -u $mdstart + +newfs -n $newfs_flags /dev/md$mdstart > /dev/null +mount /dev/md$mdstart $mntpoint + +mycc -o /tmp/swap -Wall -Wextra -O0 ../tools/swap.c || exit 1 +timeout -k 90 60 /tmp/swap -d 100 & +for i in `jot 10`; do + capacity=`swapinfo | tail -1 | sed 's/.* //; s/%//'` + [ $capacity -gt 1 ] && break + sleep 2 # Wait for swapping +done + +cd $mntpoint +/tmp/$prog; s=$? +pkill swap +wait +cmp $mntpoint/file $mntpoint/clone || { echo Fail; s=1; } +cd - + +umount $mntpoint +mdconfig -d -u $mdstart +rm /tmp/$prog /tmp/$prog.c +exit $s diff --git a/tools/test/stress2/misc/mmap41.sh b/tools/test/stress2/misc/mmap41.sh new file mode 100755 index 000000000000..5051681aaf31 --- /dev/null +++ b/tools/test/stress2/misc/mmap41.sh @@ -0,0 +1,160 @@ +#!/bin/sh + +# +# Copyright (c) 2024 Peter Holm +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Based on code from https://syzkaller.appspot.com/text?tag=ReproC&x=15d9baada80000 +# No problems seen + +. ../default.cfg + +prog=$(basename "$0" .sh) +odir=`pwd` +cd /tmp +sed '1,/^EOF/d' < $odir/$0 > $prog.c +mycc -o $prog -Wall -Wextra -O0 $prog.c -lpthread || exit 1 +rm -f $prog.c + +set -e +mount | grep "on $mntpoint " | grep -q /dev/md && umount -f $mntpoint +[ -c /dev/md$mdstart ] && mdconfig -d -u $mdstart +mdconfig -a -t swap -s 2g -u $mdstart +newfs $newfs_flags md$mdstart > /dev/null +mount /dev/md$mdstart $mntpoint +set +e + +$odir/../testcases/swap/swap -t 2m -i 10 > /dev/null & +cd $mntpoint +/tmp/$prog +cd $odir +while pkill swap; do :; done +wait + +for i in `jot 6`; do + mount | grep -q "on $mntpoint " || break + umount $mntpoint && break || sleep 10 + [ $i -eq 6 ] && + { echo FATAL; fstat -mf $mntpoint; exit 1; } +done +mdconfig -d -u $mdstart +rm -f /tmp/$prog +exit 0 + +EOF +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 /* 1 to enable */ +#define THREADS 2 + +static volatile int go; +static int fd; +static char *p, path[128]; + +#define ADDR (void *) 0x20000000ul +#define LEN 0x1000000ul + +void * +thr(void *arg) +{ + struct iovec iov; + long n, w; + char *p1; + + if (*(int *)arg == 0) { + while (go == 0) + usleep(100); + while (go == 1) { + if ((p1 = mmap(ADDR, LEN, PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) + err(1, "mmap() in %s", __func__); + usleep(arc4random() % 50); + if ((p1 = mmap(ADDR, LEN, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) + err(1, "mmap() in %s", __func__); + usleep(arc4random() % 10000); + } + } else { + while (go == 0) + usleep(100); + n = w = 0; + while (go == 1) { + iov.iov_base = p; + iov.iov_len = 0x100000; + if (pwritev(fd, &iov, 1, 0) != -1) + w++; + n++; + } + if (DEBUG == 1) + fprintf(stderr, "%ld out of %ld writes (%ld%%)\n", w, n, w * 100 / n); + } + + + return (0); +} + +void +test(void) +{ + pthread_t threads[THREADS]; + int nr[THREADS]; + int i, r; + + sprintf(path, "mmap.%06d", getpid()); + if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0622)) == -1) + err(1,"open()"); + + + if ((p = mmap(ADDR, LEN, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)) == MAP_FAILED) + err(1, "mmap() in %s", __func__); + + go = 0; + for (i = 0; i < THREADS; i++) { + nr[i] = i; + if ((r = pthread_create(&threads[i], NULL, thr, + (void *)&nr[i])) != 0) + errc(1, r, "pthread_create()"); + } + + go = 1; + sleep(60); + go = 2; + + for (i = 0; i < THREADS; i++) { + if ((r = pthread_join(threads[i], NULL)) != 0) + errc(1, r, "pthread_join(%d)", i); + } + close(fd); + if (DEBUG == 0) { + if (unlink(path) == -1) + err(1, "unlink(%s)", path); + } + + _exit(0); +} + +int +main(void) +{ + pid_t pid; + int i; + + for (i = 0; i < 2; i++) { + if ((pid = fork()) == 0) + test(); + if (waitpid(pid, NULL, 0) != pid) + err(1, "waitpid()"); + } +} diff --git a/tools/test/stress2/misc/mmap42.sh b/tools/test/stress2/misc/mmap42.sh new file mode 100755 index 000000000000..11235e581e73 --- /dev/null +++ b/tools/test/stress2/misc/mmap42.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +# Test scenario by: kib@ +# Test program obtained from Kyle Evans + +# Demonstrate UFS SU file corruption: +# ffs: on write into a buffer without content + +[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 + +. ../default.cfg + +set -u +prog=$(basename "$0" .sh) +s=0 +cat > /tmp/$prog.c < +#include + +#include +#include +#include +#include +#include + +#define FILE "file" + +int +main(void) +{ + struct stat sb; + ssize_t wsz; + size_t bufsz; + void *buf, *obuf; + int mfd, fd; + int done = 0; + + mfd = open(FILE, O_RDONLY); + assert(mfd >= 0); + + assert(fstat(mfd, &sb) == 0); + bufsz = sb.st_size; + buf = obuf = mmap(NULL, bufsz, PROT_READ, MAP_SHARED, mfd, 0); + assert(buf != MAP_FAILED); + + /* O_RDWR */ + fd = open(FILE, O_RDWR); + if (fd < 0) + err(1, "open"); + assert(fd >= 0); + +again: + while (bufsz > 0) { + wsz = write(fd, buf, bufsz); + if (wsz < 0) + err(1, "write"); + else if (wsz == 0) + fprintf(stderr, "Huh?\n"); + bufsz -= wsz; + buf += wsz; + } + + bufsz = sb.st_size; + buf = obuf; + + if (++done < 2) + goto again; + + close(fd); + munmap(obuf, sb.st_size); + close(mfd); + return (0); +} +EOF +mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1 + +mount | grep -q "on $mntpoint " && umount -f $mntpoint +mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart +mdconfig -s 32m -u $mdstart + +pagesize=$(sysctl -n hw.pagesize) +newfs -Un -b $pagesize /dev/md$mdstart > /dev/null +mount /dev/md$mdstart $mntpoint +dd if=/dev/random of=/mnt/file.orig bs=${pagesize} count=1 status=none +cp $mntpoint/file.orig $mntpoint/file +cat $mntpoint/file $mntpoint/file > $mntpoint/file.post +umount $mntpoint + +mount /dev/md$mdstart $mntpoint +(cd $mntpoint; /tmp/$prog) + +if ! cmp $mntpoint/file $mntpoint/file.post; then + echo "Files differ" + ls -l $mntpoint/file $mntpoint/file.post + s=1 +fi + +umount $mntpoint +mdconfig -d -u $mdstart +rm /tmp/$prog /tmp/$prog.c +exit $s diff --git a/tools/test/stress2/misc/mmap43.sh b/tools/test/stress2/misc/mmap43.sh new file mode 100755 index 000000000000..98f1de174d54 --- /dev/null +++ b/tools/test/stress2/misc/mmap43.sh @@ -0,0 +1,182 @@ +#!/bin/sh + +# Test program obtained from Kyle Evans + +# Demonstrate UFS SU file corruption + +[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 + +. ../default.cfg + +set -u +prog=$(basename "$0" .sh) +log=/tmp/$prog.log +rm -f $log +cat > /tmp/$prog.c < +#include + +#include +#include +#include +#include +#include + +#define FILE "file" + +int +main(void) +{ + struct stat sb; + ssize_t wsz; + size_t bufsz; + void *buf, *obuf; + int mfd, fd; + int done = 0; + + mfd = open(FILE, O_RDONLY); + assert(mfd >= 0); + + assert(fstat(mfd, &sb) == 0); + bufsz = sb.st_size; + buf = obuf = mmap(NULL, bufsz, PROT_READ, MAP_SHARED, mfd, 0); + assert(buf != MAP_FAILED); + + /* O_RDWR */ + fd = open(FILE, O_RDWR); + if (fd < 0) + err(1, "open"); + assert(fd >= 0); + +again: + while (bufsz > 0) { + wsz = write(fd, buf, bufsz); + if (wsz < 0) + err(1, "write"); + else if (wsz == 0) + fprintf(stderr, "Huh?\n"); + bufsz -= wsz; + buf += wsz; + } + + bufsz = sb.st_size; + buf = obuf; + + if (++done < 2) + goto again; + + close(fd); + munmap(obuf, sb.st_size); + close(mfd); + return (0); +} +EOF +mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1 + +cat > /tmp/$prog.serial.c < +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + size_t i, size; + long ix, *lp; + int fd; + char *file; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + file = argv[1]; + size = atol(argv[2]); + + if ((fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0) + err(1, "%s", file); + + if (lseek(fd, size - 1, SEEK_SET) == -1) + err(1, "lseek error"); + + /* write a dummy byte at the last location */ + if (write(fd, "\0", 1) != 1) + err(1, "write error"); + + if ((lp = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) + err(1, "mmap()"); + + for (i = 0, ix = 0; i < size; i += sizeof(long), ix++) + lp[ix] = ix; + + if (munmap(lp, size) == -1) + err(1, "munmap"); + close(fd); +} +EOF +mycc -o /tmp/$prog.serial -Wall -Wextra -O0 /tmp/$prog.serial.c || exit 1 + +mount | grep -q "on $mntpoint " && umount -f $mntpoint +mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart +mdconfig -s 5g -u $mdstart + +newfs -n $newfs_flags /dev/md$mdstart > /dev/null +mount /dev/md$mdstart $mntpoint + +here=`pwd` +cd $mntpoint + +size=875998990 +pagesize=`sysctl -n hw.pagesize` +tail=$((size % pagesize)) +/tmp/$prog.serial file $size + +cat file file > file.post +mv file file.orig +md5=`md5 < file.post` + +cp /usr/bin/sort /tmp/$prog.sort +counter=1 +n=$((`sysctl -n hw.ncpu`)) +[ $n -gt 10 ] && n=10 +s=0 +start=`date +%s` +while [ $((`date +%s` - start)) -lt 300 ]; do + st=`date +%s` + cp file.orig file + for i in `jot $n`; do + timeout -k 70s 1m /tmp/$prog.sort /dev/zero & + done + sleep $n + /tmp/$prog + while pkill $prog.sort; do sleep .2; done + wait + m=`md5 < file` + if [ $md5 != $m ]; then + echo "Failed @ iteration $counter" + ls -l + od -t x8 file > /var/tmp/$prog.file1 + od -t x8 file.post > /var/tmp/$prog.file2 + diff /var/tmp/$prog.file1 /var/tmp/$prog.file2 > $log + head -10 $log + rm /var/tmp/$prog.file1 /var/tmp/$prog.file2 + s=1 + break + fi + echo "`date +%T` Loop #$counter, elapsed $((`date +%s` - st)) seconds." + counter=$((counter + 1)) +done +cd $here + +umount $mntpoint +mdconfig -d -u $mdstart +rm /tmp/$prog /tmp/$prog.c /tmp/$prog.sort +[ $s -eq 0 ] && + printf "OK File size is %9d, tail is %4d bytes. (%3d loops)\n" $size $tail $counter || + printf "FAIL File size is %9d, tail is %4d bytes. (%3d loops)\n" $size $tail $counter +exit $s