Hello everyone!
Actually I was working on a way to create a degraded RAID.
As the ioctl create RAID syscall takes a list of dev_t,
I tried NODEV for Not yet Online DEVice. ;-)
I expected the kernel to complain. But instead it crashed.
How to reproduce:
1) Apply the diff below.
2) Build (just) sbin/bioctl.
3) Run: bioctl -c 1 -l XdYZ,OFFLINE softraid0 # XdYZ at your choice
4) The system crashes.
Short version:
--- sbin/bioctl/bioctl.c.old Fri Apr 26 07:45:28 2024
+++ sbin/bioctl/bioctl.c Tue Jan 2 00:14:59 2024
@@ -1015,16 +1026,20 @@
/* got one */
sz = e - s + 1;
strlcpy(dev, s, sz + 1);
- fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
- if (fd == -1)
- err(1, "could not open %s", dev);
- if (fstat(fd, &sb) == -1) {
- int saved_errno = errno;
+ if (strcmp(dev, "OFFLINE")) {
+ fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
+ if (fd == -1)
+ err(1, "could not open %s", dev);
+ if (fstat(fd, &sb) == -1) {
+ int saved_errno = errno;
+ close(fd);
+ errc(1, saved_errno, "could not stat
%s", dev);
+ }
close(fd);
- errc(1, saved_errno, "could not stat %s", dev);
+ dt[no_dev] = sb.st_rdev;
+ } else {
+ dt[no_dev] = NODEV;
}
- close(fd);
- dt[no_dev] = sb.st_rdev;
no_dev++;
if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t)))
errx(1, "too many devices on device list");
Long version:
--- sbin/bioctl/bioctl.c.old Fri Apr 26 07:45:28 2024
+++ sbin/bioctl/bioctl.c Tue Jan 2 00:14:59 2024
@@ -833,9 +833,9 @@
struct sr_crypto_kdfinfo kdfinfo;
struct sr_crypto_pbkdf kdfhint;
struct stat sb;
- int rv, no_dev, fd;
+ int rv, no_dev, online = 0, fd, i;
dev_t *dt;
- u_int16_t min_disks = 0;
+ u_int16_t min_disks = 0, min_online;
if (!dev_list)
errx(1, "no devices specified");
@@ -845,6 +845,7 @@
err(1, "not enough memory for dev_t list");
no_dev = bio_parse_devlist(dev_list, dt);
+ min_online = no_dev;
switch (level) {
case 0:
@@ -852,12 +853,15 @@
break;
case 1:
min_disks = 2;
+ min_online = 1;
break;
case 5:
min_disks = 3;
+ min_online = no_dev - 1;
break;
- case 'C':
case 0x1C:
+ min_online = 1;
+ case 'C':
min_disks = 1;
break;
case 'c':
@@ -870,6 +874,13 @@
if (no_dev < min_disks)
errx(1, "not enough disks");
+ for (i = 0; i < no_dev; i++)
+ if (dt[i] != NODEV)
+ online++;
+
+ if (online < min_online)
+ errx(1, "not enough disks online");
+
/* for crypto raid we only allow one single chunk */
if (level == 'C' && no_dev != min_disks)
errx(1, "not exactly one partition");
@@ -1015,16 +1026,20 @@
/* got one */
sz = e - s + 1;
strlcpy(dev, s, sz + 1);
- fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
- if (fd == -1)
- err(1, "could not open %s", dev);
- if (fstat(fd, &sb) == -1) {
- int saved_errno = errno;
+ if (strcmp(dev, "OFFLINE")) {
+ fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
+ if (fd == -1)
+ err(1, "could not open %s", dev);
+ if (fstat(fd, &sb) == -1) {
+ int saved_errno = errno;
+ close(fd);
+ errc(1, saved_errno, "could not stat
%s", dev);
+ }
close(fd);
- errc(1, saved_errno, "could not stat %s", dev);
+ dt[no_dev] = sb.st_rdev;
+ } else {
+ dt[no_dev] = NODEV;
}
- close(fd);
- dt[no_dev] = sb.st_rdev;
no_dev++;
if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t)))
errx(1, "too many devices on device list");
@@ -1034,7 +1049,7 @@
for (i = 0; i < no_dev; i++)
for (x = 0; x < no_dev; x++)
- if (dt[i] == dt[x] && x != i)
+ if (dt[i] == dt[x] && x != i && dt[i] != NODEV)
errx(1, "duplicate device in list");
return (no_dev);