DF-4013 / fix.diff
diff --git a/sys/dev/raid/mps/mps_user.c b/sys/dev/raid/mps/mps_user.c --- a/sys/dev/raid/mps/mps_user.c 2026-09-17 14:20:44.382606971 +0000 +++ b/sys/dev/raid/mps/mps_user.c 2026-09-17 14:20:44.398606765 +0000 @@ -1373,6 +1373,22 @@ *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; return (MPS_DIAG_FAILURE); } + + /* + * DF-4013: the driver owns exactly ONE shared DMA trio + * (fw_diag_dmat/fw_diag_buffer/fw_diag_map/fw_diag_busaddr) for + * all three buffer types. Registering a second type while a trio + * is live used to silently replace the pointers (leaking the old + * allocation) while the first type kept its per-type size, so + * later reads were bounds-checked against a stale size and ran off + * the new allocation. Refuse the second registration (the + * immediate-takeover path above remains the only legal re-use). + */ + if (sc->fw_diag_buffer != NULL) { + *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; + return (MPS_DIAG_FAILURE); + } + if (bus_dma_tag_create( sc->mps_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ @@ -1424,6 +1440,15 @@ sc->fw_diag_map); if (sc->fw_diag_dmat != NULL) bus_dma_tag_destroy(sc->fw_diag_dmat); + /* + * DF-4013: pBuffer stays registered (unique_id/size) when + * the post fails; never leave the freed trio dangling. + */ + sc->fw_diag_buffer = NULL; + sc->fw_diag_dmat = NULL; + sc->fw_diag_map = NULL; + sc->fw_diag_busaddr = 0; + pBuffer->size = 0; } return (status); @@ -1477,6 +1502,15 @@ sc->fw_diag_map); if (sc->fw_diag_dmat != NULL) bus_dma_tag_destroy(sc->fw_diag_dmat); + /* + * DF-4013: with the one-trio invariant above this free is + * the right one; NULL the trio so a later read or a second + * unregister cannot touch freed memory (UAF/double-free). + */ + sc->fw_diag_buffer = NULL; + sc->fw_diag_dmat = NULL; + sc->fw_diag_map = NULL; + sc->fw_diag_busaddr = 0; } return (status); @@ -1578,6 +1612,14 @@ pBuffer = &sc->fw_diag_buffer_list[i]; /* + * DF-4013: never copy out through a freed/absent shared trio. + */ + if (sc->fw_diag_buffer == NULL) { + *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; + return (MPS_DIAG_FAILURE); + } + + /* * Make sure requested read is within limits */ if (diag_read_buffer->StartingOffset + diag_read_buffer->BytesToRead > |