1#!/bin/sh 2 3# Bug 275570 - self-referential nullfs mount over tmpfs in combination with MNT_UPDATE results in a hang 4 5# Test scenario mounts: 6# tmpfs on /mnt (tmpfs, local, noexec, read-only) 7# .setup-done on /mnt/.setup-done (nullfs, local) 8 9# Hang seen on a GENERIC kernel only 10# UID PID PPID C PRI NI VSZ RSS MWCHAN STAT TT TIME COMMAND 11# 0 4429 4417 4 59 0 14192 2832 vlp2 D+ 0 0:00.06 ./nullfs33 12# https://people.freebsd.org/~pho/stress/log/log0663.txt 13 14 15. ../default.cfg 16set -u 17prog=$(basename "$0" .sh) 18 19cat > /tmp/$prog.c <<EOF 20#include <assert.h> 21#include <err.h> 22#include <errno.h> 23#include <stdlib.h> 24#include <stdio.h> 25#include <string.h> 26#include <unistd.h> 27#include <sys/_iovec.h> 28#include <sys/file.h> 29#include <sys/mount.h> 30 31static void xmount(const char* fstype, const char* from, const char* to, int flags) { 32 33 char errmsg[255]; 34 errmsg[0] = '\0'; 35 36 int capacity = 8; 37 38 struct iovec* iov = malloc(sizeof(struct iovec) * capacity); 39 40 iov[0].iov_base = "fstype"; 41 iov[0].iov_len = sizeof("fstype"); 42 iov[1].iov_base = __DECONST(char*, fstype); 43 iov[1].iov_len = strlen(fstype) + 1; 44 45 iov[2].iov_base = "fspath"; 46 iov[2].iov_len = sizeof("fspath"); 47 iov[3].iov_base = __DECONST(char*, to); 48 iov[3].iov_len = strlen(to) + 1; 49 50 iov[4].iov_base = "from"; 51 iov[4].iov_len = sizeof("from"); 52 iov[5].iov_base = __DECONST(char*, from); 53 iov[5].iov_len = strlen(from) + 1; 54 55 iov[6].iov_base = "errmsg"; 56 iov[6].iov_len = sizeof("errmsg"); 57 iov[7].iov_base = errmsg; 58 iov[7].iov_len = sizeof(errmsg); 59 60 int len = 8; 61 62 if (flags & MNT_NOCOVER) { 63 64 assert(len + 2 <= capacity); 65 66 iov[len].iov_base = "nocover"; 67 iov[len].iov_len = sizeof("nocover"); 68 len++; 69 70 iov[len].iov_base = NULL; 71 iov[len].iov_len = 0; 72 len++; 73 } 74 75 assert(len == capacity); 76 77 if (nmount(iov, len, (int)flags) == -1) { 78 err(EXIT_FAILURE, "nmount %s -> %s: %s", from, to, errmsg); 79 } 80 81 free(iov); 82} 83 84static void xchdir(const char* path) { 85 if (chdir(path) == -1) { 86 err(EXIT_FAILURE, "chdir(\"%s\")", path); 87 } 88} 89 90static void xcreat(char* path, int mode) { 91 int fd = open(path, O_CREAT | O_WRONLY, mode); 92 if (fd == -1) { 93 err(EXIT_FAILURE, "can't create %s", path); 94 } 95 close(fd); 96} 97 98//#define ROOT_DIR "/mnt" 99#define ROOT_DIR "$mntpoint" 100 101int main(int argc __unused, char* argv[] __unused) { 102 103 xchdir(ROOT_DIR); 104 105 xmount("tmpfs", "tmpfs", ".", MNT_NOEXEC); 106 107 xcreat(".setup-done", 0444); 108 xmount("nullfs", ".setup-done", ".setup-done", 0); 109 110 xmount("tmpfs", "tmpfs", ".", MNT_RDONLY | MNT_NOEXEC | MNT_UPDATE); 111 112 unmount(ROOT_DIR "/.setup-done", MNT_FORCE); 113 unmount(ROOT_DIR, MNT_FORCE); 114 115 return 0; 116} 117EOF 118cc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 1 119 120cd /tmp 121start=`date +%s` 122while [ $((`date +%s` - start)) -lt 60 ]; do 123 ./$prog || break 124done 125cd - 126 127mount | grep -q "on $mntpoint " && umount $mntpoint 128rm -f /tmp/$prog /tmp/$prog.c 129exit 0 130