diff --git a/src/dwarfs/block_cache.cpp b/src/dwarfs/block_cache.cpp index 8084fd00..b1c84091 100644 --- a/src/dwarfs/block_cache.cpp +++ b/src/dwarfs/block_cache.cpp @@ -81,9 +81,13 @@ class cached_block { const uint8_t* data() const { return data_.data(); } + size_t size() const { return data_.size(); } + void decompress_until(size_t end) { while (data_.size() < end) { - assert(decompressor_); + if (!decompressor_) { + DWARFS_THROW(runtime_error, "no decompressor for block"); + } if (decompressor_->decompress_frame()) { // We're done, free the memory @@ -128,7 +132,7 @@ class block_request { : begin_(begin) , end_(end) , promise_(std::move(promise)) { - assert(begin_ < end_); + DWARFS_ASSERT(begin_ < end_, "invalid block_request"); } block_request(block_request&&) = default; @@ -401,22 +405,31 @@ class block_cache_ : public block_cache::impl { // Bummer. We don't know anything about the block. - LOG_DEBUG << "block " << block_no << " not found"; + try { + if (block_no >= block_.size()) { + DWARFS_THROW(runtime_error, + fmt::format("block number out of range {0} >= {1}", + block_no, block_.size())); + } - assert(block_no < block_.size()); + LOG_DEBUG << "block " << block_no << " not found"; - auto block = std::make_shared( - LOG_GET_LOGGER, block_[block_no], mm_, options_.mm_release); - ++blocks_created_; + auto block = std::make_shared( + LOG_GET_LOGGER, DWARFS_NOTHROW(block_.at(block_no)), mm_, + options_.mm_release); + ++blocks_created_; - // Make a new set for the block - brs = std::make_shared(std::move(block), block_no); + // Make a new set for the block + brs = std::make_shared(std::move(block), block_no); - // Promise will be fulfilled asynchronously - brs->add(offset, range_end, std::move(promise)); + // Promise will be fulfilled asynchronously + brs->add(offset, range_end, std::move(promise)); - active_[block_no].emplace_back(brs); - enqueue_job(std::move(brs)); + active_[block_no].emplace_back(brs); + enqueue_job(std::move(brs)); + } catch (...) { + promise.set_exception(std::current_exception()); + } return future; } @@ -561,6 +574,15 @@ block_range::block_range(std::shared_ptr block, size_t offset, size_t size) : begin_(block->data() + offset) , end_(begin_ + size) - , block_(std::move(block)) {} + , block_(std::move(block)) { + if (!block_->data()) { + DWARFS_THROW(runtime_error, "block_range: block data is null"); + } + if (size > block_->size()) { + DWARFS_THROW(runtime_error, + fmt::format("block_range: size out of range ({0} > {1})", size, + block_->size())); + } +} } // namespace dwarfs diff --git a/src/dwarfs/block_compressor.cpp b/src/dwarfs/block_compressor.cpp index 747d34e0..604cf985 100644 --- a/src/dwarfs/block_compressor.cpp +++ b/src/dwarfs/block_compressor.cpp @@ -493,7 +493,15 @@ class null_block_decompressor : public block_decompressor::impl { : decompressed_(target) , data_(data) , uncompressed_size_(size) { - decompressed_.reserve(uncompressed_size_); + // TODO: we shouldn't have to copy this to memory at all... + try { + decompressed_.reserve(uncompressed_size_); + } catch (std::bad_alloc const&) { + DWARFS_THROW( + runtime_error, + fmt::format("could not reserve {} bytes for decompressed block", + uncompressed_size_)); + } } compression_type type() const override { return compression_type::NONE; } @@ -536,7 +544,14 @@ class lzma_block_decompressor : public block_decompressor::impl { LZMA_OK) { DWARFS_THROW(runtime_error, "lzma_stream_decoder"); } - decompressed_.reserve(uncompressed_size_); + try { + decompressed_.reserve(uncompressed_size_); + } catch (std::bad_alloc const&) { + DWARFS_THROW( + runtime_error, + fmt::format("could not reserve {} bytes for decompressed block", + uncompressed_size_)); + } } ~lzma_block_decompressor() { lzma_end(&stream_); } @@ -605,7 +620,14 @@ class lz4_block_decompressor : public block_decompressor::impl { , data_(data + sizeof(uint32_t)) , input_size_(size - sizeof(uint32_t)) , uncompressed_size_(get_uncompressed_size(data)) { - decompressed_.reserve(uncompressed_size_); + try { + decompressed_.reserve(uncompressed_size_); + } catch (std::bad_alloc const&) { + DWARFS_THROW( + runtime_error, + fmt::format("could not reserve {} bytes for decompressed block", + uncompressed_size_)); + } } compression_type type() const override { return compression_type::LZ4; } @@ -656,7 +678,14 @@ class zstd_block_decompressor : public block_decompressor::impl { , data_(data) , size_(size) , uncompressed_size_(ZSTD_getDecompressedSize(data, size)) { - decompressed_.reserve(uncompressed_size_); + try { + decompressed_.reserve(uncompressed_size_); + } catch (std::bad_alloc const&) { + DWARFS_THROW( + runtime_error, + fmt::format("could not reserve {} bytes for decompressed block", + uncompressed_size_)); + } } compression_type type() const override { return compression_type::ZSTD; } diff --git a/src/dwarfs/inode_reader_v2.cpp b/src/dwarfs/inode_reader_v2.cpp index 456d665d..2bdbbcdd 100644 --- a/src/dwarfs/inode_reader_v2.cpp +++ b/src/dwarfs/inode_reader_v2.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -127,6 +128,11 @@ inode_reader_::read(size_t size, off_t offset, chunk_range chunks, size_t chunksize = it->size() - offset; size_t chunkoff = it->offset() + offset; + if (chunksize == 0) { + LOG_ERROR << "invalid zero-sized chunk"; + return -EIO; + } + if (num_read + chunksize > size) { chunksize = size - num_read; } @@ -137,15 +143,22 @@ inode_reader_::read(size_t size, off_t offset, chunk_range chunks, offset = 0; } - // now fill the buffer - size_t num_read = 0; - for (auto& r : ranges) { - auto br = r.get(); - store(num_read, br); - num_read += br.size(); + try { + // now fill the buffer + size_t num_read = 0; + for (auto& r : ranges) { + auto br = r.get(); + store(num_read, br); + num_read += br.size(); + } + return num_read; + } catch (runtime_error const& e) { + LOG_ERROR << e.what(); + } catch (...) { + LOG_ERROR << folly::exceptionStr(std::current_exception()); } - return num_read; + return -EIO; } template diff --git a/src/dwarfs/metadata_v2.cpp b/src/dwarfs/metadata_v2.cpp index 657b54b9..93fef80a 100644 --- a/src/dwarfs/metadata_v2.cpp +++ b/src/dwarfs/metadata_v2.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -304,6 +305,15 @@ class metadata_ : public metadata_v2::impl { auto inode = entry.inode() - chunk_index_offset_; uint32_t cur = meta_.chunk_index()[inode]; uint32_t end = meta_.chunk_index()[inode + 1]; + if (cur > end) { + DWARFS_THROW(runtime_error, + fmt::format("invalid chunk range: [{0}..{1}]", cur, end)); + } + if (end > meta_.chunks().size()) { + DWARFS_THROW(runtime_error, + fmt::format("chunk index out of range: {0} > {1}", end, + meta_.chunks().size())); + } size_t size = 0; while (cur < end) { size += meta_.chunks()[cur++].size(); @@ -321,8 +331,8 @@ class metadata_ : public metadata_v2::impl { } } - void - walk(entry_view entry, std::function const& func) const; + void walk(entry_view entry, std::unordered_set& seen, + std::function const& func) const; std::optional get_entry(int inode) const { inode -= inode_offset_; @@ -563,20 +573,27 @@ std::string metadata_::modestring(uint16_t mode) const { template void metadata_::walk( - entry_view entry, std::function const& func) const { + entry_view entry, std::unordered_set& seen, + std::function const& func) const { func(entry); if (S_ISDIR(entry.mode())) { + auto inode = entry.inode(); + if (!seen.emplace(inode).second) { + DWARFS_THROW(runtime_error, "cycle detected during directory walk"); + } auto dir = make_directory_view(entry); for (auto cur : dir.entry_range()) { - walk(make_entry_view(cur), func); + walk(make_entry_view(cur), seen, func); } + seen.erase(inode); } } template void metadata_::walk( std::function const& func) const { - walk(root_, func); + std::unordered_set seen; + walk(root_, seen, func); } template diff --git a/src/dwarfsbench.cpp b/src/dwarfsbench.cpp index a60f19a2..76c31215 100644 --- a/src/dwarfsbench.cpp +++ b/src/dwarfsbench.cpp @@ -19,6 +19,8 @@ * along with dwarfs. If not, see . */ +#include + #include #include @@ -74,8 +76,13 @@ int dwarfsbench(int argc, char** argv) { po::variables_map vm; - po::store(po::parse_command_line(argc, argv, opts), vm); - po::notify(vm); + try { + po::store(po::parse_command_line(argc, argv, opts), vm); + po::notify(vm); + } catch (po::error const& e) { + std::cerr << "error: " << e.what() << std::endl; + return 1; + } if (vm.count("help") or !vm.count("filesystem")) { std::cout << "dwarfsbench (" << DWARFS_VERSION << ")\n\n" @@ -83,43 +90,54 @@ int dwarfsbench(int argc, char** argv) { return 0; } - stream_logger lgr(std::cerr, logger::parse_level(log_level)); - filesystem_options fsopts; + try { + stream_logger lgr(std::cerr, logger::parse_level(log_level)); + filesystem_options fsopts; - fsopts.lock_mode = parse_mlock_mode(lock_mode_str); - fsopts.block_cache.max_bytes = parse_size_with_unit(cache_size_str); - fsopts.block_cache.num_workers = num_workers; - fsopts.block_cache.decompress_ratio = folly::to(decompress_ratio_str); + fsopts.lock_mode = parse_mlock_mode(lock_mode_str); + fsopts.block_cache.max_bytes = parse_size_with_unit(cache_size_str); + fsopts.block_cache.num_workers = num_workers; + fsopts.block_cache.decompress_ratio = + folly::to(decompress_ratio_str); - dwarfs::filesystem_v2 fs(lgr, std::make_shared(filesystem), - fsopts); + dwarfs::filesystem_v2 fs(lgr, std::make_shared(filesystem), + fsopts); - worker_group wg("reader", num_readers); + worker_group wg("reader", num_readers); - fs.walk([&](auto entry) { - if (S_ISREG(entry.mode())) { - wg.add_job([&fs, entry] { - struct ::stat stbuf; - if (fs.getattr(entry, &stbuf) == 0) { - std::vector buf(stbuf.st_size); - int fh = fs.open(entry); - fs.read(fh, buf.data(), buf.size()); - } - }); - } - }); + fs.walk([&](auto entry) { + if (S_ISREG(entry.mode())) { + wg.add_job([&fs, entry] { + try { + struct ::stat stbuf; + if (fs.getattr(entry, &stbuf) == 0) { + std::vector buf(stbuf.st_size); + int fh = fs.open(entry); + fs.read(fh, buf.data(), buf.size()); + } + } catch (runtime_error const& e) { + std::cerr << "error: " << e.what() << std::endl; + } catch (...) { + std::cerr << "error: " + << folly::exceptionStr(std::current_exception()) + << std::endl; + dump_exceptions(); + } + }); + } + }); - wg.wait(); + wg.wait(); + } catch (runtime_error const& e) { + std::cerr << "error: " << e.what() << std::endl; + return 1; + } return 0; } + } // namespace int main(int argc, char** argv) { - try { - return dwarfsbench(argc, argv); - } catch (std::exception const& e) { - std::cerr << "ERROR: " << folly::exceptionStr(e) << std::endl; - return 1; - } + return dwarfs::safe_main([&] { return dwarfsbench(argc, argv); }); }