Fixed number of blocks calculation Number of blocks calculation was being based on raw number of bytes being read, and _not_ the block-aligned start/end offsets (which capture which blocks, and therefore how many blocks, are read) Change-Id: Ib62a0e84b39f1e94a8b53eaf393b07023156a735
diff --git a/zircon/system/ulib/blobfs/compression/zstd-seekable-blob.cc b/zircon/system/ulib/blobfs/compression/zstd-seekable-blob.cc index bc11023..af2818fc 100644 --- a/zircon/system/ulib/blobfs/compression/zstd-seekable-blob.cc +++ b/zircon/system/ulib/blobfs/compression/zstd-seekable-blob.cc
@@ -45,6 +45,12 @@ TRACE_DURATION("blobfs", "ZSTDRead", "byte_offset", file->byte_offset, "bytes", num_bytes); + if (num_bytes == 0) { + FS_TRACE_ERROR("[blobfs][zstd-seekable] Attempt to read 0 bytes\n"); + file->status = ZX_ERR_OUT_OF_RANGE; + return -1; + } + // |file->byte_offset| does not account for ZSTD seekable header. uint64_t data_byte_offset; if (add_overflow(kZSTDSeekableHeaderSize, file->byte_offset, &data_byte_offset)) { @@ -55,28 +61,40 @@ } // Safely convert units: Bytes to blocks. - uint64_t data_block_offset64 = data_byte_offset / kBlobfsBlockSize; - if (data_block_offset64 > std::numeric_limits<uint32_t>::max()) { - FS_TRACE_ERROR("[blobfs][zstd-seekable] Oversized data block offset: %lu / %u = %lu > %u\n", - data_byte_offset, kBlobfsBlockSize, data_block_offset64, + uint64_t data_block_start64 = data_byte_offset / kBlobfsBlockSize; + if (data_block_start64 > std::numeric_limits<uint32_t>::max()) { + FS_TRACE_ERROR("[blobfs][zstd-seekable] Oversized data block start: %lu / %u = %lu > %u\n", + data_byte_offset, kBlobfsBlockSize, data_block_start64, std::numeric_limits<uint32_t>::max()); file->status = ZX_ERR_OUT_OF_RANGE; return -1; } - uint32_t data_block_offset = static_cast<uint32_t>(data_block_offset64); - uint64_t num_blocks64 = fbl::round_up(num_bytes, kBlobfsBlockSize) / kBlobfsBlockSize; + uint32_t data_block_start = static_cast<uint32_t>(data_block_start64); + uint64_t data_byte_end; + if (add_overflow(data_byte_offset, num_bytes, &data_byte_end)) { + FS_TRACE_ERROR("[blobfs][zstd-seekable] Oversized data block end: data_byte_offset=%lu, num_bytes=%lu\n", + data_byte_offset, num_bytes); + file->status = ZX_ERR_OUT_OF_RANGE; + return -1; + } + uint64_t data_block_end64 = fbl::round_up(data_byte_end, kBlobfsBlockSize) / kBlobfsBlockSize; + uint64_t num_blocks64; + if (sub_overflow(data_block_end64, data_block_start64, &num_blocks64)) { + FS_TRACE_ERROR("[blobfs][zstd-seekable] Block calculation error: (data_block_end=%lu - data_block_start=%lu) should be non-negative\n", + data_block_end64, data_block_start64); + file->status = ZX_ERR_INTERNAL; + return -1; + } if (num_blocks64 > std::numeric_limits<uint32_t>::max()) { - FS_TRACE_ERROR( - "[blobfs][zstd-seekable] Oversized number of blocks: round_up(%lu, %u) / %u = %lu > %u\n", - num_bytes, kBlobfsBlockSize, kBlobfsBlockSize, num_blocks64, - std::numeric_limits<uint32_t>::max()); + FS_TRACE_ERROR("[blobfs][zstd-seekable] Oversized number of blocks: %lu > %u\n", + num_blocks64, std::numeric_limits<uint32_t>::max()); file->status = ZX_ERR_OUT_OF_RANGE; return -1; } uint32_t num_blocks = static_cast<uint32_t>(num_blocks64); // Delegate block-level read to compressed block collection. - zx_status_t status = file->blocks->Read(data_block_offset, num_blocks); + zx_status_t status = file->blocks->Read(data_block_start, num_blocks); if (status != ZX_OK) { FS_TRACE_ERROR("[blobfs][zstd-seekable] Failed to read blocks: %s\n", zx_status_get_string(status));
diff --git a/zircon/system/ulib/blobfs/test/unit/zstd-seekable-blob-test.cc b/zircon/system/ulib/blobfs/test/unit/zstd-seekable-blob-test.cc index 5423320..f7d9c43 100644 --- a/zircon/system/ulib/blobfs/test/unit/zstd-seekable-blob-test.cc +++ b/zircon/system/ulib/blobfs/test/unit/zstd-seekable-blob-test.cc
@@ -49,7 +49,10 @@ const uint32_t kNumFilesystemBlocks = 4000; +constexpr unsigned kZeroToThirtyTwoAndRandomBlobSrcFunctionRandomSeed = 9572331; + void ZeroToThirtyTwoAndRandomBlobSrcFunction(char* data, size_t length) { + srand(kZeroToThirtyTwoAndRandomBlobSrcFunctionRandomSeed); for (size_t i = 0; i < length; i++) { if ((i / 32) % 2 == 0) { uint8_t value = static_cast<uint8_t>(i % 32); @@ -74,6 +77,24 @@ void SetDeviceOwner(std::string name) { gDeviceOwner = name; } + +constexpr size_t gLoggingBytesPerLine = 64; + +void LogBuf(std::string name, const std::vector<uint8_t>& buf) { + cerr << "BUF(" << name << ") :: " << buf.size(); + + + for (size_t i = 0; i < buf.size(); i++) { + if ((i % gLoggingBytesPerLine) == 0) { + cerr << endl << "BUF(" << name << ") "; + fprintf(stderr, "%10lu", i); + cerr << " >> "; + } + fprintf(stderr, "%02X", buf[i]); + } + cerr << endl; +} + class LoggingBlockDevice : public block_client::BlockDevice { public: LoggingBlockDevice() = delete; @@ -285,7 +306,17 @@ std::vector<uint8_t> expected(blob_info->size_data); ZeroToThirtyTwoAndRandomBlobSrcFunction(reinterpret_cast<char*>(expected.data()), blob_info->size_data); ASSERT_OK(compressed_blob_collection()->Read(node_index, buf.data(), 0, blob_info->size_data)); - ASSERT_BYTES_EQ(expected.data(), buf.data(), blob_info->size_data); + + LogBuf("EXPECTED", expected); + LogBuf("WAS_READ", buf); + + const size_t incrementSize = kBlobfsBlockSize / 4; + for (size_t i = 0; i < blob_info->size_data; i += incrementSize) { + EXPECT_BYTES_EQ(&(expected.data()[i]), &(buf.data()[i]), std::min(i + incrementSize, blob_info->size_data) - i); + } + // Note: Data size is too large for stack allocation in |ASSERT_BYTES_EQ()|. That's why + // |memcmp()| is used instead. + // ASSERT_EQ(0, memcmp(expected.data(), buf.data(), blob_info->size_data)); } // DisableZSTDReadLogging();