mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Fix DR_OVERRIDDEN use-after-free race in dbuf_sync_leaf
In dbuf_sync_leaf, we clone the arc_buf in dr if we share it with db
except for overridden case. However, this exception causes a race where
dbuf_new_size could free the arc_buf after the last dereference of
*datap and causes use-after-free. We fix this by cloning the buf
regardless if it's overridden.
The race:
--
P0                                     P1
                                       dbuf_hold_impl()
                                         // dbuf_hold_copy passed
                                         // because db_data_pending NULL
dbuf_sync_leaf()
  // doesn't clone *datap
  // *datap derefed to db_buf
  dbuf_write(*datap)
                                       dbuf_new_size()
                                         dmu_buf_will_dirty()
                                           dbuf_fix_old_data()
                                             // alloc new buf for P0 dr
                                             // but can't change *datap
                                         arc_alloc_buf()
                                         arc_buf_destroy()
                                           // alloc new buf for db_buf
                                           // and destroy old buf
  dbuf_write() // continue
    abd_get_from_buf(data->b_data,
    arc_buf_size(data))
      // use-after-free
--
Here's an example when it happens:
BUG: kernel NULL pointer dereference, address: 000000000000002e
RIP: 0010:arc_buf_size+0x1c/0x30 [zfs]
Call Trace:
 dbuf_write+0x3ff/0x580 [zfs]
 dbuf_sync_leaf+0x13c/0x530 [zfs]
 dbuf_sync_list+0xbf/0x120 [zfs]
 dnode_sync+0x3ea/0x7a0 [zfs]
 sync_dnodes_task+0x71/0xa0 [zfs]
 taskq_thread+0x2b8/0x4e0 [spl]
 kthread+0x112/0x130
 ret_from_fork+0x1f/0x30
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Chunwei Chen <david.chen@nutanix.com>
Co-authored-by: Chunwei Chen <david.chen@nutanix.com>
Closes #16854
			
			
This commit is contained in:
		
							parent
							
								
									19a04e5ad2
								
							
						
					
					
						commit
						6c9b4f18d3
					
				@ -4779,8 +4779,7 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (*datap != NULL && *datap == db->db_buf &&
 | 
						if (*datap != NULL && *datap == db->db_buf &&
 | 
				
			||||||
	    dn->dn_object != DMU_META_DNODE_OBJECT &&
 | 
						    dn->dn_object != DMU_META_DNODE_OBJECT &&
 | 
				
			||||||
	    zfs_refcount_count(&db->db_holds) > 1 &&
 | 
						    zfs_refcount_count(&db->db_holds) > 1) {
 | 
				
			||||||
	    dr->dt.dl.dr_override_state != DR_OVERRIDDEN) {
 | 
					 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * If this buffer is currently "in use" (i.e., there
 | 
							 * If this buffer is currently "in use" (i.e., there
 | 
				
			||||||
		 * are active holds and db_data still references it),
 | 
							 * are active holds and db_data still references it),
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user