fix use after free in flush_range and flush_iovec

Calling blocks_flushed can cause the piece entry to be freed so its
callers need to be aware of that and avoid dereferencing the pointer if
the entry is freed.
This commit is contained in:
Steven Siloti 2018-04-26 10:32:02 -07:00 committed by Arvid Norberg
parent 366b7983d1
commit f5e33932d2
4 changed files with 13 additions and 9 deletions

View File

@ -440,7 +440,8 @@ namespace libtorrent
// used to convert dirty blocks into non-dirty ones
// i.e. from being part of the write cache to being part
// of the read cache. it's used when flushing blocks to disk
void blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed);
// returns true if the piece entry was freed
bool blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed);
// adds a block to the cache, marks it as dirty and
// associates the job with it. When the block is

View File

@ -477,7 +477,8 @@ namespace libtorrent
, file::iovec_t* iov, int* flushing, int block_base_index = 0);
void flush_iovec(cached_piece_entry* pe, file::iovec_t const* iov, int const* flushing
, int num_blocks, storage_error& error);
void iovec_flushed(cached_piece_entry* pe
// returns true if the piece entry was freed
bool iovec_flushed(cached_piece_entry* pe
, int* flushing, int num_blocks, int block_offset
, storage_error const& error
, jobqueue_t& completed_jobs);

View File

@ -793,7 +793,7 @@ cached_piece_entry* block_cache::add_dirty_block(disk_io_job* j)
// (since these blocks now are part of the read cache) the refcounts of the
// blocks are also decremented by this function. They are expected to have been
// incremented by the caller.
void block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed)
bool block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed)
{
TORRENT_PIECE_ASSERT(pe->in_use, pe);
@ -817,7 +817,7 @@ void block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int
pe->num_dirty -= num_flushed;
update_cache_state(pe);
maybe_free_piece(pe);
return maybe_free_piece(pe);
}
std::pair<block_cache::iterator, block_cache::iterator> block_cache::all_pieces() const

View File

@ -676,7 +676,7 @@ namespace libtorrent
// It is necessary to call this function with the blocks produced by
// build_iovec, to reset their state to not being flushed anymore
// the cache needs to be locked when calling this function
void disk_io_thread::iovec_flushed(cached_piece_entry* pe
bool disk_io_thread::iovec_flushed(cached_piece_entry* pe
, int* flushing, int num_blocks, int block_offset
, storage_error const& error
, jobqueue_t& completed_jobs)
@ -691,7 +691,8 @@ namespace libtorrent
DLOG("%d ", flushing[i]);
DLOG("]\n");
#endif
m_disk_cache.blocks_flushed(pe, flushing, num_blocks);
if (m_disk_cache.blocks_flushed(pe, flushing, num_blocks))
return true;
int block_size = m_disk_cache.block_size();
@ -721,6 +722,8 @@ namespace libtorrent
j = next;
}
}
return false;
}
// issues write operations for blocks in the given
@ -756,9 +759,8 @@ namespace libtorrent
TORRENT_PIECE_ASSERT(pe->piece_refcount > 0, pe);
--pe->piece_refcount;
iovec_flushed(pe, flushing, iov_len, 0, error, completed_jobs);
m_disk_cache.maybe_free_piece(pe);
if (!iovec_flushed(pe, flushing, iov_len, 0, error, completed_jobs))
m_disk_cache.maybe_free_piece(pe);
// if the cache is under high pressure, we need to evict
// the blocks we just flushed to make room for more write pieces