DF-3161 / umount_fifoleak.sh
#!/bin/sh # DF-3161 PoC: `umount -f` force-close loop never passes FREAD, so # fifo_close() never decrements fi_readers -> the fifoinfo and its # connected AF_LOCAL socket pair are leaked permanently. # # Observable: connected unix-domain socket count (netstat -f unix). # CONTROL: fifo opened on tmpfs, holder exits normally -> count returns # to baseline (fifo_close destroys the fip). # BUG RUN: fifo opened on tmpfs, `umount -f` while held, then holder # exits -> count stays +2 above baseline forever (sockets leaked). # # Must run as root (needs mount/umount -f). set -u D=/mnt/df3161 count_sockpairs() { netstat -f unix 2>/dev/null | awk '$2 == "stream" { c++ } END { print c + 0 }' } [ "$(id -u)" = 0 ] || { echo "must be root"; exit 2; } mkdir -p "$D" echo "== baseline ==" BASE=$(count_sockpairs) echo "baseline connected-stream sockets: $BASE" echo echo "== CONTROL: open fifo on tmpfs, close normally (no umount -f) ==" mount -t tmpfs tmpfs "$D" || exit 2 mkfifo "$D/f" ./fifo_hold "$D/f" & H=$! sleep 1 CTRL_OPEN=$(count_sockpairs) echo "with fifo open: $CTRL_OPEN (baseline+$((CTRL_OPEN-BASE)); +2 expected)" kill -TERM $H; wait $H 2>/dev/null sleep 1 CTRL_CLOSED=$(count_sockpairs) echo "after holder exit: $CTRL_CLOSED (baseline+$((CTRL_CLOSED-BASE)); 0 expected if no leak)" umount "$D" || echo "(control umount failed: $?)" echo echo "== BUG RUN: open fifo on tmpfs, umount -f, then close ==" mount -t tmpfs tmpfs "$D" || exit 2 mkfifo "$D/f" ./fifo_hold "$D/f" & H=$! sleep 1 BUG_OPEN=$(count_sockpairs) echo "with fifo open: $BUG_OPEN (baseline+$((BUG_OPEN-BASE)); +2 expected)" echo "umount -f $D" umount -f "$D" || echo "(umount -f rc=$?)" kill -TERM $H; wait $H 2>/dev/null sleep 1 BUG_CLOSED=$(count_sockpairs) echo "after holder exit: $BUG_CLOSED (baseline+$((BUG_CLOSED-BASE)))" sleep 3 BUG_CLOSED2=$(count_sockpairs) echo "after 3s more: $BUG_CLOSED2 (baseline+$((BUG_CLOSED2-BASE)))" umount "$D" 2>/dev/null echo echo "== VERDICT ==" if [ $((BUG_CLOSED - BASE)) -ge 2 ] && [ $((CTRL_CLOSED - BASE)) -eq 0 ]; then echo "reproduced: socket pair leaked after umount -f + close " \ "(+2 sockets persist; control returns to baseline)" exit 0 else echo "not_reproduced (ctrl delta=$((CTRL_CLOSED-BASE)) bug delta=$((BUG_CLOSED-BASE)))" exit 1 fi |