Loading fs_mgr/liblp/builder.cpp +49 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <string.h> #include <algorithm> #include <limits> #include <android-base/unique_fd.h> Loading Loading @@ -369,7 +370,10 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, } // Align the metadata size up to the nearest sector. metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE); if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) { LERROR << "Max metadata size " << metadata_max_size << " is too large."; return false; } // Validate and build the block device list. uint32_t logical_block_size = 0; Loading Loading @@ -401,10 +405,15 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, // untouched to be compatible code that looks for an MBR. Thus we // start counting free sectors at sector 1, not 0. uint64_t free_area_start = LP_SECTOR_SIZE; bool ok; if (out.alignment) { free_area_start = AlignTo(free_area_start, out.alignment); ok = AlignTo(free_area_start, out.alignment, &free_area_start); } else { free_area_start = AlignTo(free_area_start, logical_block_size); ok = AlignTo(free_area_start, logical_block_size, &free_area_start); } if (!ok) { LERROR << "Integer overflow computing free area start"; return false; } out.first_logical_sector = free_area_start / LP_SECTOR_SIZE; Loading Loading @@ -441,10 +450,15 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, // Compute the first free sector, factoring in alignment. uint64_t free_area_start = total_reserved; bool ok; if (super.alignment || super.alignment_offset) { free_area_start = AlignTo(free_area_start, super.alignment); ok = AlignTo(free_area_start, super.alignment, &free_area_start); } else { free_area_start = AlignTo(free_area_start, logical_block_size); ok = AlignTo(free_area_start, logical_block_size, &free_area_start); } if (!ok) { LERROR << "Integer overflow computing free area start"; return false; } super.first_logical_sector = free_area_start / LP_SECTOR_SIZE; Loading Loading @@ -544,7 +558,11 @@ void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents, const Interval& current = extents[i]; DCHECK(previous.device_index == current.device_index); uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end); uint64_t aligned; if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) { LERROR << "Sector " << previous.end << " caused integer overflow."; continue; } if (aligned >= current.start) { // There is no gap between these two extents, try the next one. // Note that we check with >= instead of >, since alignment may Loading Loading @@ -730,7 +748,10 @@ std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper( // Choose an aligned sector for the midpoint. This could lead to one half // being slightly larger than the other, but this will not restrict the // size of partitions (it might lead to one extra extent if "B" overflows). midpoint = AlignSector(super, midpoint); if (!AlignSector(super, midpoint, &midpoint)) { LERROR << "Unexpected integer overflow aligning midpoint " << midpoint; return free_list; } std::vector<Interval> first_half; std::vector<Interval> second_half; Loading Loading @@ -768,7 +789,11 @@ std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent( // If the sector ends where the next aligned chunk begins, then there's // no missing gap to try and allocate. const auto& block_device = block_devices_[extent->device_index()]; uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector()); uint64_t next_aligned_sector; if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) { LERROR << "Integer overflow aligning sector " << extent->end_sector(); return nullptr; } if (extent->end_sector() == next_aligned_sector) { return nullptr; } Loading Loading @@ -925,13 +950,19 @@ uint64_t MetadataBuilder::UsedSpace() const { return size; } uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector) const { bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector, uint64_t* out) const { // Note: when reading alignment info from the Kernel, we don't assume it // is aligned to the sector size, so we round up to the nearest sector. uint64_t lba = sector * LP_SECTOR_SIZE; uint64_t aligned = AlignTo(lba, block_device.alignment); return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE; if (!AlignTo(lba, block_device.alignment, out)) { return false; } if (!AlignTo(*out, LP_SECTOR_SIZE, out)) { return false; } *out /= LP_SECTOR_SIZE; return true; } bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name, Loading Loading @@ -1005,7 +1036,12 @@ bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size, const std::vector<Interval>& free_region_hint) { // Align the space needed up to the nearest sector. uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size); uint64_t aligned_size; if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) { LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size << " bytes; integer overflow."; return false; } uint64_t old_size = partition->size(); if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) { Loading fs_mgr/liblp/builder_test.cpp +16 −1 Original line number Diff line number Diff line Loading @@ -228,8 +228,9 @@ TEST_F(BuilderTest, InternalPartitionAlignment) { ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR); EXPECT_EQ(extent.num_sectors, 80); uint64_t aligned_lba; uint64_t lba = extent.target_data * LP_SECTOR_SIZE; uint64_t aligned_lba = AlignTo(lba, device_info.alignment); ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba)); EXPECT_EQ(lba, aligned_lba); } Loading Loading @@ -1051,3 +1052,17 @@ TEST_F(BuilderTest, AlignFreeRegion) { EXPECT_EQ(e2->physical_sector(), 3072); EXPECT_EQ(e2->end_sector(), 4197368); } TEST_F(BuilderTest, ResizeOverflow) { BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096); std::vector<BlockDeviceInfo> block_devices = {super}; unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2); ASSERT_NE(builder, nullptr); ASSERT_TRUE(builder->AddGroup("group", 0)); Partition* p = builder->AddPartition("system", "default", 0); ASSERT_NE(p, nullptr); ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL)); } fs_mgr/liblp/include/liblp/builder.h +1 −1 Original line number Diff line number Diff line Loading @@ -359,7 +359,7 @@ class MetadataBuilder { bool GrowPartition(Partition* partition, uint64_t aligned_size, const std::vector<Interval>& free_region_hint); void ShrinkPartition(Partition* partition, uint64_t aligned_size); uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const; bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const; uint64_t TotalSizeOfGroup(PartitionGroup* group) const; bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info); bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const; Loading fs_mgr/liblp/utility.h +17 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <stdint.h> #include <sys/types.h> #include <limits> #include <string> #include <string_view> Loading Loading @@ -66,16 +67,26 @@ int64_t SeekFile64(int fd, int64_t offset, int whence); void SHA256(const void* data, size_t length, uint8_t out[32]); // Align |base| such that it is evenly divisible by |alignment|, which does not // have to be a power of two. constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) { // have to be a power of two. Return false on overflow. template <typename T> bool AlignTo(T base, uint32_t alignment, T* out) { static_assert(std::numeric_limits<T>::is_integer); static_assert(!std::numeric_limits<T>::is_signed); if (!alignment) { return base; *out = base; return true; } uint64_t remainder = base % alignment; T remainder = base % alignment; if (remainder == 0) { return base; *out = base; return true; } return base + (alignment - remainder); T to_add = alignment - remainder; if (to_add > std::numeric_limits<T>::max() - base) { return false; } *out = base + to_add; return true; } // Update names from C++ strings. Loading fs_mgr/liblp/utility_test.cpp +23 −8 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #include <optional> #include <gtest/gtest.h> #include <liblp/builder.h> #include <liblp/liblp.h> Loading Loading @@ -58,15 +60,28 @@ TEST(liblp, GetMetadataOffset) { EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0); } std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) { uint64_t r; if (!AlignTo(base, alignment, &r)) { return {}; } return {r}; } TEST(liblp, AlignTo) { EXPECT_EQ(AlignTo(37, 0), 37); EXPECT_EQ(AlignTo(1024, 1024), 1024); EXPECT_EQ(AlignTo(555, 1024), 1024); EXPECT_EQ(AlignTo(555, 1000), 1000); EXPECT_EQ(AlignTo(0, 1024), 0); EXPECT_EQ(AlignTo(54, 32), 64); EXPECT_EQ(AlignTo(32, 32), 32); EXPECT_EQ(AlignTo(17, 32), 32); EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37)); EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024)); EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024)); EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000)); EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0)); EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64)); EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32)); EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32)); auto u32limit = std::numeric_limits<uint32_t>::max(); auto u64limit = std::numeric_limits<uint64_t>::max(); EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit}); EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{}); } TEST(liblp, GetPartitionSlotSuffix) { Loading Loading
fs_mgr/liblp/builder.cpp +49 −13 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <string.h> #include <algorithm> #include <limits> #include <android-base/unique_fd.h> Loading Loading @@ -369,7 +370,10 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, } // Align the metadata size up to the nearest sector. metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE); if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) { LERROR << "Max metadata size " << metadata_max_size << " is too large."; return false; } // Validate and build the block device list. uint32_t logical_block_size = 0; Loading Loading @@ -401,10 +405,15 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, // untouched to be compatible code that looks for an MBR. Thus we // start counting free sectors at sector 1, not 0. uint64_t free_area_start = LP_SECTOR_SIZE; bool ok; if (out.alignment) { free_area_start = AlignTo(free_area_start, out.alignment); ok = AlignTo(free_area_start, out.alignment, &free_area_start); } else { free_area_start = AlignTo(free_area_start, logical_block_size); ok = AlignTo(free_area_start, logical_block_size, &free_area_start); } if (!ok) { LERROR << "Integer overflow computing free area start"; return false; } out.first_logical_sector = free_area_start / LP_SECTOR_SIZE; Loading Loading @@ -441,10 +450,15 @@ bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices, // Compute the first free sector, factoring in alignment. uint64_t free_area_start = total_reserved; bool ok; if (super.alignment || super.alignment_offset) { free_area_start = AlignTo(free_area_start, super.alignment); ok = AlignTo(free_area_start, super.alignment, &free_area_start); } else { free_area_start = AlignTo(free_area_start, logical_block_size); ok = AlignTo(free_area_start, logical_block_size, &free_area_start); } if (!ok) { LERROR << "Integer overflow computing free area start"; return false; } super.first_logical_sector = free_area_start / LP_SECTOR_SIZE; Loading Loading @@ -544,7 +558,11 @@ void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents, const Interval& current = extents[i]; DCHECK(previous.device_index == current.device_index); uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end); uint64_t aligned; if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) { LERROR << "Sector " << previous.end << " caused integer overflow."; continue; } if (aligned >= current.start) { // There is no gap between these two extents, try the next one. // Note that we check with >= instead of >, since alignment may Loading Loading @@ -730,7 +748,10 @@ std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper( // Choose an aligned sector for the midpoint. This could lead to one half // being slightly larger than the other, but this will not restrict the // size of partitions (it might lead to one extra extent if "B" overflows). midpoint = AlignSector(super, midpoint); if (!AlignSector(super, midpoint, &midpoint)) { LERROR << "Unexpected integer overflow aligning midpoint " << midpoint; return free_list; } std::vector<Interval> first_half; std::vector<Interval> second_half; Loading Loading @@ -768,7 +789,11 @@ std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent( // If the sector ends where the next aligned chunk begins, then there's // no missing gap to try and allocate. const auto& block_device = block_devices_[extent->device_index()]; uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector()); uint64_t next_aligned_sector; if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) { LERROR << "Integer overflow aligning sector " << extent->end_sector(); return nullptr; } if (extent->end_sector() == next_aligned_sector) { return nullptr; } Loading Loading @@ -925,13 +950,19 @@ uint64_t MetadataBuilder::UsedSpace() const { return size; } uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector) const { bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector, uint64_t* out) const { // Note: when reading alignment info from the Kernel, we don't assume it // is aligned to the sector size, so we round up to the nearest sector. uint64_t lba = sector * LP_SECTOR_SIZE; uint64_t aligned = AlignTo(lba, block_device.alignment); return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE; if (!AlignTo(lba, block_device.alignment, out)) { return false; } if (!AlignTo(*out, LP_SECTOR_SIZE, out)) { return false; } *out /= LP_SECTOR_SIZE; return true; } bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name, Loading Loading @@ -1005,7 +1036,12 @@ bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size, const std::vector<Interval>& free_region_hint) { // Align the space needed up to the nearest sector. uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size); uint64_t aligned_size; if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) { LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size << " bytes; integer overflow."; return false; } uint64_t old_size = partition->size(); if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) { Loading
fs_mgr/liblp/builder_test.cpp +16 −1 Original line number Diff line number Diff line Loading @@ -228,8 +228,9 @@ TEST_F(BuilderTest, InternalPartitionAlignment) { ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR); EXPECT_EQ(extent.num_sectors, 80); uint64_t aligned_lba; uint64_t lba = extent.target_data * LP_SECTOR_SIZE; uint64_t aligned_lba = AlignTo(lba, device_info.alignment); ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba)); EXPECT_EQ(lba, aligned_lba); } Loading Loading @@ -1051,3 +1052,17 @@ TEST_F(BuilderTest, AlignFreeRegion) { EXPECT_EQ(e2->physical_sector(), 3072); EXPECT_EQ(e2->end_sector(), 4197368); } TEST_F(BuilderTest, ResizeOverflow) { BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096); std::vector<BlockDeviceInfo> block_devices = {super}; unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2); ASSERT_NE(builder, nullptr); ASSERT_TRUE(builder->AddGroup("group", 0)); Partition* p = builder->AddPartition("system", "default", 0); ASSERT_NE(p, nullptr); ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL)); }
fs_mgr/liblp/include/liblp/builder.h +1 −1 Original line number Diff line number Diff line Loading @@ -359,7 +359,7 @@ class MetadataBuilder { bool GrowPartition(Partition* partition, uint64_t aligned_size, const std::vector<Interval>& free_region_hint); void ShrinkPartition(Partition* partition, uint64_t aligned_size); uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const; bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const; uint64_t TotalSizeOfGroup(PartitionGroup* group) const; bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info); bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const; Loading
fs_mgr/liblp/utility.h +17 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <stdint.h> #include <sys/types.h> #include <limits> #include <string> #include <string_view> Loading Loading @@ -66,16 +67,26 @@ int64_t SeekFile64(int fd, int64_t offset, int whence); void SHA256(const void* data, size_t length, uint8_t out[32]); // Align |base| such that it is evenly divisible by |alignment|, which does not // have to be a power of two. constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) { // have to be a power of two. Return false on overflow. template <typename T> bool AlignTo(T base, uint32_t alignment, T* out) { static_assert(std::numeric_limits<T>::is_integer); static_assert(!std::numeric_limits<T>::is_signed); if (!alignment) { return base; *out = base; return true; } uint64_t remainder = base % alignment; T remainder = base % alignment; if (remainder == 0) { return base; *out = base; return true; } return base + (alignment - remainder); T to_add = alignment - remainder; if (to_add > std::numeric_limits<T>::max() - base) { return false; } *out = base + to_add; return true; } // Update names from C++ strings. Loading
fs_mgr/liblp/utility_test.cpp +23 −8 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #include <optional> #include <gtest/gtest.h> #include <liblp/builder.h> #include <liblp/liblp.h> Loading Loading @@ -58,15 +60,28 @@ TEST(liblp, GetMetadataOffset) { EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0); } std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) { uint64_t r; if (!AlignTo(base, alignment, &r)) { return {}; } return {r}; } TEST(liblp, AlignTo) { EXPECT_EQ(AlignTo(37, 0), 37); EXPECT_EQ(AlignTo(1024, 1024), 1024); EXPECT_EQ(AlignTo(555, 1024), 1024); EXPECT_EQ(AlignTo(555, 1000), 1000); EXPECT_EQ(AlignTo(0, 1024), 0); EXPECT_EQ(AlignTo(54, 32), 64); EXPECT_EQ(AlignTo(32, 32), 32); EXPECT_EQ(AlignTo(17, 32), 32); EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37)); EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024)); EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024)); EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000)); EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0)); EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64)); EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32)); EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32)); auto u32limit = std::numeric_limits<uint32_t>::max(); auto u64limit = std::numeric_limits<uint64_t>::max(); EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit}); EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{}); } TEST(liblp, GetPartitionSlotSuffix) { Loading