--- a/sys/dev/disk/nata/ata-all.c +++ b/sys/dev/disk/nata/ata-all.c @@ -83,6 +83,7 @@ int ata_wc = 1; /* local vars */ +static struct lock ata_channel_ctl_lock; /* DF-4476: serialize channel control ioctls */ static int ata_dma = 1; static int atapi_dma = 1; @@ -385,8 +386,10 @@ if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; + lockmgr(&ata_channel_ctl_lock, LK_EXCLUSIVE); error = ata_reinit(device); ata_start(device); + lockmgr(&ata_channel_ctl_lock, LK_RELEASE); break; case IOCATAATTACH: @@ -394,14 +397,18 @@ !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; /* XXX SOS should enable channel HW on controller */ + lockmgr(&ata_channel_ctl_lock, LK_EXCLUSIVE); error = ata_attach(device); + lockmgr(&ata_channel_ctl_lock, LK_RELEASE); break; case IOCATADETACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; + lockmgr(&ata_channel_ctl_lock, LK_EXCLUSIVE); error = ata_detach(device); + lockmgr(&ata_channel_ctl_lock, LK_RELEASE); /* XXX SOS should disable channel HW on controller */ break; @@ -485,7 +492,17 @@ request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } - request->timeout = ioc_request->timeout; + /* + * DF-4475: clamp the untrusted timeout. A negative timeout makes + * the ata-queue completion wait (timeout * hz * 4) wrap to a ~1-tick + * expiry, so a request merely queued behind others hits the + * "taskqueue timeout" fallback and is then freed by this ioctl + * while the channel still owns it (use-after-free). + */ + if (ioc_request->timeout <= 0) + request->timeout = ATA_DEFAULT_TIMEOUT; + else + request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; @@ -1077,6 +1094,7 @@ objcache_malloc_alloc, objcache_malloc_free, &ata_request_malloc_args); + lockinit(&ata_channel_ctl_lock, "atactl", 0, 0); ata_composite_cache = objcache_create("ata_composite", 0, 0, ata_composite_cache_ctor, NULL, NULL, objcache_malloc_alloc, --- a/sys/dev/disk/nata/ata-queue.c +++ b/sys/dev/disk/nata/ata-queue.c @@ -144,10 +144,16 @@ if (!(request->flags & ATA_R_COMPLETED)) { /* apparently not */ device_printf(request->dev, "WARNING - %s taskqueue timeout - " - "completing request directly\n", + "waiting for channel completion\n", ata_cmd2str(request)); - request->flags |= ATA_R_DANGER1; - ata_completed(request, 0); + /* + * DF-4475: the channel (queue / running / taskqueue) may + * still own this request. Do NOT complete it here and do + * NOT let the issuer free it: keep waiting; the channel's + * own ata_timeout callout guarantees completion. + */ + while (!(request->flags & ATA_R_COMPLETED)) + lksleep(request, &request->done, 0, "atareqcw", hz); } } lockuninit(&request->done);