mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 19:04:45 +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;
|
||||
dbuf_dirty_record_t *dr = dsa->dsa_dr;
|
||||
dmu_buf_impl_t *db = dr->dr_dbuf;
|
||||
dmu_tx_t *tx = dsa->dsa_tx;
|
||||
|
||||
abd_free(zio->io_abd);
|
||||
|
||||
@@ -101,6 +102,11 @@ dmu_write_direct_done(zio_t *zio)
|
||||
db->db_state = DB_UNCACHED;
|
||||
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);
|
||||
|
||||
if (zio->io_error != 0) {
|
||||
@@ -120,7 +126,7 @@ dmu_write_direct_done(zio_t *zio)
|
||||
* calling dbuf_undirty().
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user