Commit cb65b282 authored by Li Lingfeng's avatar Li Lingfeng Committed by Mike Snitzer
Browse files

dm thin metadata: check fail_io before using data_sm



Must check pmd->fail_io before using pmd->data_sm since
pmd->data_sm may be destroyed by other processes.

       P1(kworker)                             P2(message)
do_worker
 process_prepared
  process_prepared_discard_passdown_pt2
   dm_pool_dec_data_range
                                    pool_message
                                     commit
                                      dm_pool_commit_metadata
                                        ↓
                                       // commit failed
                                      metadata_operation_failed
                                       abort_transaction
                                        dm_pool_abort_metadata
                                         __open_or_format_metadata
                                           ↓
                                          dm_sm_disk_open
                                            ↓
                                           // open failed
                                           // pmd->data_sm is NULL
    dm_sm_dec_blocks
      ↓
     // try to access pmd->data_sm --> UAF

As shown above, if dm_pool_commit_metadata() and
dm_pool_abort_metadata() fail in pool_message process, kworker may
trigger UAF.

Fixes: be500ed7 ("dm space maps: improve performance with inc/dec on ranges of blocks")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarLi Lingfeng <lilingfeng3@huawei.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent 2760904d
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -1756,13 +1756,15 @@ int dm_thin_remove_range(struct dm_thin_device *td,

int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *result)
{
	int r;
	int r = -EINVAL;
	uint32_t ref_count;

	down_read(&pmd->root_lock);
	if (!pmd->fail_io) {
		r = dm_sm_get_count(pmd->data_sm, b, &ref_count);
		if (!r)
			*result = (ref_count > 1);
	}
	up_read(&pmd->root_lock);

	return r;
@@ -1770,9 +1772,10 @@ int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *re

int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
{
	int r = 0;
	int r = -EINVAL;

	pmd_write_lock(pmd);
	if (!pmd->fail_io)
		r = dm_sm_inc_blocks(pmd->data_sm, b, e);
	pmd_write_unlock(pmd);

@@ -1781,9 +1784,10 @@ int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_

int dm_pool_dec_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
{
	int r = 0;
	int r = -EINVAL;

	pmd_write_lock(pmd);
	if (!pmd->fail_io)
		r = dm_sm_dec_blocks(pmd->data_sm, b, e);
	pmd_write_unlock(pmd);