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);

Reply via email to