DragonFlyBSD Kernel Audit
DF-2831 / fix.diff
← back to finding ↓ download raw
--- subr_diskslice.c.orig
+++ subr_diskslice.c
@@ -769,6 +769,8 @@
 	ssp = *sspp;
 	dev->si_bsize_phys = info->d_media_blksize;
 	slice = dkslice(dev);
+	if (slice >= ssp->dss_nslices)
+		return (EINVAL);
 	part = dkpart(dev);
 	sp = &ssp->dss_slices[slice];
 	dssetmask(sp, part);
@@ -857,6 +859,12 @@
 		dev_dclose(dev, FREAD, S_IFCHR, NULL);
 		ssp = *sspp;
 	}
+	/*
+	 * The re-open above may have serialized with a reprobe; the
+	 * reloaded (or original) ssp must be revalidated before use.
+	 */
+	if (slice >= ssp->dss_nslices)
+		return (-1);
 	lp = ssp->dss_slices[slice].ds_label;
 	if (part == WHOLE_SLICE_PART) {
 		blocks = ssp->dss_slices[slice].ds_size;
--- subr_disk.c.orig
+++ subr_disk.c
@@ -1265,12 +1265,33 @@
 	if (dp == NULL)
 		return(ENODEV);
 
+	/*
+	 * dssize() walks dp->d_slice (and the per-slice disklabel) with no
+	 * internal serialization while disk_probe()/disk_msg_core replace
+	 * and dsgone() those structures under ds_token (DF-2831).  Run the
+	 * whole walk under ds_token like diskopen/diskclose/diskioctl do.
+	 * Token acquisition is recursive for the dev_dopen() path inside
+	 * dssize() (diskopen takes ds_token itself), and blocking inside
+	 * the token (sync reprobe messages) releases/reacquires it.
+	 */
+	lwkt_gettoken(&ds_token);
 	ap->a_result = dssize(dev, &dp->d_slice);
+	lwkt_reltoken(&ds_token);
 
 	if ((ap->a_result == -1) &&
 	   (dp->d_info.d_dsflags & DSO_RAWPSIZE)) {
-		ap->a_head.a_dev = dp->d_rawdev;
-		return dev_doperate(&ap->a_head);
+		/*
+		 * The raw size describes the whole disk.  Substituting it
+		 * for a partition (label-dependent) device silently lies
+		 * about the geometry (DF-2831: swapon on a transiently
+		 * labelless partition installed whole-disk-sized swap).
+		 * Only fall back for whole-disk or whole-slice devices.
+		 */
+		if (dkslice(dev) == WHOLE_DISK_SLICE ||
+		    dkpart(dev) == WHOLE_SLICE_PART) {
+			ap->a_head.a_dev = dp->d_rawdev;
+			return dev_doperate(&ap->a_head);
+		}
 	}
 	return(0);
 }