DF-0117 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 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); } |