make disk_buffer_holder know the size of the bufer it holds, to fix buffer overrun in chained_buffer

fix
This commit is contained in:
arvidn 2017-10-13 02:34:24 +03:00 committed by Arvid Norberg
parent 58a76e26a8
commit 8adcfdbf41
6 changed files with 36 additions and 26 deletions

View File

@ -112,10 +112,10 @@ namespace libtorrent {
#if TORRENT_CPP98_DEQUE #if TORRENT_CPP98_DEQUE
move_construct_holder_fun move_holder; move_construct_holder_fun move_holder;
#endif #endif
aux::aligned_storage<24>::type holder; aux::aligned_storage<32>::type holder;
char* buf; // the first byte of the buffer char* buf = nullptr; // the first byte of the buffer
int size; // the total size of the buffer int size = 0; // the total size of the buffer
int used_size; // this is the number of bytes to send/receive int used_size = 0; // this is the number of bytes to send/receive
}; };
public: public:

View File

@ -63,7 +63,8 @@ namespace libtorrent {
struct TORRENT_EXTRA_EXPORT disk_buffer_holder struct TORRENT_EXTRA_EXPORT disk_buffer_holder
{ {
// internal // internal
disk_buffer_holder(buffer_allocator_interface& alloc, char* buf) noexcept; disk_buffer_holder(buffer_allocator_interface& alloc
, char* buf, std::size_t sz) noexcept;
disk_buffer_holder& operator=(disk_buffer_holder&&) noexcept; disk_buffer_holder& operator=(disk_buffer_holder&&) noexcept;
disk_buffer_holder(disk_buffer_holder&&) noexcept; disk_buffer_holder(disk_buffer_holder&&) noexcept;
@ -75,7 +76,9 @@ namespace libtorrent {
// using a disk buffer pool directly (there's only one // using a disk buffer pool directly (there's only one
// disk_buffer_pool per session) // disk_buffer_pool per session)
disk_buffer_holder(buffer_allocator_interface& alloc disk_buffer_holder(buffer_allocator_interface& alloc
, aux::block_cache_reference const& ref, char* buf) noexcept; , aux::block_cache_reference const& ref
, char* buf
, std::size_t sz) noexcept;
// frees any unreleased disk buffer held by this object // frees any unreleased disk buffer held by this object
~disk_buffer_holder(); ~disk_buffer_holder();
@ -92,14 +95,15 @@ namespace libtorrent {
// set the holder object to hold the specified buffer // set the holder object to hold the specified buffer
// (or nullptr by default). If it's already holding a // (or nullptr by default). If it's already holding a
// disk buffer, it will first be freed. // disk buffer, it will first be freed.
void reset(char* buf = nullptr); void reset(char* buf = nullptr, std::size_t sz = 0);
void reset(aux::block_cache_reference const& ref, char* buf); void reset(aux::block_cache_reference const& ref, char* buf, std::size_t sz);
// swap pointers of two disk buffer holders. // swap pointers of two disk buffer holders.
void swap(disk_buffer_holder& h) noexcept void swap(disk_buffer_holder& h) noexcept
{ {
TORRENT_ASSERT(h.m_allocator == m_allocator); TORRENT_ASSERT(h.m_allocator == m_allocator);
std::swap(h.m_buf, m_buf); std::swap(h.m_buf, m_buf);
std::swap(h.m_size, m_size);
std::swap(h.m_ref, m_ref); std::swap(h.m_ref, m_ref);
} }
@ -110,12 +114,13 @@ namespace libtorrent {
// buffer // buffer
explicit operator bool() const noexcept { return m_buf != nullptr; } explicit operator bool() const noexcept { return m_buf != nullptr; }
std::size_t size() const { return 0x4000; } std::size_t size() const { return m_size; }
private: private:
buffer_allocator_interface* m_allocator; buffer_allocator_interface* m_allocator;
char* m_buf; char* m_buf;
std::size_t m_size;
aux::block_cache_reference m_ref; aux::block_cache_reference m_ref;
}; };

View File

@ -1670,10 +1670,11 @@ int block_cache::copy_from_piece(cached_piece_entry* const pe
// make sure it didn't wrap // make sure it didn't wrap
TORRENT_PIECE_ASSERT(pe->refcount > 0, pe); TORRENT_PIECE_ASSERT(pe->refcount > 0, pe);
int const blocks_per_piece = (j->storage->files().piece_length() + block_size() - 1) / block_size(); int const blocks_per_piece = (j->storage->files().piece_length() + block_size() - 1) / block_size();
TORRENT_ASSERT(block_offset < 0x4000);
j->argument = disk_buffer_holder(allocator j->argument = disk_buffer_holder(allocator
, aux::block_cache_reference{ j->storage->storage_index() , aux::block_cache_reference{ j->storage->storage_index()
, static_cast<int>(pe->piece) * blocks_per_piece + start_block} , static_cast<int>(pe->piece) * blocks_per_piece + start_block}
, bl.buf + (j->d.io.offset & (block_size() - 1))); , bl.buf + block_offset, static_cast<std::size_t>(0x4000 - block_offset));
j->storage->inc_refcount(); j->storage->inc_refcount();
++m_send_buffer_blocks; ++m_send_buffer_blocks;
@ -1690,14 +1691,13 @@ int block_cache::copy_from_piece(cached_piece_entry* const pe
} }
j->argument = disk_buffer_holder(allocator j->argument = disk_buffer_holder(allocator
, allocate_buffer("send buffer")); , allocate_buffer("send buffer"), 0x4000);
if (!boost::get<disk_buffer_holder>(j->argument)) return -2; if (!boost::get<disk_buffer_holder>(j->argument)) return -2;
while (size > 0) while (size > 0)
{ {
TORRENT_PIECE_ASSERT(pe->blocks[block].buf, pe); TORRENT_PIECE_ASSERT(pe->blocks[block].buf, pe);
int to_copy = (std::min)(block_size() int to_copy = std::min(block_size() - block_offset, size);
- block_offset, size);
std::memcpy(boost::get<disk_buffer_holder>(j->argument).get() std::memcpy(boost::get<disk_buffer_holder>(j->argument).get()
+ buffer_offset + buffer_offset
, pe->blocks[block].buf + block_offset , pe->blocks[block].buf + block_offset

View File

@ -36,8 +36,9 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent { namespace libtorrent {
disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc, char* buf) noexcept disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc
: m_allocator(&alloc), m_buf(buf), m_ref() , char* buf, std::size_t sz) noexcept
: m_allocator(&alloc), m_buf(buf), m_size(sz), m_ref()
{} {}
disk_buffer_holder& disk_buffer_holder::operator=(disk_buffer_holder&& h) noexcept disk_buffer_holder& disk_buffer_holder::operator=(disk_buffer_holder&& h) noexcept
@ -48,7 +49,7 @@ namespace libtorrent {
} }
disk_buffer_holder::disk_buffer_holder(disk_buffer_holder&& h) noexcept disk_buffer_holder::disk_buffer_holder(disk_buffer_holder&& h) noexcept
: m_allocator(h.m_allocator), m_buf(h.m_buf), m_ref(h.m_ref) : m_allocator(h.m_allocator), m_buf(h.m_buf), m_size(h.m_size), m_ref(h.m_ref)
{ {
// we own this buffer now // we own this buffer now
h.m_buf = nullptr; h.m_buf = nullptr;
@ -56,23 +57,26 @@ namespace libtorrent {
} }
disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc disk_buffer_holder::disk_buffer_holder(buffer_allocator_interface& alloc
, aux::block_cache_reference const& ref, char* buf) noexcept , aux::block_cache_reference const& ref, char* buf
: m_allocator(&alloc), m_buf(buf), m_ref(ref) , std::size_t sz) noexcept
: m_allocator(&alloc), m_buf(buf), m_size(sz), m_ref(ref)
{} {}
void disk_buffer_holder::reset(aux::block_cache_reference const& ref, char* buf) void disk_buffer_holder::reset(aux::block_cache_reference const& ref, char* buf, std::size_t const sz)
{ {
if (m_ref.cookie != aux::block_cache_reference::none) m_allocator->reclaim_blocks(m_ref); if (m_ref.cookie != aux::block_cache_reference::none) m_allocator->reclaim_blocks(m_ref);
else if (m_buf) m_allocator->free_disk_buffer(m_buf); else if (m_buf) m_allocator->free_disk_buffer(m_buf);
m_buf = buf; m_buf = buf;
m_size = sz;
m_ref = ref; m_ref = ref;
} }
void disk_buffer_holder::reset(char* const buf) void disk_buffer_holder::reset(char* const buf, std::size_t const sz)
{ {
if (m_ref.cookie != aux::block_cache_reference::none) m_allocator->reclaim_blocks(m_ref); if (m_ref.cookie != aux::block_cache_reference::none) m_allocator->reclaim_blocks(m_ref);
else if (m_buf) m_allocator->free_disk_buffer(m_buf); else if (m_buf) m_allocator->free_disk_buffer(m_buf);
m_buf = buf; m_buf = buf;
m_size = sz;
m_ref = aux::block_cache_reference(); m_ref = aux::block_cache_reference();
} }
@ -81,6 +85,7 @@ namespace libtorrent {
TORRENT_ASSERT(m_ref.cookie == aux::block_cache_reference::none); TORRENT_ASSERT(m_ref.cookie == aux::block_cache_reference::none);
char* ret = m_buf; char* ret = m_buf;
m_buf = nullptr; m_buf = nullptr;
m_size = 0;
m_ref = aux::block_cache_reference(); m_ref = aux::block_cache_reference();
return ret; return ret;
} }

View File

@ -1231,7 +1231,7 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
status_t disk_io_thread::do_uncached_read(disk_io_job* j) status_t disk_io_thread::do_uncached_read(disk_io_job* j)
{ {
j->argument = disk_buffer_holder(*this, m_disk_cache.allocate_buffer("send buffer")); j->argument = disk_buffer_holder(*this, m_disk_cache.allocate_buffer("send buffer"), 0x4000);
auto& buffer = boost::get<disk_buffer_holder>(j->argument); auto& buffer = boost::get<disk_buffer_holder>(j->argument);
if (buffer.get() == nullptr) if (buffer.get() == nullptr)
{ {
@ -1583,7 +1583,7 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
j->piece = r.piece; j->piece = r.piece;
j->d.io.offset = r.start; j->d.io.offset = r.start;
j->d.io.buffer_size = std::uint16_t(r.length); j->d.io.buffer_size = std::uint16_t(r.length);
j->argument = disk_buffer_holder(*this, nullptr); j->argument = disk_buffer_holder(*this, nullptr, 0);
j->flags = flags; j->flags = flags;
j->callback = std::move(handler); j->callback = std::move(handler);
@ -1688,7 +1688,7 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
TORRENT_ASSERT(r.length <= 16 * 1024); TORRENT_ASSERT(r.length <= 16 * 1024);
bool exceeded = false; bool exceeded = false;
disk_buffer_holder buffer(*this, m_disk_cache.allocate_buffer(exceeded, o, "receive buffer")); disk_buffer_holder buffer(*this, m_disk_cache.allocate_buffer(exceeded, o, "receive buffer"), 0x4000);
if (!buffer) aux::throw_ex<std::bad_alloc>(); if (!buffer) aux::throw_ex<std::bad_alloc>();
std::memcpy(buffer.get(), buf, aux::numeric_cast<std::size_t>(r.length)); std::memcpy(buffer.get(), buf, aux::numeric_cast<std::size_t>(r.length));

View File

@ -140,7 +140,7 @@ static void nop() {}
wj.d.io.offset = (b) * 0x4000; \ wj.d.io.offset = (b) * 0x4000; \
wj.d.io.buffer_size = 0x4000; \ wj.d.io.buffer_size = 0x4000; \
wj.piece = piece_index_t(p); \ wj.piece = piece_index_t(p); \
wj.argument = disk_buffer_holder(alloc, bc.allocate_buffer("write-test")); \ wj.argument = disk_buffer_holder(alloc, bc.allocate_buffer("write-test"), 0x4000); \
pe = bc.add_dirty_block(&wj) pe = bc.add_dirty_block(&wj)
#define READ_BLOCK(p, b, r) \ #define READ_BLOCK(p, b, r) \
@ -149,7 +149,7 @@ static void nop() {}
rj.d.io.buffer_size = 0x4000; \ rj.d.io.buffer_size = 0x4000; \
rj.piece = piece_index_t(p); \ rj.piece = piece_index_t(p); \
rj.storage = pm; \ rj.storage = pm; \
rj.argument = disk_buffer_holder(alloc, nullptr); \ rj.argument = disk_buffer_holder(alloc, nullptr, 0); \
ret = bc.try_read(&rj, alloc) ret = bc.try_read(&rj, alloc)
#define FLUSH(flushing) \ #define FLUSH(flushing) \
@ -444,7 +444,7 @@ void test_unaligned_read()
rj.d.io.buffer_size = 0x4000; rj.d.io.buffer_size = 0x4000;
rj.piece = piece_index_t(0); rj.piece = piece_index_t(0);
rj.storage = pm; rj.storage = pm;
rj.argument = disk_buffer_holder(alloc, nullptr); rj.argument = disk_buffer_holder(alloc, nullptr, 0);
ret = bc.try_read(&rj, alloc); ret = bc.try_read(&rj, alloc);
// unaligned reads copies the data into a new buffer // unaligned reads copies the data into a new buffer