DragonFlyBSD Kernel Audit
DF-0117 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/subr_diskiocom.c b/sys/kern/subr_diskiocom.c
--- a/sys/kern/subr_diskiocom.c
+++ b/sys/kern/subr_diskiocom.c
@@ -383,11 +383,25 @@
 done:
 	if (reterr) {
 		if (msg->any.head.cmd & DMSGF_DELETE) {
-			if (iost && iost->count == 0) {
-				kfree(iost, M_DEVBUF);
-				msg->state->any.any = NULL;
+			if (iost && iost->count > 0) {
+				/*
+				 * I/O still in flight: defer the DELETE reply to
+				 * diskiodone.  Replying now would let cleanuptx
+				 * tear down the state (parent=NULL, removed from
+				 * rbtree) while async disk I/O still holds a raw
+				 * pointer to it in bio_caller_info1.ptr -- UAF in
+				 * diskiodone (DF-0117).  Setting eof=1 makes the
+				 * last diskiodone send the DELETE reply itself,
+				 * after all derefs are done.
+				 */
+				iost->eof = 1;
+			} else {
+				if (iost) {
+					kfree(iost, M_DEVBUF);
+					msg->state->any.any = NULL;
+				}
+				kdmsg_msg_reply(msg, error);
 			}
-			kdmsg_msg_reply(msg, error);
 		} else {
 			kdmsg_msg_result(msg, error);
 		}
@@ -459,11 +473,25 @@
 done:
 	if (reterr) {
 		if (msg->any.head.cmd & DMSGF_DELETE) {
-			if (iost && iost->count == 0) {
-				kfree(iost, M_DEVBUF);
-				msg->state->any.any = NULL;
+			if (iost && iost->count > 0) {
+				/*
+				 * I/O still in flight: defer the DELETE reply to
+				 * diskiodone.  Replying now would let cleanuptx
+				 * tear down the state (parent=NULL, removed from
+				 * rbtree) while async disk I/O still holds a raw
+				 * pointer to it in bio_caller_info1.ptr -- UAF in
+				 * diskiodone (DF-0117).  Setting eof=1 makes the
+				 * last diskiodone send the DELETE reply itself,
+				 * after all derefs are done.
+				 */
+				iost->eof = 1;
+			} else {
+				if (iost) {
+					kfree(iost, M_DEVBUF);
+					msg->state->any.any = NULL;
+				}
+				kdmsg_msg_reply(msg, error);
 			}
-			kdmsg_msg_reply(msg, error);
 		} else {
 			kdmsg_msg_result(msg, error);
 		}
@@ -510,11 +538,25 @@
 	}
 	if (reterr) {
 		if (msg->any.head.cmd & DMSGF_DELETE) {
-			if (iost && iost->count == 0) {
-				kfree(iost, M_DEVBUF);
-				msg->state->any.any = NULL;
+			if (iost && iost->count > 0) {
+				/*
+				 * I/O still in flight: defer the DELETE reply to
+				 * diskiodone.  Replying now would let cleanuptx
+				 * tear down the state (parent=NULL, removed from
+				 * rbtree) while async disk I/O still holds a raw
+				 * pointer to it in bio_caller_info1.ptr -- UAF in
+				 * diskiodone (DF-0117).  Setting eof=1 makes the
+				 * last diskiodone send the DELETE reply itself,
+				 * after all derefs are done.
+				 */
+				iost->eof = 1;
+			} else {
+				if (iost) {
+					kfree(iost, M_DEVBUF);
+					msg->state->any.any = NULL;
+				}
+				kdmsg_msg_reply(msg, error);
 			}
-			kdmsg_msg_reply(msg, error);
 		} else {
 			kdmsg_msg_result(msg, error);
 		}
@@ -561,11 +603,25 @@
 	}
 	if (reterr) {
 		if (msg->any.head.cmd & DMSGF_DELETE) {
-			if (iost && iost->count == 0) {
-				kfree(iost, M_DEVBUF);
-				msg->state->any.any = NULL;
+			if (iost && iost->count > 0) {
+				/*
+				 * I/O still in flight: defer the DELETE reply to
+				 * diskiodone.  Replying now would let cleanuptx
+				 * tear down the state (parent=NULL, removed from
+				 * rbtree) while async disk I/O still holds a raw
+				 * pointer to it in bio_caller_info1.ptr -- UAF in
+				 * diskiodone (DF-0117).  Setting eof=1 makes the
+				 * last diskiodone send the DELETE reply itself,
+				 * after all derefs are done.
+				 */
+				iost->eof = 1;
+			} else {
+				if (iost) {
+					kfree(iost, M_DEVBUF);
+					msg->state->any.any = NULL;
+				}
+				kdmsg_msg_reply(msg, error);
 			}
-			kdmsg_msg_reply(msg, error);
 		} else {
 			kdmsg_msg_result(msg, error);
 		}