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