Module Name:    src
Committed By:   jdolecek
Date:           Sat Sep 22 16:14:25 UTC 2018

Modified Files:
        src/sys/dev/ata [jdolecek-ncqfixes]: TODO.ncq ata.c atavar.h wd.c
            wdvar.h

Log Message:
fix use-after-free in wd(4) dump, detected by switch to the pool

change code in wd_dumpblocks() to use it's own non-pool ata_xfer,
which skips the deallocation step and thus keeps the contents when the I/O
is finished


To generate a diff of this commit:
cvs rdiff -u -r1.4.2.5 -r1.4.2.6 src/sys/dev/ata/TODO.ncq
cvs rdiff -u -r1.141.6.8 -r1.141.6.9 src/sys/dev/ata/ata.c
cvs rdiff -u -r1.99.2.5 -r1.99.2.6 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.441.2.4 -r1.441.2.5 src/sys/dev/ata/wd.c
cvs rdiff -u -r1.46.6.2 -r1.46.6.3 src/sys/dev/ata/wdvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ata/TODO.ncq
diff -u src/sys/dev/ata/TODO.ncq:1.4.2.5 src/sys/dev/ata/TODO.ncq:1.4.2.6
--- src/sys/dev/ata/TODO.ncq:1.4.2.5	Sat Sep 22 09:26:48 2018
+++ src/sys/dev/ata/TODO.ncq	Sat Sep 22 16:14:25 2018
@@ -1,8 +1,6 @@
 jdolecek-ncqfixes goals:
 - add to wd(4) a callout to restart buf queue processing when ata_get_xfer()
   call fails and remove ata_channel_start()
-- change wd(4) dump code to use preallocated or on-stack ata_xfer to not rely
-  on pool having memory
 - re-fix QEMU ahci(4) bug workaround (no READ LOG EXT support) - now it
   triggers KASSERT()
 - fix ahci(4) error handling under paralles - invalid bio via WD_CHAOS_MONKEY

Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.141.6.8 src/sys/dev/ata/ata.c:1.141.6.9
--- src/sys/dev/ata/ata.c:1.141.6.8	Sat Sep 22 12:20:31 2018
+++ src/sys/dev/ata/ata.c	Sat Sep 22 16:14:25 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ata.c,v 1.141.6.8 2018/09/22 12:20:31 jdolecek Exp $	*/
+/*	$NetBSD: ata.c,v 1.141.6.9 2018/09/22 16:14:25 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.141.6.8 2018/09/22 12:20:31 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.141.6.9 2018/09/22 16:14:25 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -1350,7 +1350,7 @@ ata_free_xfer(struct ata_channel *chp, s
 
 	ata_channel_lock(chp);
 
-	if (xfer->c_flags & (C_WAITACT|C_WAITTIMO)) {
+	if (__predict_false(xfer->c_flags & (C_WAITACT|C_WAITTIMO))) {
 		/* Someone is waiting for this xfer, so we can't free now */
 		xfer->c_flags |= C_FREE;
 		cv_broadcast(&chq->c_active);
@@ -1360,7 +1360,7 @@ ata_free_xfer(struct ata_channel *chp, s
 
 	/* XXX move PIOBM and free_gw to deactivate? */
 #if NATA_PIOBM		/* XXX wdc dependent code */
-	if (xfer->c_flags & C_PIOBM) {
+	if (__predict_false(xfer->c_flags & C_PIOBM)) {
 		struct wdc_softc *wdc = CHAN_TO_WDC(chp);
 
 		/* finish the busmastering PIO */
@@ -1375,7 +1375,8 @@ ata_free_xfer(struct ata_channel *chp, s
  
 	ata_channel_unlock(chp);
 
-	pool_put(&ata_xfer_pool, xfer);
+	if (__predict_true(!ISSET(xfer->c_flags, C_PRIVATE_ALLOC)))
+		pool_put(&ata_xfer_pool, xfer);
 }
 
 void

Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.99.2.5 src/sys/dev/ata/atavar.h:1.99.2.6
--- src/sys/dev/ata/atavar.h:1.99.2.5	Sat Sep 22 09:22:59 2018
+++ src/sys/dev/ata/atavar.h	Sat Sep 22 16:14:25 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: atavar.h,v 1.99.2.5 2018/09/22 09:22:59 jdolecek Exp $	*/
+/*	$NetBSD: atavar.h,v 1.99.2.6 2018/09/22 16:14:25 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -198,6 +198,7 @@ struct ata_xfer_ops {
 #define C_WAITTIMO	0x0400		/* race vs. timeout */
 #define C_CHAOS		0x0800		/* forced error xfer */
 #define C_RECOVERED	0x1000		/* error recovered, no need for reset */
+#define C_PRIVATE_ALLOC	0x2000		/* private alloc, skip pool_put() */
 
 /* reasons for c_kill_xfer() */
 #define KILL_GONE 1		/* device is gone while xfer was active */

Index: src/sys/dev/ata/wd.c
diff -u src/sys/dev/ata/wd.c:1.441.2.4 src/sys/dev/ata/wd.c:1.441.2.5
--- src/sys/dev/ata/wd.c:1.441.2.4	Sat Sep 22 09:22:59 2018
+++ src/sys/dev/ata/wd.c	Sat Sep 22 16:14:25 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: wd.c,v 1.441.2.4 2018/09/22 09:22:59 jdolecek Exp $ */
+/*	$NetBSD: wd.c,v 1.441.2.5 2018/09/22 16:14:25 jdolecek Exp $ */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.441.2.4 2018/09/22 09:22:59 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.441.2.5 2018/09/22 16:14:25 jdolecek Exp $");
 
 #include "opt_ata.h"
 #include "opt_wd.h"
@@ -1458,7 +1458,7 @@ wd_dumpblocks(device_t dev, void *va, da
 	struct wd_softc *wd = device_private(dev);
 	struct dk_softc *dksc = &wd->sc_dksc;
 	struct disk_geom *dg = &dksc->sc_dkdev.dk_geom;
-	struct ata_xfer *xfer;
+	struct ata_xfer *xfer = &wd->dump_xfer;
 	int err;
 
 	/* Recalibrate, if first dump transfer. */
@@ -1469,11 +1469,8 @@ wd_dumpblocks(device_t dev, void *va, da
 		wd->drvp->state = RESET;
 	}
 
-	xfer = ata_get_xfer(wd->drvp->chnl_softc, false);
-	if (xfer == NULL) {
-		printf("%s: no xfer\n", __func__);
-		return EAGAIN;
-	}
+	memset(xfer, 0, sizeof(*xfer));
+	xfer->c_flags |= C_PRIVATE_ALLOC;
 
 	xfer->c_bio.blkno = blkno;
 	xfer->c_bio.flags = ATA_POLL;
@@ -1519,7 +1516,7 @@ wd_dumpblocks(device_t dev, void *va, da
 		err = 0;
 		break;
 	default:
-		panic("wddump: unknown error type %d", err);
+		panic("wddump: unknown error type %x", err);
 	}
 
 	if (err != 0) {

Index: src/sys/dev/ata/wdvar.h
diff -u src/sys/dev/ata/wdvar.h:1.46.6.2 src/sys/dev/ata/wdvar.h:1.46.6.3
--- src/sys/dev/ata/wdvar.h:1.46.6.2	Sat Sep 22 09:22:59 2018
+++ src/sys/dev/ata/wdvar.h	Sat Sep 22 16:14:25 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: wdvar.h,v 1.46.6.2 2018/09/22 09:22:59 jdolecek Exp $	*/
+/*	$NetBSD: wdvar.h,v 1.46.6.3 2018/09/22 16:14:25 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -71,6 +71,8 @@ struct wd_softc {
 	SLIST_HEAD(, ata_xfer) sc_requeue_list;
 	struct callout sc_requeue_callout;	/* requeue callout handle */
 
+	struct ata_xfer dump_xfer;
+
 	/* Sysctl nodes specific for the disk */
 	struct sysctllog *nodelog;
 	bool drv_ncq;

Reply via email to