DF-2875 / 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 124 125 126 127 128 | --- a/sys/kern/subr_diskiocom.c 2026-09-02 18:33:18.862281095 +0000 +++ b/sys/kern/subr_diskiocom.c 2026-09-02 18:33:36.210063774 +0000 @@ -425,6 +425,8 @@ * the buffer cache buffer for the case where we can just * use the message's data pointer. */ + kdmsg_data_t *adata; + reterr = 0; if (msg->aux_size >= msg->any.blk_write.bytes) bp = getpbuf(NULL); @@ -435,14 +437,25 @@ bp->b_cmd = BUF_CMD_WRITE; bp->b_bcount = msg->any.blk_write.bytes; bp->b_resid = bp->b_bcount; + bio->bio_caller_info2.ptr = NULL; if (msg->aux_size >= msg->any.blk_write.bytes) { + /* + * DF-2875: the detached aux buffer must be owned by + * THIS bio, not by the shared single-slot + * iost->data. Pipelined writes in one transaction + * used to overwrite the slot, freeing the most + * recent buffer while its bio was still in flight + * and leaking all earlier ones. + */ bp->b_data = msg->aux_data; - kdmsg_detach_aux_data(msg, &iost->data); + adata = kmalloc(sizeof(*adata), M_DEVBUF, + M_WAITOK | M_ZERO); + kdmsg_detach_aux_data(msg, adata); + bio->bio_caller_info2.ptr = adata; } else { bcopy(msg->aux_data, bp->b_data, msg->aux_size); bzero(bp->b_data + msg->aux_size, msg->any.blk_write.bytes - msg->aux_size); - bzero(&iost->data, sizeof(iost->data)); } bio->bio_offset = msg->any.blk_write.offset; bio->bio_caller_info1.ptr = msg->state; @@ -595,6 +608,17 @@ cmd = DMSG_LNK_ERROR; data = bp->b_data; bytes = bp->b_bcount; + /* + * DF-2876: on error or short reads the buffer content is + * stale (whatever a previous pbuf user left behind); do + * not disclose it. Return only completed bytes. + */ + if (bp->b_flags & B_ERROR) { + data = NULL; + bytes = 0; + } else if (bp->b_resid) { + bytes = bp->b_bcount - bp->b_resid; + } /* fall through */ case BUF_CMD_WRITE: if (bp->b_flags & B_ERROR) { @@ -603,7 +627,18 @@ error = 0; resid = bp->b_resid; } - kdmsg_free_aux_data(&iost->data); + /* + * DF-2875: free exactly the aux data owned by THIS bio, + * not the shared iost->data slot. + */ + { + kdmsg_data_t *adata = bio->bio_caller_info2.ptr; + if (adata) { + kdmsg_free_aux_data(adata); + kfree(adata, M_DEVBUF); + bio->bio_caller_info2.ptr = NULL; + } + } break; case BUF_CMD_FLUSH: case BUF_CMD_FREEBLKS: --- a/sys/kern/kern_dmsg.c 2026-09-02 18:33:18.862281095 +0000 +++ b/sys/kern/kern_dmsg.c 2026-09-02 18:33:36.234063474 +0000 @@ -136,11 +136,28 @@ /* * Destroy the current connection */ + int retries; + + /* + * Destroy the current connection + */ lockmgr(&iocom->msglk, LK_EXCLUSIVE); atomic_set_int(&iocom->msg_ctl, KDMSG_CLUSTERCTL_KILLRX); + retries = 10; while (iocom->msgrd_td || iocom->msgwr_td) { wakeup(&iocom->msg_ctl); lksleep(iocom, &iocom->msglk, 0, "clstrkl", hz); + /* + * DF-2877: the old reader can be parked in fp_read() on a + * non-cooperating peer socket forever. Force EOF after + * 10 seconds just like kdmsg_iocom_uninit() does, so this + * ioctl eventually returns instead of deadlocking the + * caller in an unkillable state. + */ + if (--retries == 0 && iocom->msg_fp) { + fp_shutdown(iocom->msg_fp, SHUT_RDWR); + /* retries allowed to go negative, keep looping */ + } } /* --- a/sys/kern/subr_disk.c 2026-09-02 18:33:18.862281095 +0000 +++ b/sys/kern/subr_disk.c 2026-09-02 18:33:36.234063474 +0000 @@ -1189,6 +1189,17 @@ } if (ap->a_cmd == DIOCRECLUSTER && dev == dp->d_cdev) { + /* + * DF-2874: wiring a kernel-side raw-disk dmsg server to an + * arbitrary fd turns the kernel into a root-credential + * proxy for the peer (dev_dopen with proc0.p_ucred and + * unrestricted raw strategy); require write access to the + * device node as defense-in-depth. + */ + if ((ap->a_fflag & FWRITE) == 0) { + error = EPERM; + return error; + } error = disk_iocom_ioctl(dp, ap->a_cmd, ap->a_data); return error; } |