mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-26 12:12:13 +03:00
dmu_direct: avoid UAF in dmu_write_direct_done()
dmu_write_direct_done() passes dmu_sync_arg_t to dmu_sync_done(), which updates the override state and frees the completion context. The Direct I/O error path then still dereferences dsa->dsa_tx while rolling the dirty record back with dbuf_undirty(), resulting in a use-after-free. Save dsa->dsa_tx in a local variable before calling dmu_sync_done() and use that saved tx for the error rollback. This preserves the existing ownership model for dsa and does not change the Direct I/O write semantics. Reviewed-by: Brian Atkinson <batkinson@lanl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Co-authored-by: gality369 <gality369@example.com> Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Closes #18440
This commit is contained in:
@@ -91,6 +91,7 @@ dmu_write_direct_done(zio_t *zio)
|
|||||||
dmu_sync_arg_t *dsa = zio->io_private;
|
dmu_sync_arg_t *dsa = zio->io_private;
|
||||||
dbuf_dirty_record_t *dr = dsa->dsa_dr;
|
dbuf_dirty_record_t *dr = dsa->dsa_dr;
|
||||||
dmu_buf_impl_t *db = dr->dr_dbuf;
|
dmu_buf_impl_t *db = dr->dr_dbuf;
|
||||||
|
dmu_tx_t *tx = dsa->dsa_tx;
|
||||||
|
|
||||||
abd_free(zio->io_abd);
|
abd_free(zio->io_abd);
|
||||||
|
|
||||||
@@ -101,6 +102,11 @@ dmu_write_direct_done(zio_t *zio)
|
|||||||
db->db_state = DB_UNCACHED;
|
db->db_state = DB_UNCACHED;
|
||||||
mutex_exit(&db->db_mtx);
|
mutex_exit(&db->db_mtx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dmu_sync_done() owns dsa and frees it after publishing the final
|
||||||
|
* override state. The direct-I/O error path still needs the original
|
||||||
|
* open-context tx to roll the dirty record back with dbuf_undirty().
|
||||||
|
*/
|
||||||
dmu_sync_done(zio, NULL, zio->io_private);
|
dmu_sync_done(zio, NULL, zio->io_private);
|
||||||
|
|
||||||
if (zio->io_error != 0) {
|
if (zio->io_error != 0) {
|
||||||
@@ -120,7 +126,7 @@ dmu_write_direct_done(zio_t *zio)
|
|||||||
* calling dbuf_undirty().
|
* calling dbuf_undirty().
|
||||||
*/
|
*/
|
||||||
mutex_enter(&db->db_mtx);
|
mutex_enter(&db->db_mtx);
|
||||||
VERIFY3B(dbuf_undirty(db, dsa->dsa_tx), ==, B_FALSE);
|
VERIFY3B(dbuf_undirty(db, tx), ==, B_FALSE);
|
||||||
mutex_exit(&db->db_mtx);
|
mutex_exit(&db->db_mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user