From 7ebd92ebb3a89fc8421112515851ac5f90cadbc9 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Mon, 10 Aug 2026 14:59:56 -0700 Subject: [PATCH 1/2] Harden payload buffer bounds handling Validate forged offsets and preserve buffer state on allocation failures so malformed payload data cannot trigger out-of-bounds accesses or stranded allocations. --- toolbelt/payload_buffer.cc | 124 +++++-- toolbelt/payload_buffer.h | 147 ++++++-- toolbelt/payload_buffer_test.cc | 616 ++++++++++++++++++++++++++++++++ 3 files changed, 828 insertions(+), 59 deletions(-) diff --git a/toolbelt/payload_buffer.cc b/toolbelt/payload_buffer.cc index cf7a221..0ac6d37 100644 --- a/toolbelt/payload_buffer.cc +++ b/toolbelt/payload_buffer.cc @@ -53,6 +53,9 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, BufferOffset header_offset) { // Get address of the string header BufferOffset *hdr = (*self)->ToAddress(header_offset); + if (hdr == nullptr) { + return nullptr; + } void *str = nullptr; // Load the pointer and convert to address. @@ -66,6 +69,9 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, } else { str = Allocate(self, len + 4, 4, false); } + if (str == nullptr) { + return nullptr; + } uint32_t *p = reinterpret_cast(str); p[0] = uint32_t(len); memcpy(p + 1, s, len); @@ -73,6 +79,10 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, // The buffer may have moved. Reassign the address of the string // back into the header. BufferOffset *oldp = (*self)->ToAddress(header_offset); + if (oldp == nullptr) { + (*self)->Free(str); + return nullptr; + } *oldp = (*self)->ToOffset(str); return reinterpret_cast(str); } @@ -80,6 +90,9 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, void PayloadBuffer::ClearString(PayloadBuffer **self, BufferOffset header_offset) { BufferOffset *hdr = (*self)->ToAddress(header_offset); + if (hdr == nullptr) { + return; + } if (*hdr != 0) { (*self)->Free((*self)->ToAddress(*hdr)); // Free doesn't move the buffer so the address is still valid. @@ -89,42 +102,69 @@ void PayloadBuffer::ClearString(PayloadBuffer **self, // 'addr' is the address of the pointer to the string data. std::string PayloadBuffer::GetString(const StringHeader *addr) const { - const uint32_t *p = reinterpret_cast(ToAddress(*addr)); - if (p == nullptr) { + if (addr == nullptr) { + return ""; + } + const uint32_t *p = ToAddress(*addr); + if (p == nullptr || (*p > 0 && !IsValidAddress(p + 1, *p))) { return ""; } return std::string(reinterpret_cast(p + 1), *p); } std::string_view PayloadBuffer::GetStringView(const StringHeader *addr) const { - const uint32_t *p = reinterpret_cast(ToAddress(*addr)); - if (p == nullptr) { + if (addr == nullptr) { + return ""; + } + const uint32_t *p = ToAddress(*addr); + if (p == nullptr || (*p > 0 && !IsValidAddress(p + 1, *p))) { return ""; } return std::string_view(reinterpret_cast(p + 1), *p); } size_t PayloadBuffer::StringSize(const StringHeader *addr) const { - const uint32_t *p = reinterpret_cast(ToAddress(*addr)); - if (p == nullptr) { + if (addr == nullptr) { + return 0; + } + const uint32_t *p = ToAddress(*addr); + if (p == nullptr || (*p > 0 && !IsValidAddress(p + 1, *p))) { return 0; } return size_t(*p); } const char *PayloadBuffer::StringData(const StringHeader *addr) const { - const uint32_t *p = reinterpret_cast(ToAddress(*addr)); - if (p == nullptr) { + if (addr == nullptr) { + return nullptr; + } + const uint32_t *p = ToAddress(*addr); + if (p == nullptr || (*p > 0 && !IsValidAddress(p + 1, *p))) { return nullptr; } return reinterpret_cast(p + 1); } +bool PayloadBuffer::StringWithinBounds(const StringHeader *addr) const { + if (addr == nullptr) { + return false; + } + // An unset string has a zero body offset and serializes as empty. + if (*addr == 0) { + return true; + } + const uint32_t *p = ToAddress(*addr); + return p != nullptr && (*p == 0 || IsValidAddress(p + 1, *p)); +} + absl::Span PayloadBuffer::AllocateString(PayloadBuffer **self, size_t len, BufferOffset header_offset, bool clear) { // Get address of the string header BufferOffset *hdr = (*self)->ToAddress(header_offset); + if (hdr == nullptr) { + return {}; + } void *str = nullptr; // Load the pointer and convert to address. @@ -138,12 +178,19 @@ absl::Span PayloadBuffer::AllocateString(PayloadBuffer **self, size_t len, } else { str = Allocate(self, len + 4, 4, clear); } + if (str == nullptr) { + return {}; + } uint32_t *p = reinterpret_cast(str); p[0] = uint32_t(len); // The buffer may have moved. Reassign the address of the string // back into the header. BufferOffset *oldp = (*self)->ToAddress(header_offset); + if (oldp == nullptr) { + (*self)->Free(str); + return {}; + } *oldp = (*self)->ToOffset(str); // The span returned is the string data, not the address of the length. return absl::Span(reinterpret_cast(str) + 4, len); @@ -679,31 +726,49 @@ void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, (*buffer)->Free(p); return newp; } -bool PayloadBuffer::PrimeBitmapAllocator(PayloadBuffer **self, size_t size) { - int index = BitmapRunIndex(size); - if (index < 0) { - return true; - } - if ((*self)->bitmaps[index] != 0) { - return true; - } + +static bool InitializeBitMapRunVector(PayloadBuffer **self, int index, + uint32_t size, uint32_t num) { BufferOffset offset = (*self)->AllocateBitMapRunVector(self); if (offset == 0) { return false; } - (*self)->bitmaps[index] = offset; + auto free_bitmap_vector = [self, offset]() { + VectorHeader *hdr = (*self)->ToAddress(offset); + PayloadBuffer::VectorClear(self, hdr); + (*self)->Free((*self)->ToAddress(offset)); + }; - BitMapRun *run = PayloadBuffer::AllocateBitMapRun( - self, bitmp_run_infos[index].size, bitmp_run_infos[index].num); + BitMapRun *run = PayloadBuffer::AllocateBitMapRun(self, size, num); if (run == nullptr) { + free_bitmap_vector(); return false; } + // Re-derive hdr since AllocateBitMapRun may have triggered a buffer resize. - VectorHeader *hdr = (*self)->ToAddress((*self)->bitmaps[index]); - (*self)->VectorPush(self, hdr, (*self)->ToOffset(run), false); + VectorHeader *hdr = (*self)->ToAddress(offset); + BufferOffset run_offset = (*self)->ToOffset(run); + if (!(*self)->VectorPush(self, hdr, run_offset, false)) { + (*self)->Free((*self)->ToAddress(run_offset)); + free_bitmap_vector(); + return false; + } + (*self)->bitmaps[index] = offset; return true; } +bool PayloadBuffer::PrimeBitmapAllocator(PayloadBuffer **self, size_t size) { + int index = BitmapRunIndex(size); + if (index < 0) { + return true; + } + if ((*self)->bitmaps[index] != 0) { + return true; + } + return InitializeBitMapRunVector(self, index, bitmp_run_infos[index].size, + bitmp_run_infos[index].num); +} + BufferOffset PayloadBuffer::AllocateBitMapRunVector(PayloadBuffer **self) { // Allocate space for the VectorHeader. Although this is a small block, we // can't use the small block allocator because this is initializing it. @@ -714,8 +779,11 @@ BufferOffset PayloadBuffer::AllocateBitMapRunVector(PayloadBuffer **self) { BufferOffset hdr_offset = (*self)->ToOffset(hdr); // Preallocate space for 8 elements. - VectorReserve(self, reinterpret_cast(hdr), 8, - false); + if (!VectorReserve(self, reinterpret_cast(hdr), + 8, false)) { + (*self)->Free((*self)->ToAddress(hdr_offset)); + return 0; + } return hdr_offset; } @@ -741,11 +809,9 @@ void *BitMapRun::Allocate(PayloadBuffer **pb, int index, uint32_t, int size, int num, bool clear) { // Lazy init of vector. if ((*pb)->bitmaps[index] == 0) { - BufferOffset offset = (*pb)->AllocateBitMapRunVector(pb); - if (offset == 0) { + if (!InitializeBitMapRunVector(pb, index, size, num)) { return nullptr; } - (*pb)->bitmaps[index] = offset; } for (;;) { // Re-derive hdr each iteration since allocations below may trigger a @@ -791,7 +857,11 @@ void *BitMapRun::Allocate(PayloadBuffer **pb, int index, uint32_t, int size, // Re-derive hdr since AllocateBitMapRun may have triggered a buffer // resize, invalidating the previous pointer. hdr = (*pb)->ToAddress((*pb)->bitmaps[index]); - (*pb)->VectorPush(pb, hdr, (*pb)->ToOffset(run), false); + BufferOffset run_offset = (*pb)->ToOffset(run); + if (!(*pb)->VectorPush(pb, hdr, run_offset, false)) { + (*pb)->Free((*pb)->ToAddress(run_offset)); + return nullptr; + } } } diff --git a/toolbelt/payload_buffer.h b/toolbelt/payload_buffer.h index 08b1775..f112a86 100644 --- a/toolbelt/payload_buffer.h +++ b/toolbelt/payload_buffer.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace toolbelt { @@ -105,7 +106,7 @@ inline constexpr int kBitmapRunSize2 = 32; inline constexpr int kBitmapRunSize3 = 64; inline constexpr int kBitmapRunSize4 = 128; -// In order to allow free to work without searching, we use the 8 bytes +// In order to allow free to work without searching, we use the 4 bytes // preceding the allocated block in the run to store the size of the block, the // index into the BitMapRun vector and the bit number in the bitmap. In order // to distinguish this between small blocks and regular blocks allocated from @@ -246,6 +247,7 @@ struct PayloadBuffer { // The string is copied in. // C-string style (allows for no allocation of std::string). + // Returns nullptr without changing the header if allocation fails. static char *SetString(PayloadBuffer **self, const char *s, size_t len, BufferOffset header_offset); @@ -257,6 +259,7 @@ struct PayloadBuffer { static void ClearString(PayloadBuffer **self, BufferOffset header_offset); + // Returns an empty span without changing the header if allocation fails. static absl::Span AllocateString(PayloadBuffer **self, size_t len, BufferOffset header_offset, bool clear = false); @@ -269,16 +272,22 @@ struct PayloadBuffer { template void Set(BufferOffset offset, T v); template T &Get(BufferOffset offset); + // Appends v to the vector. Returns false without modifying hdr if allocation + // fails. template - static void VectorPush(PayloadBuffer **self, VectorHeader *hdr, T v, + static bool VectorPush(PayloadBuffer **self, VectorHeader *hdr, T v, bool enable_small_block = true); + // Reserves space for at least n elements. Returns false without modifying hdr + // if allocation fails. template - static void VectorReserve(PayloadBuffer **self, VectorHeader *hdr, size_t n, + static bool VectorReserve(PayloadBuffer **self, VectorHeader *hdr, size_t n, bool enable_small_block = true); + // Resizes the vector to n elements. Returns false without modifying hdr if + // allocation fails. template - static void VectorResize(PayloadBuffer **self, VectorHeader *hdr, size_t n); + static bool VectorResize(PayloadBuffer **self, VectorHeader *hdr, size_t n); template static void VectorClear(PayloadBuffer **self, VectorHeader *hdr); @@ -298,10 +307,19 @@ struct PayloadBuffer { return StringData(ToAddress(header_offset)); } + // True when the string at header_offset is safe to serialize: either unset + // (body offset 0, serializes as empty) or its length prefix and declared body + // lie wholly within the buffer. Lets the serializer reject a forged string + // rather than silently emitting empty as the general readers do. + bool StringWithinBounds(BufferOffset header_offset) const { + return StringWithinBounds(ToAddress(header_offset)); + } + std::string GetString(const StringHeader *addr) const; std::string_view GetStringView(const StringHeader *addr) const; size_t StringSize(const StringHeader *addr) const; const char *StringData(const StringHeader *addr) const; + bool StringWithinBounds(const StringHeader *addr) const; template T VectorGet(const VectorHeader *hdr, size_t index) const; @@ -340,12 +358,27 @@ struct PayloadBuffer { return (magic & kBitMapMask) == kMovableBufferMagic; } - bool IsValidAddress(const void *addr, size_t size) const { + // Integer-only bounds check on a [offset, offset + size) range. size == 0 + // means the access extent is unknown (e.g. a void* or a variable-length + // region); only the start is validated. The range form is written as a + // subtraction to avoid overflow when offset + size would wrap. + bool IsValidOffset(size_t offset, size_t size) const { if (size == 0) { - size = full_size; + return offset < full_size; + } + return offset <= full_size && size <= full_size - offset; + } + + bool IsValidAddress(const void *addr, size_t size) const { + // Compare and subtract through uintptr_t rather than pointers: 'addr' may + // come from an unrelated allocation, and comparing/subtracting unrelated + // pointers is undefined behavior. + const uintptr_t base = reinterpret_cast(this); + const uintptr_t a = reinterpret_cast(addr); + if (a < base) { + return false; } - return addr >= reinterpret_cast(this) && - addr < reinterpret_cast(this) + size; + return IsValidOffset(static_cast(a - base), size); } // Given the address of a block, return the size of the block. This is @@ -367,13 +400,20 @@ struct PayloadBuffer { if (!IsValidMagic()) { return nullptr; } - // Validate that we don't go outside the buffer. - char *addr = reinterpret_cast(this) + offset; - if (!IsValidAddress(addr, size)) { + // Without an explicit size, bound the access by sizeof(T) so a value that + // starts in-bounds but extends past the buffer end is rejected. + if constexpr (!std::is_void_v) { + if (size == 0) { + size = sizeof(T); + } + } + // Validate with integer arithmetic before forming any pointer past the + // buffer, which would itself be undefined behavior. + if (!IsValidOffset(offset, size)) { return nullptr; } - return reinterpret_cast(addr); + return reinterpret_cast(reinterpret_cast(this) + offset); } template BufferOffset ToOffset(T *addr, size_t size = 0) { @@ -383,11 +423,16 @@ struct PayloadBuffer { if (!IsValidMagic()) { return 0; } + if constexpr (!std::is_void_v) { + if (size == 0) { + size = sizeof(T); + } + } if (!IsValidAddress(addr, size)) { return 0; } - return reinterpret_cast(addr) - - reinterpret_cast(this); + return static_cast(reinterpret_cast(addr) - + reinterpret_cast(this)); } template @@ -398,9 +443,16 @@ struct PayloadBuffer { if (!IsValidMagic()) { return nullptr; } - // Validate that we don't go outside the buffer. - const char *addr = reinterpret_cast(this) + offset; - if (!IsValidAddress(addr, size)) { + // Without an explicit size, bound the access by sizeof(T) so a value that + // starts in-bounds but extends past the buffer end is rejected. + if constexpr (!std::is_void_v) { + if (size == 0) { + size = sizeof(T); + } + } + // Validate with integer arithmetic before forming any pointer past the + // buffer, which would itself be undefined behavior. + if (!IsValidOffset(offset, size)) { return nullptr; } @@ -416,11 +468,16 @@ struct PayloadBuffer { if (!IsValidMagic()) { return 0; } + if constexpr (!std::is_void_v) { + if (size == 0) { + size = sizeof(T); + } + } if (!IsValidAddress(addr, size)) { return 0; } - return reinterpret_cast(addr) - - reinterpret_cast(this); + return static_cast(reinterpret_cast(addr) - + reinterpret_cast(this)); } void InsertNewFreeBlockAtEnd(FreeBlockHeader *free_block, @@ -474,7 +531,7 @@ template inline T &PayloadBuffer::Get(BufferOffset offset) { } template -inline void PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, +inline bool PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, T v, bool enable_small_block) { // hdr points to a VectorHeader: // uint32_t num_elements; - number of elements in the vector @@ -487,6 +544,9 @@ inline void PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, if (hdr->data == 0) { // The vector is empty, allocate it with a default size of 2. void *vecp = Allocate(self, 2 * sizeof(T), true, enable_small_block); + if (vecp == nullptr) { + return false; + } VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); hdr = new_hdr; @@ -494,11 +554,14 @@ inline void PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodeSize(block); + uint32_t current_size = DecodedSize(block); if (current_size == total_size) { // Need to double the size of the memory. void *vecp = Realloc(self, block, 2 * hdr->num_elements * sizeof(T), true, enable_small_block); + if (vecp == nullptr) { + return false; + } VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); hdr = new_hdr; @@ -510,57 +573,77 @@ inline void PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, *valuep = v; // Increment the number of elements. hdr->num_elements++; + return true; } template -inline void PayloadBuffer::VectorReserve(PayloadBuffer **self, +inline bool PayloadBuffer::VectorReserve(PayloadBuffer **self, VectorHeader *hdr, size_t n, bool enable_small_block) { + if (n == 0) { + return true; + } BufferOffset hdr_offset = (*self)->ToOffset(hdr); if (hdr->data == 0) { void *vecp = Allocate(self, n * sizeof(T), false, enable_small_block); - VectorHeader* new_hdr = (*self)->ToAddress(hdr_offset); + if (vecp == nullptr) { + return false; + } + VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); - hdr = new_hdr; } else { // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodeSize(block); + uint32_t current_size = DecodedSize(block); if (current_size < n * sizeof(T)) { // Need to expand the memory to the size given. void *vecp = Realloc(self, block, n * sizeof(T), false, enable_small_block); - VectorHeader* new_hdr = (*self)->ToAddress(hdr_offset); + if (vecp == nullptr) { + return false; + } + VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); - hdr = new_hdr; } } + return true; } template -inline void PayloadBuffer::VectorResize(PayloadBuffer **self, VectorHeader *hdr, +inline bool PayloadBuffer::VectorResize(PayloadBuffer **self, VectorHeader *hdr, size_t n) { + if (n == 0 && hdr->data == 0) { + hdr->num_elements = 0; + return true; + } BufferOffset hdr_offset = (*self)->ToOffset(hdr); if (hdr->data == 0) { void *vecp = Allocate(self, n * sizeof(T)); - VectorHeader* new_hdr = (*self)->ToAddress(hdr_offset); + if (vecp == nullptr) { + return false; + } + VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); hdr = new_hdr; } else { // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodeSize(block); + uint32_t current_size = DecodedSize(block); if (current_size < n * sizeof(T)) { // Need to expand the memory to the size given. void *vecp = Realloc(self, block, n * sizeof(T), 8); - VectorHeader* new_hdr = (*self)->ToAddress(hdr_offset); + if (vecp == nullptr) { + return false; + } + VectorHeader *new_hdr = (*self)->ToAddress(hdr_offset); new_hdr->data = (*self)->ToOffset(vecp); hdr = new_hdr; } } hdr->num_elements = n; + return true; } template @@ -578,7 +661,7 @@ inline T PayloadBuffer::VectorGet(const VectorHeader *hdr, size_t index) const { if (index >= hdr->num_elements) { return static_cast(0); } - const T *addr = ToAddress(hdr->data); + const T *addr = ToAddress(hdr->data, (index + 1) * sizeof(T)); if (addr == nullptr) { return static_cast(0); } diff --git a/toolbelt/payload_buffer_test.cc b/toolbelt/payload_buffer_test.cc index cd7d9db..2c8aa2e 100644 --- a/toolbelt/payload_buffer_test.cc +++ b/toolbelt/payload_buffer_test.cc @@ -3,6 +3,7 @@ #include "toolbelt/payload_buffer.h" #include #include +#include #include using PayloadBuffer = toolbelt::PayloadBuffer; @@ -180,6 +181,167 @@ TEST(BufferTest, SmallBlockAllocFree) { free(buffer); } +TEST(BufferTest, PrimeBitmapAllocatorReserveFailureReclaimsVectorHeader) { + constexpr size_t kReserveAllocationSize = + 8 * sizeof(BufferOffset) + sizeof(uint64_t); + constexpr size_t kSize = sizeof(PayloadBuffer) + kReserveAllocationSize; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + const BufferOffset initial_free_list = pb->free_list; + toolbelt::FreeBlockHeader *initial_free_block = + pb->ToAddress(initial_free_list); + ASSERT_NE(nullptr, initial_free_block); + const uint32_t initial_free_length = initial_free_block->length; + + EXPECT_FALSE(PayloadBuffer::PrimeBitmapAllocator( + &pb, toolbelt::kBitmapRunSize1)); + EXPECT_EQ(0u, pb->bitmaps[0]); + EXPECT_EQ(initial_free_list, pb->free_list); + toolbelt::FreeBlockHeader *restored_free_block = + pb->ToAddress(pb->free_list); + ASSERT_NE(nullptr, restored_free_block); + EXPECT_EQ(initial_free_length, restored_free_block->length); + + free(buffer); +} + +TEST(BufferTest, PrimeBitmapAllocatorRunFailureRollsBackInitialization) { + constexpr size_t kVectorHeaderAllocationSize = + sizeof(VectorHeader) + sizeof(uint64_t); + constexpr size_t kReserveAllocationSize = + 8 * sizeof(BufferOffset) + sizeof(uint64_t); + constexpr size_t kSize = + sizeof(PayloadBuffer) + kVectorHeaderAllocationSize + + kReserveAllocationSize + sizeof(toolbelt::FreeBlockHeader); + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + const BufferOffset initial_free_list = pb->free_list; + toolbelt::FreeBlockHeader *initial_free_block = + pb->ToAddress(initial_free_list); + ASSERT_NE(nullptr, initial_free_block); + const uint32_t initial_free_length = initial_free_block->length; + + for (int attempt = 0; attempt < 2; attempt++) { + EXPECT_FALSE(PayloadBuffer::PrimeBitmapAllocator( + &pb, toolbelt::kBitmapRunSize1)); + EXPECT_EQ(0u, pb->bitmaps[0]); + EXPECT_EQ(initial_free_list, pb->free_list); + toolbelt::FreeBlockHeader *restored_free_block = + pb->ToAddress(pb->free_list); + ASSERT_NE(nullptr, restored_free_block); + EXPECT_EQ(initial_free_length, restored_free_block->length); + } + + free(buffer); +} + +TEST(BufferTest, LazyBitmapAllocatorRunFailureRollsBackInitialization) { + constexpr size_t kVectorHeaderAllocationSize = + sizeof(VectorHeader) + sizeof(uint64_t); + constexpr size_t kReserveAllocationSize = + 8 * sizeof(BufferOffset) + sizeof(uint64_t); + constexpr size_t kSize = + sizeof(PayloadBuffer) + kVectorHeaderAllocationSize + + kReserveAllocationSize + sizeof(toolbelt::FreeBlockHeader); + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + const BufferOffset initial_free_list = pb->free_list; + toolbelt::FreeBlockHeader *initial_free_block = + pb->ToAddress(initial_free_list); + ASSERT_NE(nullptr, initial_free_block); + const uint32_t initial_free_length = initial_free_block->length; + + for (int attempt = 0; attempt < 2; attempt++) { + EXPECT_EQ(nullptr, + PayloadBuffer::Allocate(&pb, toolbelt::kBitmapRunSize1)); + EXPECT_EQ(0u, pb->bitmaps[0]); + EXPECT_EQ(initial_free_list, pb->free_list); + toolbelt::FreeBlockHeader *restored_free_block = + pb->ToAddress(pb->free_list); + ASSERT_NE(nullptr, restored_free_block); + EXPECT_EQ(initial_free_length, restored_free_block->length); + } + + free(buffer); +} + +TEST(BufferTest, BitmapRunGrowthFailureReclaimsUnappendedRun) { + constexpr size_t kSize = 8192; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + ASSERT_TRUE( + PayloadBuffer::PrimeBitmapAllocator(&pb, toolbelt::kBitmapRunSize1)); + const BufferOffset bitmap_vector_offset = pb->bitmaps[0]; + VectorHeader *hdr = pb->ToAddress(bitmap_vector_offset); + ASSERT_NE(nullptr, hdr); + const size_t bitmap_capacity = + PayloadBuffer::DecodedSize(pb->ToAddress(hdr->data)) / + sizeof(BufferOffset); + ASSERT_GT(bitmap_capacity, hdr->num_elements); + + for (size_t i = hdr->num_elements; i < bitmap_capacity; i++) { + toolbelt::BitMapRun *run = PayloadBuffer::AllocateBitMapRun( + &pb, toolbelt::kBitmapRunSize1, toolbelt::kRunSize1); + ASSERT_NE(nullptr, run); + const BufferOffset run_offset = pb->ToOffset(run); + hdr = pb->ToAddress(bitmap_vector_offset); + ASSERT_TRUE( + PayloadBuffer::VectorPush(&pb, hdr, run_offset, false)); + } + hdr = pb->ToAddress(bitmap_vector_offset); + ASSERT_EQ(bitmap_capacity, hdr->num_elements); + const BufferOffset bitmap_data_offset = hdr->data; + for (size_t i = 0; i < hdr->num_elements; i++) { + toolbelt::BitMapRun *run = + pb->ToAddress(pb->VectorGet(hdr, i)); + ASSERT_NE(nullptr, run); + run->bits = run->num == 32 ? std::numeric_limits::max() + : (uint32_t{1} << run->num) - 1; + run->free = 0; + } + + toolbelt::BitMapRun *probe_run = PayloadBuffer::AllocateBitMapRun( + &pb, toolbelt::kBitmapRunSize1, toolbelt::kRunSize1); + ASSERT_NE(nullptr, probe_run); + const size_t run_allocation_size = + PayloadBuffer::DecodedSize(reinterpret_cast(probe_run)) + + sizeof(uint64_t); + pb->Free(probe_run); + + toolbelt::FreeBlockHeader *free_block = pb->FreeList(); + ASSERT_NE(nullptr, free_block); + ASSERT_EQ(0u, free_block->next); + ASSERT_GT(free_block->length, run_allocation_size + sizeof(uint64_t)); + const size_t drain_size = + free_block->length - run_allocation_size - sizeof(uint64_t); + ASSERT_EQ(0u, drain_size % sizeof(uint64_t)); + ASSERT_NE(nullptr, PayloadBuffer::Allocate(&pb, drain_size, false, false)); + + const BufferOffset initial_free_list = pb->free_list; + free_block = pb->FreeList(); + ASSERT_NE(nullptr, free_block); + ASSERT_EQ(run_allocation_size, free_block->length); + + for (int attempt = 0; attempt < 2; attempt++) { + EXPECT_EQ(nullptr, + PayloadBuffer::Allocate(&pb, toolbelt::kBitmapRunSize1)); + hdr = pb->ToAddress(bitmap_vector_offset); + ASSERT_NE(nullptr, hdr); + EXPECT_EQ(bitmap_capacity, hdr->num_elements); + EXPECT_EQ(bitmap_data_offset, hdr->data); + EXPECT_EQ(initial_free_list, pb->free_list); + free_block = pb->FreeList(); + ASSERT_NE(nullptr, free_block); + EXPECT_EQ(run_allocation_size, free_block->length); + } + + free(buffer); +} + // This performance test compares the performance of the small block allocator // against the regular allocator. It is a best-case test where we are not // stressing the small block allocator by allocating more blocks than a single @@ -618,6 +780,145 @@ TEST(BufferTest, VectorResizeWithResize) { free(buffer); } +TEST(BufferTest, EmptyVectorZeroSizeOperationsSucceed) { + constexpr size_t kSize = 256; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); + + ASSERT_NE(nullptr, + PayloadBuffer::AllocateMainMessage(&pb, sizeof(VectorHeader))); + VectorHeader *hdr = pb->ToAddress(pb->message); + + EXPECT_TRUE(PayloadBuffer::VectorReserve(&pb, hdr, 0, false)); + EXPECT_TRUE(PayloadBuffer::VectorResize(&pb, hdr, 0)); + EXPECT_EQ(0u, hdr->data); + EXPECT_EQ(0u, hdr->num_elements); + + free(buffer); +} + +TEST(BufferTest, VectorPushFixedBufferAllocationFailurePreservesHeader) { + constexpr size_t kSize = 256; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); + + ASSERT_NE(nullptr, + PayloadBuffer::AllocateMainMessage(&pb, sizeof(VectorHeader))); + const BufferOffset msg_offset = pb->message; + VectorHeader *hdr = pb->ToAddress(msg_offset); + + toolbelt::FreeBlockHeader *free_block = + pb->ToAddress(pb->free_list); + ASSERT_NE(nullptr, free_block); + ASSERT_NE(nullptr, + PayloadBuffer::Allocate(&pb, free_block->length - sizeof(uint64_t), + false, false)); + ASSERT_EQ(0u, pb->free_list); + + EXPECT_FALSE( + PayloadBuffer::VectorPush(&pb, hdr, 0x12345678, false)); + hdr = pb->ToAddress(msg_offset); + EXPECT_EQ(0u, hdr->data); + EXPECT_EQ(0u, hdr->num_elements); + + free(buffer); +} + +TEST(BufferTest, VectorPushFixedBufferGrowthFailurePreservesHeader) { + constexpr size_t kSize = 256; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); + + ASSERT_NE(nullptr, + PayloadBuffer::AllocateMainMessage(&pb, sizeof(VectorHeader))); + const BufferOffset msg_offset = pb->message; + VectorHeader *hdr = pb->ToAddress(msg_offset); + ASSERT_TRUE( + PayloadBuffer::VectorPush(&pb, hdr, 0x12345678, false)); + ASSERT_TRUE( + PayloadBuffer::VectorPush(&pb, hdr, 0x9abcdef0, false)); + + hdr = pb->ToAddress(msg_offset); + const BufferOffset original_data = hdr->data; + toolbelt::FreeBlockHeader *free_block = + pb->ToAddress(pb->free_list); + ASSERT_NE(nullptr, free_block); + ASSERT_NE(nullptr, + PayloadBuffer::Allocate(&pb, free_block->length - sizeof(uint64_t), + false, false)); + ASSERT_EQ(0u, pb->free_list); + + EXPECT_FALSE( + PayloadBuffer::VectorPush(&pb, hdr, 0xdeadbeef, false)); + hdr = pb->ToAddress(msg_offset); + EXPECT_EQ(original_data, hdr->data); + ASSERT_EQ(2u, hdr->num_elements); + EXPECT_EQ(0x12345678u, pb->VectorGet(hdr, 0)); + EXPECT_EQ(0x9abcdef0u, pb->VectorGet(hdr, 1)); + + free(buffer); +} + +TEST(BufferTest, VectorReserveFixedBufferFailurePreservesHeader) { + constexpr size_t kSize = 256; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); + + ASSERT_NE(nullptr, + PayloadBuffer::AllocateMainMessage(&pb, sizeof(VectorHeader))); + const BufferOffset msg_offset = pb->message; + VectorHeader *hdr = pb->ToAddress(msg_offset); + + EXPECT_FALSE(PayloadBuffer::VectorReserve(&pb, hdr, kSize, false)); + EXPECT_EQ(0u, hdr->data); + EXPECT_EQ(0u, hdr->num_elements); + + ASSERT_TRUE( + PayloadBuffer::VectorPush(&pb, hdr, 0x12345678, false)); + hdr = pb->ToAddress(msg_offset); + const BufferOffset original_data = hdr->data; + + EXPECT_FALSE(PayloadBuffer::VectorReserve(&pb, hdr, kSize, false)); + hdr = pb->ToAddress(msg_offset); + EXPECT_EQ(original_data, hdr->data); + ASSERT_EQ(1u, hdr->num_elements); + EXPECT_EQ(0x12345678u, pb->VectorGet(hdr, 0)); + + free(buffer); +} + +TEST(BufferTest, VectorResizeFixedBufferFailurePreservesHeader) { + constexpr size_t kSize = 256; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); + + ASSERT_NE(nullptr, + PayloadBuffer::AllocateMainMessage(&pb, sizeof(VectorHeader))); + const BufferOffset msg_offset = pb->message; + VectorHeader *hdr = pb->ToAddress(msg_offset); + + EXPECT_FALSE(PayloadBuffer::VectorResize(&pb, hdr, kSize)); + EXPECT_EQ(0u, hdr->data); + EXPECT_EQ(0u, hdr->num_elements); + + ASSERT_TRUE(PayloadBuffer::VectorResize(&pb, hdr, 2)); + hdr = pb->ToAddress(msg_offset); + const BufferOffset original_data = hdr->data; + uint32_t *values = pb->ToAddress(original_data, 2 * sizeof(uint32_t)); + ASSERT_NE(nullptr, values); + values[0] = 0x12345678; + values[1] = 0x9abcdef0; + + EXPECT_FALSE(PayloadBuffer::VectorResize(&pb, hdr, kSize)); + hdr = pb->ToAddress(msg_offset); + EXPECT_EQ(original_data, hdr->data); + ASSERT_EQ(2u, hdr->num_elements); + EXPECT_EQ(0x12345678u, pb->VectorGet(hdr, 0)); + EXPECT_EQ(0x9abcdef0u, pb->VectorGet(hdr, 1)); + + free(buffer); +} + TEST(BufferTest, Resizeable) { char *buffer = (char *)calloc(1, 512); bool resized = false; @@ -668,6 +969,321 @@ TEST(BufferTest, Resizeable) { free(pb); } +TEST(BufferTest, ToAddressRejectsTypedReadPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + EXPECT_EQ(pb->ToAddress(kSize - 2), nullptr); + EXPECT_NE(pb->ToAddress(kSize - sizeof(uint32_t)), nullptr); + + free(buffer); +} + +TEST(BufferTest, StringHelpersRejectLengthHeaderPastEnd) { + constexpr size_t kSize = 4093; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + toolbelt::StringHeader header = static_cast(4092); + + EXPECT_EQ(pb->StringSize(&header), 0u); + EXPECT_EQ(pb->GetString(&header), ""); + + free(buffer); +} + +TEST(BufferTest, StringSizeRejectsBodyPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + toolbelt::StringHeader header = + static_cast(kSize - sizeof(uint32_t)); + uint32_t declared = 1; + memcpy(buffer + header, &declared, sizeof(declared)); + + EXPECT_EQ(pb->StringData(&header), nullptr); + EXPECT_EQ(pb->StringSize(&header), 0u); + + free(buffer); +} + +TEST(BufferTest, EmptyStringAtBufferTailAccepted) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + toolbelt::StringHeader header = + static_cast(kSize - sizeof(uint32_t)); + uint32_t declared = 0; + memcpy(buffer + header, &declared, sizeof(declared)); + + EXPECT_NE(pb->StringData(&header), nullptr); + EXPECT_EQ(pb->StringSize(&header), 0u); + EXPECT_EQ(pb->GetString(&header), ""); + EXPECT_EQ(pb->GetStringView(&header), ""); + + free(buffer); +} + +TEST(BufferTest, StringWithinBoundsAcceptsValidString) { + char *buffer = (char *)calloc(4096, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); + + PayloadBuffer::AllocateMainMessage(&pb, 32); + BufferOffset offset = pb->ToOffset(pb->ToAddress(pb->message)); + PayloadBuffer::SetString(&pb, std::string("foobar"), offset); + + EXPECT_TRUE(pb->StringWithinBounds(offset)); + + free(buffer); +} + +TEST(BufferTest, StringWithinBoundsAcceptsUnsetString) { + char *buffer = (char *)calloc(4096, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); + + PayloadBuffer::AllocateMainMessage(&pb, sizeof(BufferOffset)); + + EXPECT_TRUE(pb->StringWithinBounds(pb->message)); + + free(buffer); +} + +TEST(BufferTest, SetStringFixedBufferFailurePreservesHeader) { + constexpr uint32_t kBufferSize = 4096; + char *buffer = (char *)calloc(kBufferSize, 1); + PayloadBuffer *pb = + new (buffer) PayloadBuffer(kBufferSize, /*bitmap_allocator=*/false); + PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); + + const uint32_t free_len = pb->FreeList()->length; + constexpr uint32_t kRemainingBytes = 2 * sizeof(uint64_t); + ASSERT_GT(free_len, kRemainingBytes + sizeof(uint64_t)); + const uint32_t drain = free_len - kRemainingBytes - sizeof(uint64_t); + ASSERT_NE(PayloadBuffer::Allocate(&pb, drain), nullptr); + + EXPECT_EQ(PayloadBuffer::SetString(&pb, "too large", pb->message), nullptr); + EXPECT_EQ(*pb->ToAddress(pb->message), + BufferOffset(0)); + EXPECT_TRUE(PayloadBuffer::AllocateString(&pb, 9, pb->message).empty()); + EXPECT_EQ(*pb->ToAddress(pb->message), + BufferOffset(0)); + + free(buffer); +} + +TEST(BufferTest, StringReallocFixedBufferFailurePreservesValue) { + constexpr uint32_t kBufferSize = 4096; + char *buffer = (char *)calloc(kBufferSize, 1); + PayloadBuffer *pb = + new (buffer) PayloadBuffer(kBufferSize, /*bitmap_allocator=*/false); + PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); + ASSERT_NE(PayloadBuffer::SetString(&pb, "x", pb->message), nullptr); + + const BufferOffset original_offset = + *pb->ToAddress(pb->message); + ASSERT_NE(original_offset, BufferOffset(0)); + + const uint32_t free_len = pb->FreeList()->length; + constexpr uint32_t kRemainingBytes = 2 * sizeof(uint64_t); + ASSERT_GT(free_len, kRemainingBytes + sizeof(uint64_t)); + const uint32_t drain = free_len - kRemainingBytes - sizeof(uint64_t); + ASSERT_NE(PayloadBuffer::Allocate(&pb, drain), nullptr); + + EXPECT_EQ(PayloadBuffer::SetString(&pb, "too large", pb->message), nullptr); + EXPECT_EQ(*pb->ToAddress(pb->message), + original_offset); + EXPECT_EQ(pb->GetString(pb->ToAddress(pb->message)), + "x"); + EXPECT_TRUE(PayloadBuffer::AllocateString(&pb, 9, pb->message).empty()); + EXPECT_EQ(*pb->ToAddress(pb->message), + original_offset); + EXPECT_EQ(pb->GetString(pb->ToAddress(pb->message)), + "x"); + + free(buffer); +} + +TEST(BufferTest, StringWithinBoundsRejectsNullHeader) { + char *buffer = (char *)calloc(4096, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); + + EXPECT_FALSE( + pb->StringWithinBounds(static_cast(nullptr))); + + free(buffer); +} + +TEST(BufferTest, StringReadersReturnEmptyForNullHeader) { + char *buffer = (char *)calloc(4096, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); + + const toolbelt::StringHeader *header = nullptr; + + EXPECT_EQ(pb->GetString(header), ""); + EXPECT_EQ(pb->GetStringView(header), ""); + EXPECT_EQ(pb->StringSize(header), 0u); + EXPECT_EQ(pb->StringData(header), nullptr); + + free(buffer); +} + +TEST(BufferTest, StringReadersRejectHeaderOffsetPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + const BufferOffset straddling = static_cast(kSize - 2); + + EXPECT_EQ(pb->GetString(straddling), ""); + EXPECT_EQ(pb->GetStringView(straddling), ""); + EXPECT_EQ(pb->StringSize(straddling), 0u); + EXPECT_EQ(pb->StringData(straddling), nullptr); + + const BufferOffset unset = static_cast(0); + + EXPECT_EQ(pb->GetString(unset), ""); + EXPECT_EQ(pb->GetStringView(unset), ""); + EXPECT_EQ(pb->StringSize(unset), 0u); + EXPECT_EQ(pb->StringData(unset), nullptr); + + free(buffer); +} + +TEST(BufferTest, StringWritersRejectHeaderOffsetPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = + new (buffer) PayloadBuffer(kSize, /*bitmap_allocator=*/false); + const uint32_t free_len = pb->FreeList()->length; + + const BufferOffset straddling = static_cast(kSize - 2); + + EXPECT_EQ(PayloadBuffer::SetString(&pb, "x", 1, straddling), nullptr); + EXPECT_TRUE(PayloadBuffer::AllocateString(&pb, 1, straddling).empty()); + PayloadBuffer::ClearString(&pb, straddling); + + const BufferOffset unset = static_cast(0); + + EXPECT_EQ(PayloadBuffer::SetString(&pb, "x", 1, unset), nullptr); + EXPECT_TRUE(PayloadBuffer::AllocateString(&pb, 1, unset).empty()); + PayloadBuffer::ClearString(&pb, unset); + + EXPECT_EQ(pb->FreeList()->length, free_len); + + free(buffer); +} + +TEST(BufferTest, AllocateStringReturnsWritableSpanAndStoresOffset) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = + new (buffer) PayloadBuffer(kSize, /*bitmap_allocator=*/false); + PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); + + absl::Span str = PayloadBuffer::AllocateString(&pb, 3, pb->message); + ASSERT_EQ(str.size(), 3u); + EXPECT_NE(*pb->ToAddress(pb->message), + BufferOffset(0)); + + memcpy(str.data(), "abc", 3); + EXPECT_EQ(pb->GetString(pb->message), "abc"); + + absl::Span grown = PayloadBuffer::AllocateString(&pb, 5, pb->message); + ASSERT_EQ(grown.size(), 5u); + + memcpy(grown.data(), "abcde", 5); + EXPECT_EQ(pb->GetString(pb->message), "abcde"); + + free(buffer); +} + +TEST(BufferTest, StringWithinBoundsRejectsBodyOffsetPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + toolbelt::StringHeader header = static_cast(kSize - 2); + + EXPECT_FALSE(pb->StringWithinBounds(&header)); + + free(buffer); +} + +TEST(BufferTest, StringWithinBoundsRejectsBodyPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + toolbelt::StringHeader header = + static_cast(kSize - sizeof(uint32_t)); + uint32_t declared = 1; + memcpy(buffer + header, &declared, sizeof(declared)); + + EXPECT_FALSE(pb->StringWithinBounds(&header)); + + free(buffer); +} + +TEST(BufferTest, VectorGetRejectsIndexPastEnd) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + VectorHeader hdr; + hdr.num_elements = 2; + hdr.data = static_cast(kSize - sizeof(uint32_t)); + + EXPECT_EQ(pb->VectorGet(&hdr, 1), 0u); + + free(buffer); +} + +TEST(BufferTest, ToOffsetAndToAddressAgreeOnTrailingExtent) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + uint32_t *p = + reinterpret_cast(reinterpret_cast(pb) + (kSize - 2)); + + EXPECT_EQ(pb->ToAddress(kSize - 2), nullptr); + EXPECT_EQ(pb->ToOffset(p), 0u); + + free(buffer); +} + +TEST(BufferTest, ToAddressVoidStartOnlyBoundary) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + + EXPECT_NE(pb->ToAddress(kSize - 1), nullptr); + EXPECT_EQ(pb->ToAddress(kSize), nullptr); + + free(buffer); +} + +TEST(BufferTest, ToAddressAndToOffsetRejectOutOfRangeInputs) { + constexpr size_t kSize = 4096; + char *buffer = (char *)calloc(kSize, 1); + PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); + char *other_buffer = (char *)calloc(kSize, 1); + + const BufferOffset far_offset = std::numeric_limits::max(); + EXPECT_EQ(pb->ToAddress(far_offset), nullptr); + EXPECT_EQ(pb->ToAddress(far_offset), nullptr); + + const uint32_t *foreign_address = + reinterpret_cast(other_buffer); + EXPECT_EQ(pb->ToOffset(foreign_address), 0u); + + free(other_buffer); + free(buffer); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); From 92b21ed06da676c39f60db8c6c3a9b981dc6fea0 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Sun, 30 Aug 2026 16:24:11 -0700 Subject: [PATCH 2/2] Fix extremely pedantic warnings --- .bazelrc | 3 + MODULE.bazel.lock | 297 ++++++++++++---- toolbelt/BUILD.bazel | 1 + toolbelt/fd.cc | 25 +- toolbelt/fd.h | 4 +- toolbelt/hexdump.cc | 19 +- toolbelt/logging.cc | 52 +-- .../network_receiver.cc | 10 +- .../manual_socket_programs/network_sender.cc | 10 +- .../manual_socket_programs/tcp_receiver.cc | 4 +- toolbelt/payload_buffer.cc | 319 +++++++++++------- toolbelt/payload_buffer.h | 87 ++++- toolbelt/payload_buffer_test.cc | 188 +++++------ toolbelt/pipe.cc | 25 +- toolbelt/pipe.h | 25 +- toolbelt/pipe_test.cc | 22 +- toolbelt/sockets.cc | 58 +++- toolbelt/sockets.h | 9 +- toolbelt/sockets_test.cc | 54 +-- toolbelt/table.cc | 43 ++- toolbelt/table.h | 2 +- 21 files changed, 833 insertions(+), 424 deletions(-) diff --git a/.bazelrc b/.bazelrc index ace2519..72d5978 100644 --- a/.bazelrc +++ b/.bazelrc @@ -5,6 +5,9 @@ build --cxxopt="-std=c++17" build:apple_silicon --cpu=darwin_arm64 build:apple_silicon --features=oso_prefix_is_pwd +# Strict diagnostics for this repository's C++ sources. +build:strict --per_file_copt=toolbelt/.*@-Wall,-Wextra,-Wpedantic,-Wconversion,-Wsign-conversion,-Wshadow,-Wnon-virtual-dtor,-Wold-style-cast,-Wcast-align,-Woverloaded-virtual,-Wnull-dereference,-Wdouble-promotion,-Wformat=2,-Wimplicit-fallthrough,-Wundef,-Wextra-semi,-Wcast-qual,-Wmissing-declarations,-Wheader-hygiene,-Wthread-safety,-Wcomma,-Wrange-loop-analysis,-Wdeprecated,-Werror,-Wno-nullability-extension,-Wno-gcc-compat,-Wno-unknown-warning-option + # ----------------------------------------------------------------------------- # Sanitizer / dynamic-analysis configurations. # diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 574c750..f8a48ff 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,5 +1,5 @@ { - "lockFileVersion": 24, + "lockFileVersion": 28, "registryFileHashes": { "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", @@ -14,22 +14,30 @@ "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215", "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/MODULE.bazel": "51f2312901470cdab0dbdf3b88c40cd21c62a7ed58a3de45b365ddc5b11bcab2", "https://bcr.bazel.build/modules/abseil-cpp/20250814.1/source.json": "cea3901d7e299da7320700abbaafe57a65d039f10d0d7ea601c4a66938ea4b0c", + "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", + "https://bcr.bazel.build/modules/apple_support/1.21.0/MODULE.bazel": "ac1824ed5edf17dee2fdd4927ada30c9f8c3b520be1b5fd02a5da15bc10bff3e", + "https://bcr.bazel.build/modules/apple_support/1.21.1/MODULE.bazel": "5809fa3efab15d1f3c3c635af6974044bac8a4919c62238cce06acee8a8c11f1", "https://bcr.bazel.build/modules/apple_support/1.22.1/MODULE.bazel": "90bd1a660590f3ceffbdf524e37483094b29352d85317060b2327fff8f3f4458", - "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", - "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2", + "https://bcr.bazel.build/modules/apple_support/1.24.2/MODULE.bazel": "0e62471818affb9f0b26f128831d5c40b074d32e6dda5a0d3852847215a41ca4", + "https://bcr.bazel.build/modules/apple_support/1.24.2/source.json": "2c22c9827093250406c5568da6c54e6fdf0ef06238def3d99c71b12feb057a8d", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a", "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", - "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6", "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080", + "https://bcr.bazel.build/modules/bazel_features/1.42.1/source.json": "fcd4396b2df85f64f2b3bb436ad870793ecf39180f1d796f913cc9276d355309", "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", @@ -43,12 +51,13 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7", "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0", - "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", - "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", - "https://bcr.bazel.build/modules/coroutines/3.3.1/MODULE.bazel": "96746c200b0890b9a124713598fc0eb028cb7bf04a796e393573179ce5a4d34f", - "https://bcr.bazel.build/modules/coroutines/3.3.1/source.json": "c79ca39719820a3cef8fea46a5c204f0ca1ee725acd4f2799266441faf53484d", + "https://bcr.bazel.build/modules/buildozer/8.5.1/MODULE.bazel": "a35d9561b3fc5b18797c330793e99e3b834a473d5fbd3d7d7634aafc9bdb6f8f", + "https://bcr.bazel.build/modules/buildozer/8.5.1/source.json": "e3386e6ff4529f2442800dee47ad28d3e6487f36a1f75ae39ae56c70f0cd2fbd", + "https://bcr.bazel.build/modules/coroutines/3.3.2/MODULE.bazel": "ad1395ae9758ee5d113acab755b4d972c368c4dab66513bfe31423136f3ee608", + "https://bcr.bazel.build/modules/coroutines/3.3.2/source.json": "fb47a4c3e13d730a1a58ed6a34add8f6aaeef655a47d0f179427f71bf58ba150", "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", @@ -58,8 +67,11 @@ "https://bcr.bazel.build/modules/googletest/1.17.0.bcr.2/source.json": "3664514073a819992320ffbce5825e4238459df344d8b01748af2208f8d2e1eb", "https://bcr.bazel.build/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46", "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", - "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000", "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", @@ -74,12 +86,13 @@ "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a", "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12", "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", - "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", - "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", - "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/32.1/MODULE.bazel": "89cd2866a9cb07fee9ff74c41ceace11554f32e0d849de4e23ac55515cfada4d", + "https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42", + "https://bcr.bazel.build/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34", "https://bcr.bazel.build/modules/pybind11_bazel/2.13.6/MODULE.bazel": "2d746fda559464b253b2b2e6073cb51643a2ac79009ca02100ebbc44b4548656", @@ -91,10 +104,12 @@ "https://bcr.bazel.build/modules/re2/2025-08-12.bcr.1/source.json": "a8ae7c09533bf67f9f6e5122d884d5741600b09d78dca6fc0f2f8d2ee0c2d957", "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a", + "https://bcr.bazel.build/modules/rules_apple/4.1.0/MODULE.bazel": "76e10fd4a48038d3fc7c5dc6e63b7063bbf5304a2e3bd42edda6ec660eebea68", + "https://bcr.bazel.build/modules/rules_apple/4.1.0/source.json": "8ee81e1708756f81b343a5eb2b2f0b953f1d25c4ab3d4a68dc02754872e80715", "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", - "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", @@ -103,38 +118,36 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", "https://bcr.bazel.build/modules/rules_cc/0.1.4/MODULE.bazel": "bb03a452a7527ac25a7518fb86a946ef63df860b9657d8323a0c50f8504fb0b9", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", + "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", - "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", - "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", - "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64", "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", - "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", - "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", + "https://bcr.bazel.build/modules/rules_java/9.1.0/MODULE.bazel": "ee63f27e36a3fada80342869361182f120a9819c74320e8e65b1e04ba0cd7a9d", + "https://bcr.bazel.build/modules/rules_java/9.1.0/source.json": "da589573c1dee2c9ac4a568b301269a2e8191110ff0345c1a959fa7ea6c4dfd6", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", - "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", - "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", - "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", - "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", + "https://bcr.bazel.build/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", @@ -148,8 +161,8 @@ "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", - "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", - "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96", + "https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e", "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", @@ -158,18 +171,29 @@ "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937", "https://bcr.bazel.build/modules/rules_python/0.34.0/MODULE.bazel": "1d623d026e075b78c9fde483a889cda7996f5da4f36dffb24c246ab30f06513a", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", - "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13", + "https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6", "https://bcr.bazel.build/modules/rules_python/1.5.1/MODULE.bazel": "acfe65880942d44a69129d4c5c3122d57baaf3edf58ae5a6bd4edea114906bf5", - "https://bcr.bazel.build/modules/rules_python/1.5.1/source.json": "aa903e1bcbdfa1580f2b8e2d55100b7c18bc92d779ebb507fec896c75635f7bd", + "https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8", + "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8", + "https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", - "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b", + "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c", + "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046", + "https://bcr.bazel.build/modules/rules_swift/2.4.0/MODULE.bazel": "1639617eb1ede28d774d967a738b4a68b0accb40650beadb57c21846beab5efd", + "https://bcr.bazel.build/modules/rules_swift/3.1.2/MODULE.bazel": "72c8f5cf9d26427cee6c76c8e3853eb46ce6b0412a081b2b6db6e8ad56267400", + "https://bcr.bazel.build/modules/rules_swift/3.1.2/source.json": "e85761f3098a6faf40b8187695e3de6d97944e98abd0d8ce579cb2daf6319a66", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", - "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", - "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", @@ -181,11 +205,11 @@ "moduleExtensions": { "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=", + "bzlTransitiveDigest": "+Kp6j204mBZ3mxlIDDR0gBoP45BZ4jYRhRAcB8sU0qc=", "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, + "recordedInputs": [ + "REPO_MAPPING:rules_kotlin+,bazel_tools bazel_tools" + ], "generatedRepoSpecs": { "com_github_jetbrains_kotlin_git": { "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", @@ -233,23 +257,185 @@ ] } } - }, - "recordedRepoMappingEntries": [ - [ - "rules_kotlin+", - "bazel_tools", - "bazel_tools" - ] - ] + } + } + }, + "@@rules_python+//python/extensions:config.bzl%config": { + "general": { + "bzlTransitiveDigest": "dzD8Q2YmrP3fz8saWLHPmlwPLO91ImtTmP/c9JKTStM=", + "usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=", + "recordedInputs": [ + "REPO_MAPPING:rules_python+,bazel_tools bazel_tools", + "REPO_MAPPING:rules_python+,pypi__build rules_python++config+pypi__build", + "REPO_MAPPING:rules_python+,pypi__click rules_python++config+pypi__click", + "REPO_MAPPING:rules_python+,pypi__colorama rules_python++config+pypi__colorama", + "REPO_MAPPING:rules_python+,pypi__importlib_metadata rules_python++config+pypi__importlib_metadata", + "REPO_MAPPING:rules_python+,pypi__installer rules_python++config+pypi__installer", + "REPO_MAPPING:rules_python+,pypi__more_itertools rules_python++config+pypi__more_itertools", + "REPO_MAPPING:rules_python+,pypi__packaging rules_python++config+pypi__packaging", + "REPO_MAPPING:rules_python+,pypi__pep517 rules_python++config+pypi__pep517", + "REPO_MAPPING:rules_python+,pypi__pip rules_python++config+pypi__pip", + "REPO_MAPPING:rules_python+,pypi__pip_tools rules_python++config+pypi__pip_tools", + "REPO_MAPPING:rules_python+,pypi__pyproject_hooks rules_python++config+pypi__pyproject_hooks", + "REPO_MAPPING:rules_python+,pypi__setuptools rules_python++config+pypi__setuptools", + "REPO_MAPPING:rules_python+,pypi__tomli rules_python++config+pypi__tomli", + "REPO_MAPPING:rules_python+,pypi__wheel rules_python++config+pypi__wheel", + "REPO_MAPPING:rules_python+,pypi__zipp rules_python++config+pypi__zipp" + ], + "generatedRepoSpecs": { + "rules_python_internal": { + "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo", + "attributes": { + "transition_setting_generators": {}, + "transition_settings": [] + } + }, + "pypi__build": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl", + "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__click": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__colorama": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__importlib_metadata": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", + "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__installer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__more_itertools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl", + "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__packaging": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", + "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pep517": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl", + "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl", + "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip_tools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl", + "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pyproject_hooks": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", + "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__setuptools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", + "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__tomli": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__wheel": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl", + "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__zipp": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl", + "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + } + } } }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { - "bzlTransitiveDigest": "8vT1ddXtljNxYD0tJkksqzeKE6xqx4Ix+tXthAppjTI=", - "usagesDigest": "WYhzIw9khRBy34H1GxV5+fI1yi07O90NmCXosPUdHWQ=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, + "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", + "usagesDigest": "H8dQoNZcoqP+Mu0tHZTi4KHATzvNkM5ePuEqoQdklIU=", + "recordedInputs": [ + "REPO_MAPPING:rules_python+,bazel_tools bazel_tools", + "REPO_MAPPING:rules_python+,platforms platforms" + ], "generatedRepoSpecs": { "uv": { "repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo", @@ -269,21 +455,10 @@ "toolchain_target_settings": {} } } - }, - "recordedRepoMappingEntries": [ - [ - "rules_python+", - "bazel_tools", - "bazel_tools" - ], - [ - "rules_python+", - "platforms", - "platforms" - ] - ] + } } } }, - "facts": {} + "facts": {}, + "factsVersions": {} } diff --git a/toolbelt/BUILD.bazel b/toolbelt/BUILD.bazel index 4fad7ab..19c8f75 100644 --- a/toolbelt/BUILD.bazel +++ b/toolbelt/BUILD.bazel @@ -20,6 +20,7 @@ cc_library( "bitset.h", "clock.h", "color.h", + "coroutine.h", "fd.h", "hexdump.h", "logging.h", diff --git a/toolbelt/fd.cc b/toolbelt/fd.cc index a160646..56b74d3 100644 --- a/toolbelt/fd.cc +++ b/toolbelt/fd.cc @@ -1,5 +1,7 @@ #include "toolbelt/fd.h" +#include + namespace toolbelt { // Close all open file descriptor for which the predicate returns true. @@ -7,8 +9,13 @@ void CloseAllFds(std::function predicate) { struct rlimit lim; int e = getrlimit(RLIMIT_NOFILE, &lim); if (e == 0) { - for (rlim_t fd = 0; fd < lim.rlim_cur; fd++) { - if (fcntl(fd, F_GETFD) == 0 && predicate(fd) ) { + const rlim_t int_max = static_cast(std::numeric_limits::max()); + for (rlim_t i = 0; i < lim.rlim_cur; ++i) { + if (i > int_max) { + break; + } + const int fd = static_cast(i); + if (fcntl(fd, F_GETFD) == 0 && predicate(fd)) { (void)close(fd); } } @@ -17,6 +24,9 @@ void CloseAllFds(std::function predicate) { absl::StatusOr FileDescriptor::Read(void *buffer, size_t length, const co::Coroutine *c) { + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Read size too large"); + } char *buf = reinterpret_cast(buffer); size_t total = 0; while (total < length) { @@ -52,13 +62,16 @@ absl::StatusOr FileDescriptor::Read(void *buffer, size_t length, return absl::InternalError( absl::StrFormat("Read failed: %s", strerror(errno))); } - total += n; + total += static_cast(n); } - return total; + return static_cast(total); } absl::StatusOr FileDescriptor::Write(const void *buffer, size_t length, const co::Coroutine *c) { + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Write size too large"); + } const char *buf = reinterpret_cast(buffer); size_t total = 0; @@ -95,9 +108,9 @@ absl::StatusOr FileDescriptor::Write(const void *buffer, size_t length, return absl::InternalError( absl::StrFormat("Write failed: %s", strerror(errno))); } - total += n; + total += static_cast(n); } - return total; + return static_cast(total); } } // namespace toolbelt \ No newline at end of file diff --git a/toolbelt/fd.h b/toolbelt/fd.h index 5760b56..4b957f1 100644 --- a/toolbelt/fd.h +++ b/toolbelt/fd.h @@ -18,7 +18,7 @@ #include #include #include -#include "co/coroutine.h" +#include "toolbelt/coroutine.h" namespace toolbelt { @@ -93,7 +93,7 @@ class FileDescriptor { bool IsATTY() const { return Valid() && isatty(data_->fd); } // Current reference count. - int RefCount() const { return data_ == nullptr ? 0 : data_.use_count(); } + long RefCount() const { return data_ == nullptr ? 0 : data_.use_count(); } // Construct and return a struct pollfd suitable for use in ::poll. struct pollfd GetPollFd() { diff --git a/toolbelt/hexdump.cc b/toolbelt/hexdump.cc index 850b635..9aab39d 100644 --- a/toolbelt/hexdump.cc +++ b/toolbelt/hexdump.cc @@ -11,23 +11,26 @@ namespace toolbelt { void Hexdump(const void *addr, size_t length, FILE* out) { const char *p = reinterpret_cast(addr); - length = (length + 15) & ~15; while (length > 0) { - fprintf(out, "%p ", p); - for (int i = 0; i < 16; i++) { - fprintf(out, "%02X ", p[i] & 0xff); + const size_t row_length = length < 16U ? length : 16U; + fprintf(out, "%p ", static_cast(p)); + for (size_t i = 0; i < row_length; i++) { + fprintf(out, "%02X ", static_cast(p[i]) & 0xffU); + } + for (size_t i = row_length; i < 16U; ++i) { + fprintf(out, " "); } fprintf(out, " "); - for (int i = 0; i < 16; i++) { - if (isprint(p[i])) { + for (size_t i = 0; i < row_length; i++) { + if (isprint(static_cast(p[i]))) { fprintf(out, "%c", p[i]); } else { fprintf(out, "."); } } fprintf(out, "\n"); - p += 16; - length -= 16; + p += row_length; + length -= row_length; } } diff --git a/toolbelt/logging.cc b/toolbelt/logging.cc index 00fcc3e..3bdfdfd 100644 --- a/toolbelt/logging.cc +++ b/toolbelt/logging.cc @@ -5,6 +5,7 @@ #include "logging.h" #include "absl/strings/str_format.h" #include "clock.h" +#include #include #include #include @@ -156,28 +157,38 @@ void Logger::VLog(LogLevel level, const char *fmt, va_list ap) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - size_t n = vsnprintf(buffer_, sizeof(buffer_), fmt, ap); + const int formatted = vsnprintf(buffer_, sizeof(buffer_), fmt, ap); #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif + const size_t n = + formatted < 0 + ? size_t{0} + : std::min(static_cast(formatted), sizeof(buffer_) - 1); + if (formatted < 0) { + buffer_[0] = '\0'; + } + // Strip final \n if present. Refactoring from printf can leave // this in place. - if (buffer_[n - 1] == '\n') { + if (n > 0 && buffer_[n - 1] == '\n') { buffer_[n - 1] = '\0'; } struct timespec now_ts; clock_gettime(CLOCK_REALTIME, &now_ts); - uint64_t now_ns = now_ts.tv_sec * 1000000000LL + now_ts.tv_nsec; + uint64_t now_ns = static_cast(now_ts.tv_sec) * 1000000000ULL + + static_cast(now_ts.tv_nsec); char timebuf[64]; struct tm tm; - n = strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", + const size_t time_length = + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", localtime_r(&now_ts.tv_sec, &tm)); - snprintf(timebuf + n, sizeof(timebuf) - n, ".%09" PRIu64, + snprintf(timebuf + time_length, sizeof(timebuf) - time_length, ".%09" PRIu64, now_ns % 1000000000); Log(level, now_ns, "", buffer_); @@ -191,13 +202,13 @@ void Logger::Log(LogLevel level, uint64_t timestamp, const std::string &source, // Strip final \n if present. Refactoring from printf can leave // this in place. - if (text[text.size() - 1] == '\n') { + if (!text.empty() && text.back() == '\n') { text = text.substr(0, text.size() - 1); } char timebuf[64]; struct tm tm; - time_t secs = timestamp / 1000000000LL; + time_t secs = static_cast(timestamp / 1000000000ULL); size_t n = strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", localtime_r(&secs, &tm)); snprintf(timebuf + n, sizeof(timebuf) - n, ".%09" PRIu64, @@ -245,20 +256,17 @@ void Logger::SetDisplayMode(int fd) { column_widths_[0] = 30; // Timestamp. // Subsystem, with a max of 20. - column_widths_[1] = int(subsystem_.size()); - if (column_widths_[1] > 20) { - column_widths_[1] = 20; - } + column_widths_[1] = std::min(subsystem_.size(), size_t{20}); column_widths_[2] = 3; // Log level column_widths_[3] = 20; // Source - ssize_t remaining = screen_width_; - for (int i = 0; i < 4; i++) { - remaining -= column_widths_[i] + 1; + ssize_t remaining = static_cast(screen_width_); + for (size_t i = 0; i < 4; i++) { + remaining -= static_cast(column_widths_[i] + 1); } - if (remaining < 0) { + if (remaining <= 1) { remaining = 20; } - column_widths_[4] = remaining - 1; + column_widths_[4] = static_cast(remaining - 1); display_mode_ = LogDisplayMode::kColumnar; } } else { @@ -313,8 +321,8 @@ void Logger::LogColumnar(const char *timebuf, LogLevel level, bool first_line = true; size_t start = 0; int prefix_length = 0; - for (int i = 0; i < 4; i++) { - prefix_length += column_widths_[i] + 1; + for (size_t i = 0; i < 4; i++) { + prefix_length += static_cast(column_widths_[i]) + 1; } for (;;) { std::string segment = text.substr(start); @@ -326,23 +334,23 @@ void Logger::LogColumnar(const char *timebuf, LogLevel level, if (segment.size() > column_widths_[4]) { segment = segment.substr(0, column_widths_[4]); // Move back to the first space to avoid splitting words. - ssize_t i = segment.size() - 1; + ssize_t i = static_cast(segment.size()) - 1; while (i > 0) { - if (isspace(segment[i])) { + if (isspace(segment[static_cast(i)])) { break; } i--; } // If there is no space we just split the word. if (i != 0) { - segment = segment.substr(0, i); + segment = segment.substr(0, static_cast(i)); } } // clang-format off. fprintf(output_stream_, "%-*s%s%-*s%s\n", prefix_length, first_line ? prefix.c_str() : "", color::SetColor(ColorForLogLevel(level)).c_str(), - int(column_widths_[4]), segment.c_str(), + static_cast(column_widths_[4]), segment.c_str(), color::ResetColor().c_str()); // clang-format on start += segment.size(); diff --git a/toolbelt/manual_socket_programs/network_receiver.cc b/toolbelt/manual_socket_programs/network_receiver.cc index a60e966..a8f30a2 100644 --- a/toolbelt/manual_socket_programs/network_receiver.cc +++ b/toolbelt/manual_socket_programs/network_receiver.cc @@ -17,7 +17,11 @@ int main(int argc, char **argv) { if (protocol == "tcp") { addr = toolbelt::InetAddress::AnyAddress(port); } else if (protocol == "vm") { - addr = toolbelt::VirtualAddress::AnyAddress(port); + if (port < 0) { + std::cerr << "VM port must be non-negative" << std::endl; + return 1; + } + addr = toolbelt::VirtualAddress::AnyAddress(static_cast(port)); } else { std::cerr << "Unknown protocol: " << protocol << std::endl; return 1; @@ -68,7 +72,9 @@ int main(int argc, char **argv) { return 1; } std::cerr << "Received " << *status_or - << " bytes: " << std::string(message, *status_or) << std::endl; + << " bytes: " + << std::string(message, static_cast(*status_or)) + << std::endl; } return 0; diff --git a/toolbelt/manual_socket_programs/network_sender.cc b/toolbelt/manual_socket_programs/network_sender.cc index 5676ab7..c5867fc 100644 --- a/toolbelt/manual_socket_programs/network_sender.cc +++ b/toolbelt/manual_socket_programs/network_sender.cc @@ -1,5 +1,7 @@ #include "toolbelt/sockets.h" +#include + int main(int argc, char *argv[]) { // TCP socket sender. // 3 args: @@ -20,7 +22,13 @@ int main(int argc, char *argv[]) { if (protocol == "tcp") { addr = toolbelt::InetAddress(address, port); } else if (protocol == "vm") { - addr = toolbelt::VirtualAddress(std::atoi(address.c_str()), port); + const unsigned long cid = std::stoul(address); + if (cid > std::numeric_limits::max() || port < 0) { + std::cerr << "VM CID and port must fit in uint32_t" << std::endl; + return 1; + } + addr = toolbelt::VirtualAddress(static_cast(cid), + static_cast(port)); } else { std::cerr << "Unknown protocol: " << protocol << std::endl; return 1; diff --git a/toolbelt/manual_socket_programs/tcp_receiver.cc b/toolbelt/manual_socket_programs/tcp_receiver.cc index 4321549..f86f0c5 100644 --- a/toolbelt/manual_socket_programs/tcp_receiver.cc +++ b/toolbelt/manual_socket_programs/tcp_receiver.cc @@ -45,7 +45,9 @@ int main(int argc, char **argv) { return 1; } std::cerr << "Received " << *status_or - << " bytes: " << std::string(message, *status_or) << std::endl; + << " bytes: " + << std::string(message, static_cast(*status_or)) + << std::endl; } return 0; diff --git a/toolbelt/payload_buffer.cc b/toolbelt/payload_buffer.cc index 0ac6d37..2d4bf5c 100644 --- a/toolbelt/payload_buffer.cc +++ b/toolbelt/payload_buffer.cc @@ -1,22 +1,26 @@ #include "toolbelt/payload_buffer.h" #include +#include #include namespace toolbelt { +using payload_buffer_detail::FitsInU32; +using payload_buffer_detail::ToU32; + static constexpr struct BitmapRunInfo { int num; uint32_t size; } bitmp_run_infos[kNumBitmapRuns] = { - {kRunSize1, kBitmapRunSize1}, - {kRunSize2, kBitmapRunSize2}, - {kRunSize3, kBitmapRunSize3}, - {kRunSize4, kBitmapRunSize4}, + {kRunSize1, static_cast(kBitmapRunSize1)}, + {kRunSize2, static_cast(kBitmapRunSize2)}, + {kRunSize3, static_cast(kBitmapRunSize3)}, + {kRunSize4, static_cast(kBitmapRunSize4)}, }; inline int BitmapRunIndex(uint32_t n) { for (size_t i = 0; i < kNumBitmapRuns; i++) { if (n <= bitmp_run_infos[i].size) { - return i; + return static_cast(i); } } return -1; @@ -30,21 +34,27 @@ inline int BitmapRunIndexFromEncodedSize(uint32_t n) { n &= kBitmapRunSizeMask; for (size_t i = 0; i < kNumBitmapRuns; i++) { if (n <= bitmp_run_infos[i].size) { - return i; + return static_cast(i); } } return -1; } void *PayloadBuffer::AllocateMainMessage(PayloadBuffer **self, size_t size) { - void *msg = Allocate(self, size, 8, true); + if (!FitsInU32(size)) { + return nullptr; + } + void *msg = Allocate(self, ToU32(size), 8, true); (*self)->message = (*self)->ToOffset(msg); return msg; } void PayloadBuffer::AllocateMetadata(PayloadBuffer **self, void *md, size_t size) { - void *m = Allocate(self, size, 1, false); + if (!FitsInU32(size)) { + return; + } + void *m = Allocate(self, ToU32(size), 1, false); memcpy(m, md, size); (*self)->metadata = (*self)->ToOffset(m); } @@ -57,6 +67,11 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, return nullptr; } void *str = nullptr; + if (len > payload_buffer_detail::kMaxU32 - sizeof(uint32_t)) { + return nullptr; + } + const size_t allocation_size = len + sizeof(uint32_t); + const uint32_t allocation_bytes = ToU32(allocation_size); // Load the pointer and convert to address. BufferOffset str_ptr = *hdr; @@ -65,15 +80,15 @@ char *PayloadBuffer::SetString(PayloadBuffer **self, const char *s, size_t len, // If this contains a valid (non-zero) offset, reallocate the // data it points to, otherwise allocate new data. if (old_str != nullptr) { - str = Realloc(self, old_str, len + 4, 4, false); + str = Realloc(self, old_str, allocation_bytes, 4, false); } else { - str = Allocate(self, len + 4, 4, false); + str = Allocate(self, allocation_bytes, 4, false); } if (str == nullptr) { return nullptr; } uint32_t *p = reinterpret_cast(str); - p[0] = uint32_t(len); + p[0] = ToU32(len); memcpy(p + 1, s, len); // The buffer may have moved. Reassign the address of the string @@ -166,6 +181,11 @@ absl::Span PayloadBuffer::AllocateString(PayloadBuffer **self, size_t len, return {}; } void *str = nullptr; + if (len > payload_buffer_detail::kMaxU32 - sizeof(uint32_t)) { + return {}; + } + const size_t allocation_size = len + sizeof(uint32_t); + const uint32_t allocation_bytes = ToU32(allocation_size); // Load the pointer and convert to address. BufferOffset str_ptr = *hdr; @@ -174,15 +194,15 @@ absl::Span PayloadBuffer::AllocateString(PayloadBuffer **self, size_t len, // If this contains a valid (non-zero) offset, reallocate the // data it points to, otherwise allocate new data. if (old_str != nullptr) { - str = Realloc(self, old_str, len + 4, 4, clear); + str = Realloc(self, old_str, allocation_bytes, 4, clear); } else { - str = Allocate(self, len + 4, 4, clear); + str = Allocate(self, allocation_bytes, 4, clear); } if (str == nullptr) { return {}; } uint32_t *p = reinterpret_cast(str); - p[0] = uint32_t(len); + p[0] = ToU32(len); // The buffer may have moved. Reassign the address of the string // back into the header. @@ -247,7 +267,8 @@ void PayloadBuffer::InitFreeList() { header_size += sizeof(Resizer *); } FreeBlockHeader *f = reinterpret_cast(end_of_header); - f->length = full_size - header_size; + assert(header_size <= full_size); + f->length = full_size - ToU32(header_size); f->next = 0; free_list = ToOffset(f); hwm = free_list; @@ -259,8 +280,8 @@ uint32_t PayloadBuffer::TakeStartOfFreeBlock(FreeBlockHeader *block, FreeBlockHeader *prev) { uint32_t rem = block->length - length; if (rem >= sizeof(FreeBlockHeader)) { - FreeBlockHeader *next = - reinterpret_cast(uintptr_t(block) + length); + FreeBlockHeader *next = reinterpret_cast( + reinterpret_cast(block) + length); next->length = rem; next->next = block->next; // Remove from free list. @@ -304,8 +325,15 @@ void *PayloadBuffer::Allocate(PayloadBuffer **buffer, uint32_t n, return AllocateSmallBlock(buffer, n, small_block_index, clear); } } + if (n > payload_buffer_detail::kMaxU32 - 7U) { + return nullptr; + } n = AlignSize(n, 8); // Aligned. - size_t full_length = n + sizeof(uint64_t); + const size_t full_length = n + sizeof(uint64_t); + if (!FitsInU32(full_length)) { + return nullptr; + } + const uint32_t full_length_u32 = ToU32(full_length); FreeBlockHeader *free_block = (*buffer)->FreeList(); FreeBlockHeader *prev = nullptr; for (;;) { @@ -316,17 +344,25 @@ void *PayloadBuffer::Allocate(PayloadBuffer **buffer, uint32_t n, // Really out of memory. return nullptr; } - size_t old_size = (*buffer)->full_size; - size_t new_size = old_size * 2; + const size_t old_size = (*buffer)->full_size; + if (old_size >= payload_buffer_detail::kMaxU32) { + return nullptr; + } + size_t new_size = + old_size > payload_buffer_detail::kMaxU32 / 2 + ? payload_buffer_detail::kMaxU32 + : old_size * 2; while (new_size < full_length) { - new_size *= 2; + new_size = new_size > payload_buffer_detail::kMaxU32 / 2 + ? payload_buffer_detail::kMaxU32 + : new_size * 2; } // Call the resizer. This will move *buffer. (*resizer)(buffer, old_size, new_size); // Set the new size in the newly allocated bigger buffer. - (*buffer)->full_size = new_size; + (*buffer)->full_size = ToU32(new_size); // OK, so now we have to find the end of the free list in the new block. // The old pointers refer to the deallocated memory. @@ -340,14 +376,15 @@ void *PayloadBuffer::Allocate(PayloadBuffer **buffer, uint32_t n, // 'prev' is either nullptr, which means we had no free list, or points // to the last free block header in the new buffer. // Expand the free list to include the new memory. - char *start_of_new_memory = reinterpret_cast(*buffer) + old_size; + char *start_of_new_memory = + reinterpret_cast(*buffer) + static_cast(old_size); bool free_list_expanded = false; if (prev != nullptr) { char *end_of_free_list = reinterpret_cast(prev) + prev->length; if (start_of_new_memory == end_of_free_list) { // Last free block is right at the end of the memory, so edxpand it // include the new memory. This is likely to be true. - prev->length += new_size - old_size; + prev->length += ToU32(new_size - old_size); free_list_expanded = true; } } @@ -357,7 +394,7 @@ void *PayloadBuffer::Allocate(PayloadBuffer **buffer, uint32_t n, FreeBlockHeader *new_block = reinterpret_cast(start_of_new_memory); new_block->next = 0; - new_block->length = new_size - old_size; + new_block->length = ToU32(new_size - old_size); if (prev == nullptr) { (*buffer)->free_list = (*buffer)->ToOffset(new_block); } else { @@ -366,17 +403,18 @@ void *PayloadBuffer::Allocate(PayloadBuffer **buffer, uint32_t n, } return Allocate(buffer, n, clear); } - if (free_block->length >= full_length) { + if (free_block->length >= full_length_u32) { // Free block is big enough. If there's enough room for the free block // header, take the lower part of the free block and keep the remainder // in the free list. - n = (*buffer)->TakeStartOfFreeBlock(free_block, n, full_length, prev); - uint64_t *newblock = (uint64_t *)free_block; // Start of new block. - *newblock = n; // Size of allocated block. + n = (*buffer)->TakeStartOfFreeBlock(free_block, n, full_length_u32, prev); + const uint64_t block_size = n; + std::memcpy(free_block, &block_size, sizeof(block_size)); void *addr = - reinterpret_cast(uintptr_t(free_block) + sizeof(uint64_t)); + reinterpret_cast(reinterpret_cast(free_block) + + sizeof(uint64_t)); if (clear) { - memset(addr, 0, full_length - sizeof(uint64_t)); + memset(addr, 0, full_length_u32 - sizeof(uint64_t)); } return addr; } @@ -390,8 +428,18 @@ std::vector PayloadBuffer::AllocateMany(PayloadBuffer **buffer, bool clear) { // Calculate space for the whole block. This is n*aligned(size) + // n*sizeof(uint32_t). - size_t full_length = n * (AlignSize(size, 8) + sizeof(uint64_t)); - void *start = Allocate(buffer, full_length, 8, clear); + if (size > payload_buffer_detail::kMaxU32 - 7U) { + return {}; + } + const size_t item_length = AlignSize(size, 8) + sizeof(uint64_t); + if (!payload_buffer_detail::ByteCountFitsInU32(n, item_length)) { + return {}; + } + const size_t full_length = n * item_length; + if (!FitsInU32(full_length)) { + return {}; + } + void *start = Allocate(buffer, ToU32(full_length), 8, clear); if (start == nullptr) { return {}; // No memory. } @@ -417,14 +465,16 @@ void PayloadBuffer::MergeWithAboveIfPossible(FreeBlockHeader *alloc_block, FreeBlockHeader *free_block, BufferOffset *next_ptr, size_t alloc_length) { - uintptr_t alloc_addr = (uintptr_t)alloc_block; - uintptr_t free_addr = (uintptr_t)free_block; + const uintptr_t alloc_addr = + reinterpret_cast(alloc_block); + const uintptr_t free_addr = reinterpret_cast(free_block); if (alloc_addr + alloc_length == free_addr) { // Merge with block above. alloc_header->next = free_block->next; - alloc_header->length = + const size_t merged_length = alloc_length + sizeof(uint64_t) + free_block->length; + alloc_header->length = ToU32(merged_length); *next_ptr = ToOffset(alloc_header); } else { // Not adjacent to above; add to free list. @@ -437,8 +487,8 @@ void PayloadBuffer::MergeWithAboveIfPossible(FreeBlockHeader *alloc_block, static bool MergeWithBelowIfPossible(FreeBlockHeader *free_block, FreeBlockHeader *prev) { - uintptr_t prev_addr = (uintptr_t)prev; - if (prev_addr + prev->length == (uintptr_t)free_block) { + const uintptr_t prev_addr = reinterpret_cast(prev); + if (prev_addr + prev->length == reinterpret_cast(free_block)) { // Lower block is adjacent. prev->next = free_block->next; prev->length += free_block->length; @@ -464,28 +514,34 @@ void PayloadBuffer::Free(void *p) { return; } // An allocated block has its length immediately before its address. - uint64_t alloc_length = - *(reinterpret_cast(p) - 1); // Length of allocated block. - int small_block_index = - BitmapsEnabled() ? BitmapRunIndexFromEncodedSize(alloc_length) : -1; + uint64_t alloc_length = 0; + std::memcpy(&alloc_length, + reinterpret_cast(p) - sizeof(uint64_t), + sizeof(alloc_length)); + const int small_block_index = + BitmapsEnabled() + ? BitmapRunIndexFromEncodedSize(static_cast(alloc_length)) + : -1; if (small_block_index >= 0) { - int bitnum = (alloc_length >> kBitmpRunBitNumShift) & kBitmapRunBitNumMask; - int bitmap_index = - (alloc_length >> kBitmapRunBitMapShift) & kBitmapRunBitMapMask; + const int bitnum = static_cast( + (alloc_length >> kBitmpRunBitNumShift) & kBitmapRunBitNumMask); + const int bitmap_index = static_cast( + (alloc_length >> kBitmapRunBitMapShift) & kBitmapRunBitMapMask); FreeSmallBlock(this, small_block_index, bitmap_index, bitnum); return; } // Point to real start of allocated block. - FreeBlockHeader *alloc_header = - reinterpret_cast(uintptr_t(p) - sizeof(uint64_t)); + FreeBlockHeader *alloc_header = reinterpret_cast( + reinterpret_cast(p) - sizeof(uint64_t)); // Insert into free list by searching for the appropriate point in memory // sorted by address. FreeBlockHeader *free_block = FreeList(); if (free_block == nullptr) { // No free list, this block becomes the only block. - alloc_header->length = alloc_length + sizeof(uint64_t); + alloc_header->length = + ToU32(alloc_length + static_cast(sizeof(uint64_t))); alloc_header->next = 0; free_list = ToOffset(alloc_header); return; @@ -555,8 +611,8 @@ void PayloadBuffer::ExpandIntoFreeBlockAbove( // The free block has enough space. *len_ptr = new_length; - FreeBlockHeader *new_block = - reinterpret_cast(uintptr_t(free_block) + len_diff); + FreeBlockHeader *new_block = reinterpret_cast( + reinterpret_cast(free_block) + len_diff); new_block->length = free_remaining; new_block->next = ToOffset(next); *next_ptr = ToOffset(new_block); @@ -569,10 +625,8 @@ void PayloadBuffer::ExpandIntoFreeBlockAbove( uint64_t *PayloadBuffer::MergeWithFreeBlockBelow( void *alloc_block, FreeBlockHeader *prev, FreeBlockHeader *free_block, uint32_t new_length, uint32_t orig_length, bool clear) { - uintptr_t free_addr = (uintptr_t)free_block; - BufferOffset *next_ptr; - if (prev == NULL) { + if (prev == nullptr) { next_ptr = &free_list; } else { next_ptr = &prev->next; @@ -581,7 +635,7 @@ uint64_t *PayloadBuffer::MergeWithFreeBlockBelow( // the combined free block and block being reallocated. FreeBlockHeader *next = ToAddress(free_block->next); FreeBlockHeader *newb = reinterpret_cast( - free_addr + new_length + sizeof(uint64_t)); + reinterpret_cast(free_block) + new_length + sizeof(uint64_t)); newb->length = free_block->length + orig_length - new_length; newb->next = ToOffset(next); *next_ptr = ToOffset(newb); @@ -590,8 +644,8 @@ uint64_t *PayloadBuffer::MergeWithFreeBlockBelow( *len_ptr = new_length; memmove(len_ptr + 1, alloc_block, orig_length); if (clear) { - memset(reinterpret_cast(len_ptr) + sizeof(uint64_t) + orig_length, 0, - new_length - orig_length); + memset(reinterpret_cast(len_ptr) + sizeof(uint64_t) + orig_length, + 0, new_length - orig_length); } return len_ptr + 1; } @@ -599,28 +653,31 @@ uint64_t *PayloadBuffer::MergeWithFreeBlockBelow( void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, bool clear, bool enable_small_block) { - if (p == NULL) { + if (p == nullptr) { // No block to realloc, just call malloc. return Allocate(buffer, n, clear); } // The allocated block has its length immediately prior to its address. uint64_t *len_ptr = reinterpret_cast(p) - 1; - uint64_t orig_length = *len_ptr; + const uint64_t orig_length = *len_ptr; if (enable_small_block && (*buffer)->BitmapsEnabled()) { - int small_block_index = BitmapRunIndexFromEncodedSize(orig_length); + const int small_block_index = + BitmapRunIndexFromEncodedSize(static_cast(orig_length)); if (small_block_index >= 0) { - int decoded_length = - (orig_length >> kBitmapRunSizeShift) & kBitmapRunSizeMask; + const int decoded_length = static_cast( + (orig_length >> kBitmapRunSizeShift) & kBitmapRunSizeMask); // If the new size is in the same small block index we can just return the // original block. if (BitmapRunIndex(n) == small_block_index) { - int bitnum = - (orig_length >> kBitmpRunBitNumShift) & kBitmapRunBitNumMask; - int bitmap_index = - (orig_length >> kBitmapRunBitMapShift) & kBitmapRunBitMapMask; - int encoded_size = (1U << 31) | (bitmap_index << kBitmapRunBitMapShift) | - (bitnum << kBitmpRunBitNumShift) | - (n & kBitmapRunSizeMask); + const int bitnum = static_cast( + (orig_length >> kBitmpRunBitNumShift) & kBitmapRunBitNumMask); + const int bitmap_index = static_cast( + (orig_length >> kBitmapRunBitMapShift) & kBitmapRunBitMapMask); + const uint64_t encoded_size = + (1ULL << 31) | + (static_cast(bitmap_index) << kBitmapRunBitMapShift) | + (static_cast(bitnum) << kBitmpRunBitNumShift) | + (static_cast(n) & kBitmapRunSizeMask); *len_ptr = encoded_size; if (clear && n > static_cast(decoded_length)) { @@ -631,26 +688,29 @@ void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, } // Need to free the old block and allocate a new one as the small block // index is different. - BufferOffset p_offset = (*buffer)->ToOffset(p); + const BufferOffset p_offset = (*buffer)->ToOffset(p); void *newp = Allocate(buffer, n, false, enable_small_block); - if (newp == NULL) { - return NULL; + if (newp == nullptr) { + return nullptr; } // Re-derive p since Allocate may have triggered a buffer resize. p = (*buffer)->ToAddress(p_offset); - memcpy(newp, p, decoded_length); + memcpy(newp, p, static_cast(decoded_length)); if (clear && n > static_cast(decoded_length)) { memset(reinterpret_cast(newp) + decoded_length, 0, - n - decoded_length); + n - static_cast(decoded_length)); } (*buffer)->Free(p); return newp; } } - FreeBlockHeader *alloc_block = - reinterpret_cast(uintptr_t(p) - sizeof(uint64_t)); - uintptr_t alloc_addr = (uintptr_t)p; + FreeBlockHeader *alloc_block = reinterpret_cast( + reinterpret_cast(p) - sizeof(uint64_t)); + const uintptr_t alloc_addr = reinterpret_cast(p); + if (n > payload_buffer_detail::kMaxU32 - 7U) { + return nullptr; + } n = AlignSize(n); // Aligned. if (n == orig_length) { // Same size as current block, nothing to do. @@ -658,29 +718,29 @@ void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, } if (n < orig_length) { // Decreasing in size. Free the remaining part. - (*buffer)->ShrinkBlock(alloc_block, orig_length, n, len_ptr); + (*buffer)->ShrinkBlock(alloc_block, ToU32(orig_length), n, len_ptr); return p; } // Increasing in size. // See if there's a free block immediately following allocated block. FreeBlockHeader *free_block = (*buffer)->FreeList(); - FreeBlockHeader *prev = NULL; - FreeBlockHeader *prev_prev = NULL; - while (free_block != NULL) { + FreeBlockHeader *prev = nullptr; + FreeBlockHeader *prev_prev = nullptr; + while (free_block != nullptr) { BufferOffset *next_ptr; - if (prev == NULL) { + if (prev == nullptr) { next_ptr = &(*buffer)->free_list; } else { next_ptr = &prev->next; } if (free_block > alloc_block) { - uintptr_t free_addr = (uintptr_t)free_block; - int diff = n - orig_length; + const uintptr_t free_addr = reinterpret_cast(free_block); + const uint32_t diff = n - ToU32(orig_length); if (alloc_addr + orig_length == free_addr) { // There is a free block above. See if has enough space. - if (free_block->length > static_cast(diff)) { - uint32_t freelen = free_block->length - static_cast(diff); + if (free_block->length > diff) { + const uint32_t freelen = free_block->length - diff; if (freelen > sizeof(FreeBlockHeader)) { (*buffer)->ExpandIntoFreeBlockAbove(free_block, n, diff, freelen, len_ptr, next_ptr, clear); @@ -689,15 +749,16 @@ void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, } } // Check for free block adjacent below. - if (prev != NULL) { - uintptr_t prev_addr = (uintptr_t)prev; - if (prev_addr + prev->length == (uintptr_t)alloc_block && - prev->length >= static_cast(diff)) { + if (prev != nullptr) { + const uintptr_t prev_addr = reinterpret_cast(prev); + if (prev_addr + prev->length == + reinterpret_cast(alloc_block) && + prev->length >= diff) { // Previous free block is adjacent and has enough space in it. // Use start of new block as new address and place FreeBlockHeader // at newly free part. return (*buffer)->MergeWithFreeBlockBelow(p, prev_prev, prev, n, - orig_length, clear); + ToU32(orig_length), clear); } // Block doesn't have enough space. break; @@ -712,16 +773,17 @@ void *PayloadBuffer::Realloc(PayloadBuffer **buffer, void *p, uint32_t n, // one, copy the memory and free the old block. We are guaranteed that // the new block is larger than the original one since if it was smaller // we can always reuse the block. - BufferOffset p_offset = (*buffer)->ToOffset(p); + const BufferOffset p_offset = (*buffer)->ToOffset(p); void *newp = Allocate(buffer, n, false, enable_small_block); - if (newp == NULL) { - return NULL; + if (newp == nullptr) { + return nullptr; } // Re-derive p since Allocate may have triggered a buffer resize. p = (*buffer)->ToAddress(p_offset); - memcpy(newp, p, orig_length); + memcpy(newp, p, static_cast(orig_length)); if (clear) { - memset(reinterpret_cast(newp) + orig_length, 0, n - orig_length); + memset(reinterpret_cast(newp) + orig_length, 0, + n - ToU32(orig_length)); } (*buffer)->Free(p); return newp; @@ -758,15 +820,19 @@ static bool InitializeBitMapRunVector(PayloadBuffer **self, int index, } bool PayloadBuffer::PrimeBitmapAllocator(PayloadBuffer **self, size_t size) { - int index = BitmapRunIndex(size); + if (!FitsInU32(size)) { + return false; + } + const int index = BitmapRunIndex(ToU32(size)); if (index < 0) { return true; } if ((*self)->bitmaps[index] != 0) { return true; } - return InitializeBitMapRunVector(self, index, bitmp_run_infos[index].size, - bitmp_run_infos[index].num); + return InitializeBitMapRunVector( + self, index, bitmp_run_infos[index].size, + static_cast(bitmp_run_infos[index].num)); } BufferOffset PayloadBuffer::AllocateBitMapRunVector(PayloadBuffer **self) { @@ -798,10 +864,10 @@ BitMapRun *PayloadBuffer::AllocateBitMapRun(PayloadBuffer **self, uint32_t size, if (run == nullptr) { return nullptr; } - run->size = size; - run->num = num; + run->size = static_cast(size); + run->num = static_cast(num); run->bits = 0; - run->free = num; // All blocks are free. + run->free = static_cast(num); // All blocks are free. return run; } @@ -809,54 +875,60 @@ void *BitMapRun::Allocate(PayloadBuffer **pb, int index, uint32_t, int size, int num, bool clear) { // Lazy init of vector. if ((*pb)->bitmaps[index] == 0) { - if (!InitializeBitMapRunVector(pb, index, size, num)) { + if (!InitializeBitMapRunVector(pb, index, static_cast(size), + static_cast(num))) { return nullptr; } } for (;;) { // Re-derive hdr each iteration since allocations below may trigger a // buffer resize (realloc), invalidating any previous pointer. - VectorHeader *hdr = (*pb)->ToAddress((*pb)->bitmaps[index]); + VectorHeader *hdr = + (*pb)->ToAddress((*pb)->bitmaps[static_cast(index)]); // Go backwards through the elements as that is most likely to find a free // bit. - for (int i = hdr->num_elements - 1; i >= 0; i--) { - BitMapRun *run = - (*pb)->ToAddress((*pb)->VectorGet(hdr, i)); + for (int i = static_cast(hdr->num_elements) - 1; i >= 0; i--) { + BitMapRun *run = (*pb)->ToAddress( + (*pb)->VectorGet(hdr, static_cast(i))); if (run->free == 0) { continue; } // Fast path: there is a free bit in the run. - int bit = ffs(~run->bits); + int bit = ffs(static_cast(~run->bits)); assert(bit > 0 && bit <= run->num); bit--; // Convert to 0-based index. - run->bits |= 1U << bit; + run->bits |= 1U << static_cast(bit); run->free--; // The address of the block is after the header and indexed by the bit // number times the size of the block plus 8 bytes for the length. Then // we need the address after the length word. void *addr = reinterpret_cast(run) + sizeof(BitMapRun) + - bit * (run->size + 8) + 8; - // Write the encoded size of the block into the preceding 4 bytes. + static_cast(bit) * (run->size + 8) + 8; + // Write the encoded size of the block into the preceding 8 bytes. uint64_t *p = reinterpret_cast(addr) - 1; // Encode the length. - uint64_t encoded_size = (1U << 31) | (i << kBitmapRunBitMapShift) | - (bit << kBitmpRunBitNumShift) | - (size & kBitmapRunSizeMask); + const uint64_t encoded_size = + (1ULL << 31) | + (static_cast(i) << kBitmapRunBitMapShift) | + (static_cast(bit) << kBitmpRunBitNumShift) | + (static_cast(size) & kBitmapRunSizeMask); *p = encoded_size; if (clear) { - memset(addr, 0, size); + memset(addr, 0, static_cast(size)); } return addr; } // Slow path, no free bits in any run. We need to allocate a new run. - BitMapRun *run = PayloadBuffer::AllocateBitMapRun(pb, size, num); + BitMapRun *run = + PayloadBuffer::AllocateBitMapRun(pb, static_cast(size), + static_cast(num)); if (run == nullptr) { return nullptr; } // Re-derive hdr since AllocateBitMapRun may have triggered a buffer // resize, invalidating the previous pointer. - hdr = (*pb)->ToAddress((*pb)->bitmaps[index]); + hdr = (*pb)->ToAddress((*pb)->bitmaps[static_cast(index)]); BufferOffset run_offset = (*pb)->ToOffset(run); if (!(*pb)->VectorPush(pb, hdr, run_offset, false)) { (*pb)->Free((*pb)->ToAddress(run_offset)); @@ -870,18 +942,21 @@ void BitMapRun::Free(PayloadBuffer *pb, int index, int bitmap_index, // This is always fast path since we have all the information we need // to free the block. We basically just clear a bit and increment the // free count. - VectorHeader *hdr = pb->ToAddress(pb->bitmaps[index]); + VectorHeader *hdr = + pb->ToAddress(pb->bitmaps[static_cast(index)]); assert(hdr != nullptr); - BitMapRun *run = - pb->ToAddress(pb->VectorGet(hdr, bitmap_index)); - run->bits &= ~(1U << bitnum); + BitMapRun *run = pb->ToAddress( + pb->VectorGet(hdr, static_cast(bitmap_index))); + run->bits &= ~(1U << static_cast(bitnum)); run->free++; } void *PayloadBuffer::AllocateSmallBlock(PayloadBuffer **pb, uint32_t size, int index, bool clear) { - return BitMapRun::Allocate(pb, index, size, bitmp_run_infos[index].size, - bitmp_run_infos[index].num, clear); + return BitMapRun::Allocate( + pb, index, size, + static_cast(bitmp_run_infos[static_cast(index)].size), + bitmp_run_infos[static_cast(index)].num, clear); } void PayloadBuffer::FreeSmallBlock(PayloadBuffer *pb, int index, diff --git a/toolbelt/payload_buffer.h b/toolbelt/payload_buffer.h index f112a86..55f1668 100644 --- a/toolbelt/payload_buffer.h +++ b/toolbelt/payload_buffer.h @@ -2,8 +2,10 @@ #include "absl/types/span.h" #include "toolbelt/hexdump.h" +#include #include #include +#include #include #include #include @@ -13,6 +15,30 @@ namespace toolbelt { +namespace payload_buffer_detail { + +inline constexpr uint32_t kMaxU32 = std::numeric_limits::max(); + +inline bool FitsInU32(size_t value) { return value <= kMaxU32; } + +inline bool FitsInU32(uint64_t value) { return value <= kMaxU32; } + +inline bool ByteCountFitsInU32(size_t count, size_t element_size) { + return element_size == 0 || count <= kMaxU32 / element_size; +} + +inline uint32_t ToU32(size_t value) { + assert(FitsInU32(value)); + return static_cast(value); +} + +inline uint32_t ToU32(uint64_t value) { + assert(FitsInU32(value)); + return static_cast(value); +} + +} // namespace payload_buffer_detail + constexpr uint32_t kFixedBufferMagic = 0xe5f6f1c4; constexpr uint32_t kMovableBufferMagic = 0xc5f6f1c4; @@ -540,10 +566,19 @@ inline bool PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, // by the block size (in bytes). BufferOffset hdr_offset = (*self)->ToOffset(hdr); - uint32_t total_size = hdr->num_elements * sizeof(T); + if (!payload_buffer_detail::ByteCountFitsInU32(hdr->num_elements, + sizeof(T))) { + return false; + } + const size_t total_size = static_cast(hdr->num_elements) * sizeof(T); if (hdr->data == 0) { // The vector is empty, allocate it with a default size of 2. - void *vecp = Allocate(self, 2 * sizeof(T), true, enable_small_block); + const size_t initial_bytes = 2 * sizeof(T); + if (!payload_buffer_detail::FitsInU32(initial_bytes)) { + return false; + } + void *vecp = Allocate(self, payload_buffer_detail::ToU32(initial_bytes), true, + enable_small_block); if (vecp == nullptr) { return false; } @@ -554,11 +589,15 @@ inline bool PayloadBuffer::VectorPush(PayloadBuffer **self, VectorHeader *hdr, // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodedSize(block); - if (current_size == total_size) { + const uint32_t current_size = DecodedSize(block); + if (current_size == payload_buffer_detail::ToU32(total_size)) { // Need to double the size of the memory. - void *vecp = Realloc(self, block, 2 * hdr->num_elements * sizeof(T), true, - enable_small_block); + if (total_size > payload_buffer_detail::kMaxU32 / 2) { + return false; + } + const size_t doubled_bytes = total_size * 2; + void *vecp = Realloc(self, block, payload_buffer_detail::ToU32(doubled_bytes), + true, enable_small_block); if (vecp == nullptr) { return false; } @@ -584,8 +623,13 @@ inline bool PayloadBuffer::VectorReserve(PayloadBuffer **self, return true; } BufferOffset hdr_offset = (*self)->ToOffset(hdr); + if (!payload_buffer_detail::ByteCountFitsInU32(n, sizeof(T))) { + return false; + } + const size_t reserve_bytes = n * sizeof(T); if (hdr->data == 0) { - void *vecp = Allocate(self, n * sizeof(T), false, enable_small_block); + void *vecp = Allocate(self, payload_buffer_detail::ToU32(reserve_bytes), + false, enable_small_block); if (vecp == nullptr) { return false; } @@ -595,11 +639,12 @@ inline bool PayloadBuffer::VectorReserve(PayloadBuffer **self, // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodedSize(block); - if (current_size < n * sizeof(T)) { + const uint32_t current_size = DecodedSize(block); + if (current_size < payload_buffer_detail::ToU32(reserve_bytes)) { // Need to expand the memory to the size given. void *vecp = - Realloc(self, block, n * sizeof(T), false, enable_small_block); + Realloc(self, block, payload_buffer_detail::ToU32(reserve_bytes), + false, enable_small_block); if (vecp == nullptr) { return false; } @@ -617,9 +662,17 @@ inline bool PayloadBuffer::VectorResize(PayloadBuffer **self, VectorHeader *hdr, hdr->num_elements = 0; return true; } + if (!payload_buffer_detail::FitsInU32(n)) { + return false; + } + const uint32_t element_count = payload_buffer_detail::ToU32(n); + if (!payload_buffer_detail::ByteCountFitsInU32(n, sizeof(T))) { + return false; + } + const size_t resize_bytes = n * sizeof(T); BufferOffset hdr_offset = (*self)->ToOffset(hdr); if (hdr->data == 0) { - void *vecp = Allocate(self, n * sizeof(T)); + void *vecp = Allocate(self, payload_buffer_detail::ToU32(resize_bytes)); if (vecp == nullptr) { return false; } @@ -630,10 +683,11 @@ inline bool PayloadBuffer::VectorResize(PayloadBuffer **self, VectorHeader *hdr, // Vector has some values in it. Retrieve the total size from // the allocated block header (before the start of the memory) uint32_t *block = (*self)->ToAddress(hdr->data); - uint32_t current_size = DecodedSize(block); - if (current_size < n * sizeof(T)) { + const uint32_t current_size = DecodedSize(block); + if (current_size < payload_buffer_detail::ToU32(resize_bytes)) { // Need to expand the memory to the size given. - void *vecp = Realloc(self, block, n * sizeof(T), 8); + void *vecp = + Realloc(self, block, payload_buffer_detail::ToU32(resize_bytes), 8); if (vecp == nullptr) { return false; } @@ -642,7 +696,7 @@ inline bool PayloadBuffer::VectorResize(PayloadBuffer **self, VectorHeader *hdr, hdr = new_hdr; } } - hdr->num_elements = n; + hdr->num_elements = element_count; return true; } @@ -661,6 +715,9 @@ inline T PayloadBuffer::VectorGet(const VectorHeader *hdr, size_t index) const { if (index >= hdr->num_elements) { return static_cast(0); } + if (!payload_buffer_detail::ByteCountFitsInU32(index + 1, sizeof(T))) { + return static_cast(0); + } const T *addr = ToAddress(hdr->data, (index + 1) * sizeof(T)); if (addr == nullptr) { return static_cast(0); diff --git a/toolbelt/payload_buffer_test.cc b/toolbelt/payload_buffer_test.cc index 2c8aa2e..3833f19 100644 --- a/toolbelt/payload_buffer_test.cc +++ b/toolbelt/payload_buffer_test.cc @@ -2,6 +2,7 @@ #include "toolbelt/hexdump.h" #include "toolbelt/payload_buffer.h" #include +#include #include #include #include @@ -12,7 +13,7 @@ using VectorHeader = toolbelt::VectorHeader; using Resizer = toolbelt::Resizer; TEST(BufferTest, Simple) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); pb->Dump(std::cout); toolbelt::Hexdump(pb, 64); @@ -26,7 +27,7 @@ TEST(BufferTest, Simple) { } TEST(BufferTest, TwoAllocs) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); pb->Dump(std::cout); toolbelt::Hexdump(pb, 64); @@ -46,7 +47,7 @@ TEST(BufferTest, TwoAllocs) { } TEST(BufferTest, Free) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); pb->Dump(std::cout); toolbelt::Hexdump(pb, 64); @@ -69,7 +70,7 @@ TEST(BufferTest, Free) { } TEST(BufferTest, FreeThenAlloc) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); pb->Dump(std::cout); toolbelt::Hexdump(pb, 64); @@ -96,7 +97,7 @@ TEST(BufferTest, FreeThenAlloc) { } TEST(BufferTest, SmallBlockAllocSimple) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); void *addr = PayloadBuffer::Allocate(&pb, 16); @@ -112,7 +113,7 @@ TEST(BufferTest, SmallBlockAllocSimple) { } TEST(BufferTest, SmallBlockAlloc) { - char *buffer = (char *)calloc(1, 8192); + char *buffer = static_cast(calloc(1, 8192)); PayloadBuffer *pb = new (buffer) PayloadBuffer(8192); // Small block sizes are 16, 32, 64 and 128. @@ -147,15 +148,15 @@ TEST(BufferTest, SmallBlockAlloc) { } TEST(BufferTest, SmallBlockAllocFree) { - char *buffer = (char *)calloc(1, 8192); + char *buffer = static_cast(calloc(1, 8192)); PayloadBuffer *pb = new (buffer) PayloadBuffer(8192); // Do a mix of sizes and free them. std::vector blocks; std::vector sizes = {10, 30, 50, 100, 150}; for (int i = 0; i < 50; i++) { - size_t size = sizes[i % sizes.size()]; - void *addr = PayloadBuffer::Allocate(&pb, size); + size_t size = sizes[static_cast(i) % sizes.size()]; + void *addr = PayloadBuffer::Allocate(&pb, static_cast(size)); memset(addr, 0xda, size); blocks.push_back(addr); } @@ -168,8 +169,8 @@ TEST(BufferTest, SmallBlockAllocFree) { // Now allocate every 5th block again. for (size_t i = 0; i < blocks.size(); i++) { if (i % 5 == 0) { - size_t size = sizes[i % sizes.size()]; - void *addr = PayloadBuffer::Allocate(&pb, size); + size_t size = sizes[static_cast(i) % sizes.size()]; + void *addr = PayloadBuffer::Allocate(&pb, static_cast(size)); memset(addr, 0xda, size); blocks[i] = addr; } @@ -185,7 +186,7 @@ TEST(BufferTest, PrimeBitmapAllocatorReserveFailureReclaimsVectorHeader) { constexpr size_t kReserveAllocationSize = 8 * sizeof(BufferOffset) + sizeof(uint64_t); constexpr size_t kSize = sizeof(PayloadBuffer) + kReserveAllocationSize; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); const BufferOffset initial_free_list = pb->free_list; @@ -214,7 +215,7 @@ TEST(BufferTest, PrimeBitmapAllocatorRunFailureRollsBackInitialization) { constexpr size_t kSize = sizeof(PayloadBuffer) + kVectorHeaderAllocationSize + kReserveAllocationSize + sizeof(toolbelt::FreeBlockHeader); - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); const BufferOffset initial_free_list = pb->free_list; @@ -245,7 +246,7 @@ TEST(BufferTest, LazyBitmapAllocatorRunFailureRollsBackInitialization) { constexpr size_t kSize = sizeof(PayloadBuffer) + kVectorHeaderAllocationSize + kReserveAllocationSize + sizeof(toolbelt::FreeBlockHeader); - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); const BufferOffset initial_free_list = pb->free_list; @@ -270,7 +271,7 @@ TEST(BufferTest, LazyBitmapAllocatorRunFailureRollsBackInitialization) { TEST(BufferTest, BitmapRunGrowthFailureReclaimsUnappendedRun) { constexpr size_t kSize = 8192; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); ASSERT_TRUE( @@ -319,7 +320,8 @@ TEST(BufferTest, BitmapRunGrowthFailureReclaimsUnappendedRun) { const size_t drain_size = free_block->length - run_allocation_size - sizeof(uint64_t); ASSERT_EQ(0u, drain_size % sizeof(uint64_t)); - ASSERT_NE(nullptr, PayloadBuffer::Allocate(&pb, drain_size, false, false)); + ASSERT_NE(nullptr, PayloadBuffer::Allocate(&pb, static_cast(drain_size), + false, false)); const BufferOffset initial_free_list = pb->free_list; free_block = pb->FreeList(); @@ -357,7 +359,7 @@ TEST(BufferTest, BestCasePerformance) { constexpr int kIterations = 10000; for (int iter = 0; iter < kIterations; iter++) { - char *buffer = (char *)calloc(1, kSize); + char *buffer = static_cast(calloc(1, kSize)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); ASSERT_TRUE(PayloadBuffer::PrimeBitmapAllocator(&pb, 16)); @@ -402,7 +404,7 @@ TEST(BufferTest, BestCasePerformance) { // New buffer. free(buffer); - buffer = (char *)calloc(1, kSize); + buffer = static_cast(calloc(1, kSize)); pb = new (buffer) PayloadBuffer(kSize); // Now allocate by disabling the small block allocator. @@ -466,11 +468,11 @@ TEST(BufferTest, TypicalPerformance) { std::vector sizes; // Random sizes up to 128 for (int i = 0; i < kNumBlocks; i++) { - sizes.push_back((rand() % 127) + 1); + sizes.push_back(static_cast((rand() % 127) + 1)); } for (int iter = 0; iter < kIterations; iter++) { - char *buffer = (char *)calloc(1, kSize); + char *buffer = static_cast(calloc(1, kSize)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); // No priming the small block allocator for this test. It probably won't @@ -480,13 +482,13 @@ TEST(BufferTest, TypicalPerformance) { std::vector small_blocks; uint64_t small_start = toolbelt::Now(); for (int j = 0; j < 1000; j++) { - int prev_size = int(small_blocks.size()); + const int prev_size = static_cast(small_blocks.size()); for (int i = 0; i < kNumBlocks; i++) { void *addr = PayloadBuffer::Allocate(&pb, 10, false); small_blocks.push_back(addr); } // Free some of the blocks. - for (size_t i = prev_size; i < small_blocks.size(); i++) { + for (size_t i = static_cast(prev_size); i < small_blocks.size(); i++) { if (i % 8 == 0) { continue; } @@ -500,21 +502,21 @@ TEST(BufferTest, TypicalPerformance) { // New buffer. free(buffer); - buffer = (char *)calloc(1, kSize); + buffer = static_cast(calloc(1, kSize)); pb = new (buffer) PayloadBuffer(kSize); // Switch off small block alloctor. std::vector large_blocks; uint64_t large_start = toolbelt::Now(); for (int j = 0; j < 1000; j++) { - int prev_size = int(large_blocks.size()); + const int prev_size = static_cast(large_blocks.size()); for (int i = 0; i < kNumBlocks; i++) { void *addr = PayloadBuffer::Allocate(&pb, 10, false, /*enable_small_block=*/false); large_blocks.push_back(addr); } // Free some of the blocks. - for (size_t i = prev_size; i < large_blocks.size(); i++) { + for (size_t i = static_cast(prev_size); i < large_blocks.size(); i++) { if (i % 8 == 0) { continue; } @@ -536,11 +538,11 @@ TEST(BufferTest, TypicalPerformance) { TEST(BufferTest, Many) { constexpr size_t kSize = 8192; - char *buffer = (char *)calloc(1, kSize); + char *buffer = static_cast(calloc(1, kSize)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); std::vector addrs = PayloadBuffer::AllocateMany(&pb, 100, 10, true); - ASSERT_EQ(10, addrs.size()); + ASSERT_EQ(size_t{10}, addrs.size()); // Print the addresses. for (auto addr : addrs) { std::cout << "Allocated " << addr << std::endl; @@ -558,7 +560,7 @@ TEST(BufferTest, Many) { } TEST(BufferTest, String) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); // Allocate space for a message containing an offset for the string. @@ -571,14 +573,14 @@ TEST(BufferTest, String) { BufferOffset offset = pb->ToOffset(addr); char *s = PayloadBuffer::SetString(&pb, std::string("foobar"), offset); - std::cout << "String allocated at " << (void *)s << std::endl; + std::cout << "String allocated at " << static_cast(s) << std::endl; toolbelt::Hexdump(pb, pb->hwm); // Now put in a bigger string, replacing the old one. s = PayloadBuffer::SetString(&pb, std::string("foobar has been replaced"), offset); - std::cout << "New string allocated at " << (void *)s << std::endl; + std::cout << "New string allocated at " << static_cast(s) << std::endl; toolbelt::Hexdump(pb, pb->hwm); @@ -588,7 +590,7 @@ TEST(BufferTest, String) { } TEST(BufferTest, Vector) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); // Allocate space for a message containing the VectorHeader. @@ -604,13 +606,13 @@ TEST(BufferTest, Vector) { toolbelt::Hexdump(pb, pb->hwm); uint32_t v = pb->VectorGet(hdr, 0); - ASSERT_EQ(0x12345678, v); + ASSERT_EQ(uint32_t{0x12345678}, v); free(buffer); } TEST(BufferTest, VectorExpand) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); // Allocate space for a message containing the VectorHeader. @@ -623,21 +625,21 @@ TEST(BufferTest, VectorExpand) { pb->Dump(std::cout); for (int i = 0; i < 3; i++) { - PayloadBuffer::VectorPush(&pb, hdr, i + 1); + PayloadBuffer::VectorPush(&pb, hdr, static_cast(i + 1)); } toolbelt::Hexdump(pb, pb->hwm); pb->Dump(std::cout); for (int i = 0; i < 3; i++) { - uint32_t v = pb->VectorGet(hdr, i); - ASSERT_EQ(i + 1, v); + const uint32_t v = pb->VectorGet(hdr, static_cast(i)); + ASSERT_EQ(static_cast(i + 1), v); } free(buffer); } TEST(BufferTest, VectorExpandMore) { - char *buffer = (char *)calloc(1, 4096); + char *buffer = static_cast(calloc(1, 4096)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); // Allocate space for a message containing the VectorHeader. @@ -650,15 +652,15 @@ TEST(BufferTest, VectorExpandMore) { pb->Dump(std::cout); for (int i = 0; i < 100; i++) { - PayloadBuffer::VectorPush(&pb, hdr, i + 1); - uint32_t v = pb->VectorGet(hdr, i); - ASSERT_EQ(i + 1, v); + PayloadBuffer::VectorPush(&pb, hdr, static_cast(i + 1)); + const uint32_t v = pb->VectorGet(hdr, static_cast(i)); + ASSERT_EQ(static_cast(i + 1), v); } toolbelt::Hexdump(pb, pb->hwm); for (int i = 0; i < 100; i++) { - uint32_t v = pb->VectorGet(hdr, i); - ASSERT_EQ(i + 1, v); + const uint32_t v = pb->VectorGet(hdr, static_cast(i)); + ASSERT_EQ(static_cast(i + 1), v); } pb->Dump(std::cout); @@ -666,21 +668,16 @@ TEST(BufferTest, VectorExpandMore) { } TEST(BufferTest, VectorPushWithResize) { - char *buffer = (char *)calloc(256, 1); + char *buffer = static_cast(calloc(256, 1)); bool resized = false; PayloadBuffer *pb = new (buffer) PayloadBuffer( 256, [&resized, &buffer](PayloadBuffer **p, size_t, size_t new_size) { -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wclass-memaccess" -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif *p = reinterpret_cast(realloc(*p, new_size)); -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif buffer = reinterpret_cast(*p); @@ -693,15 +690,15 @@ TEST(BufferTest, VectorPushWithResize) { constexpr int kCount = 200; for (int i = 0; i < kCount; i++) { VectorHeader *hdr = pb->ToAddress(msg_offset); - PayloadBuffer::VectorPush(&pb, hdr, i + 1); + PayloadBuffer::VectorPush(&pb, hdr, static_cast(i + 1)); } ASSERT_TRUE(resized); VectorHeader *hdr = pb->ToAddress(msg_offset); - ASSERT_EQ(kCount, hdr->num_elements); + ASSERT_EQ(static_cast(kCount), hdr->num_elements); for (int i = 0; i < kCount; i++) { - uint32_t v = pb->VectorGet(hdr, i); - ASSERT_EQ(i + 1, v); + const uint32_t v = pb->VectorGet(hdr, static_cast(i)); + ASSERT_EQ(static_cast(i + 1), v); } pb->~PayloadBuffer(); @@ -709,21 +706,16 @@ TEST(BufferTest, VectorPushWithResize) { } TEST(BufferTest, VectorReserveWithResize) { - char *buffer = (char *)calloc(256, 1); + char *buffer = static_cast(calloc(256, 1)); bool resized = false; PayloadBuffer *pb = new (buffer) PayloadBuffer( 256, [&resized, &buffer](PayloadBuffer **p, size_t, size_t new_size) { -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wclass-memaccess" -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif *p = reinterpret_cast(realloc(*p, new_size)); -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif buffer = reinterpret_cast(*p); @@ -745,21 +737,16 @@ TEST(BufferTest, VectorReserveWithResize) { } TEST(BufferTest, VectorResizeWithResize) { - char *buffer = (char *)calloc(256, 1); + char *buffer = static_cast(calloc(256, 1)); bool resized = false; PayloadBuffer *pb = new (buffer) PayloadBuffer( 256, [&resized, &buffer](PayloadBuffer **p, size_t, size_t new_size) { -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wclass-memaccess" -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif *p = reinterpret_cast(realloc(*p, new_size)); -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif buffer = reinterpret_cast(*p); @@ -782,7 +769,7 @@ TEST(BufferTest, VectorResizeWithResize) { TEST(BufferTest, EmptyVectorZeroSizeOperationsSucceed) { constexpr size_t kSize = 256; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); ASSERT_NE(nullptr, @@ -799,7 +786,7 @@ TEST(BufferTest, EmptyVectorZeroSizeOperationsSucceed) { TEST(BufferTest, VectorPushFixedBufferAllocationFailurePreservesHeader) { constexpr size_t kSize = 256; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); ASSERT_NE(nullptr, @@ -826,7 +813,7 @@ TEST(BufferTest, VectorPushFixedBufferAllocationFailurePreservesHeader) { TEST(BufferTest, VectorPushFixedBufferGrowthFailurePreservesHeader) { constexpr size_t kSize = 256; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); ASSERT_NE(nullptr, @@ -861,7 +848,7 @@ TEST(BufferTest, VectorPushFixedBufferGrowthFailurePreservesHeader) { TEST(BufferTest, VectorReserveFixedBufferFailurePreservesHeader) { constexpr size_t kSize = 256; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); ASSERT_NE(nullptr, @@ -889,7 +876,7 @@ TEST(BufferTest, VectorReserveFixedBufferFailurePreservesHeader) { TEST(BufferTest, VectorResizeFixedBufferFailurePreservesHeader) { constexpr size_t kSize = 256; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, false); ASSERT_NE(nullptr, @@ -920,22 +907,17 @@ TEST(BufferTest, VectorResizeFixedBufferFailurePreservesHeader) { } TEST(BufferTest, Resizeable) { - char *buffer = (char *)calloc(1, 512); + char *buffer = static_cast(calloc(1, 512)); bool resized = false; PayloadBuffer *pb = new (buffer) PayloadBuffer( 256, [&resized](PayloadBuffer **p, size_t, size_t new_size) { std::cout << "resize for " << new_size << std::endl; -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wclass-memaccess" -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif *p = reinterpret_cast(realloc(*p, new_size)); -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif resized = true; @@ -971,7 +953,7 @@ TEST(BufferTest, Resizeable) { TEST(BufferTest, ToAddressRejectsTypedReadPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); EXPECT_EQ(pb->ToAddress(kSize - 2), nullptr); @@ -982,7 +964,7 @@ TEST(BufferTest, ToAddressRejectsTypedReadPastEnd) { TEST(BufferTest, StringHelpersRejectLengthHeaderPastEnd) { constexpr size_t kSize = 4093; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); toolbelt::StringHeader header = static_cast(4092); @@ -995,7 +977,7 @@ TEST(BufferTest, StringHelpersRejectLengthHeaderPastEnd) { TEST(BufferTest, StringSizeRejectsBodyPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); toolbelt::StringHeader header = @@ -1011,7 +993,7 @@ TEST(BufferTest, StringSizeRejectsBodyPastEnd) { TEST(BufferTest, EmptyStringAtBufferTailAccepted) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); toolbelt::StringHeader header = @@ -1028,7 +1010,7 @@ TEST(BufferTest, EmptyStringAtBufferTailAccepted) { } TEST(BufferTest, StringWithinBoundsAcceptsValidString) { - char *buffer = (char *)calloc(4096, 1); + char *buffer = static_cast(calloc(4096, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); PayloadBuffer::AllocateMainMessage(&pb, 32); @@ -1041,7 +1023,7 @@ TEST(BufferTest, StringWithinBoundsAcceptsValidString) { } TEST(BufferTest, StringWithinBoundsAcceptsUnsetString) { - char *buffer = (char *)calloc(4096, 1); + char *buffer = static_cast(calloc(4096, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); PayloadBuffer::AllocateMainMessage(&pb, sizeof(BufferOffset)); @@ -1053,7 +1035,7 @@ TEST(BufferTest, StringWithinBoundsAcceptsUnsetString) { TEST(BufferTest, SetStringFixedBufferFailurePreservesHeader) { constexpr uint32_t kBufferSize = 4096; - char *buffer = (char *)calloc(kBufferSize, 1); + char *buffer = static_cast(calloc(kBufferSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kBufferSize, /*bitmap_allocator=*/false); PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); @@ -1076,7 +1058,7 @@ TEST(BufferTest, SetStringFixedBufferFailurePreservesHeader) { TEST(BufferTest, StringReallocFixedBufferFailurePreservesValue) { constexpr uint32_t kBufferSize = 4096; - char *buffer = (char *)calloc(kBufferSize, 1); + char *buffer = static_cast(calloc(kBufferSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kBufferSize, /*bitmap_allocator=*/false); PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); @@ -1107,7 +1089,7 @@ TEST(BufferTest, StringReallocFixedBufferFailurePreservesValue) { } TEST(BufferTest, StringWithinBoundsRejectsNullHeader) { - char *buffer = (char *)calloc(4096, 1); + char *buffer = static_cast(calloc(4096, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); EXPECT_FALSE( @@ -1117,7 +1099,7 @@ TEST(BufferTest, StringWithinBoundsRejectsNullHeader) { } TEST(BufferTest, StringReadersReturnEmptyForNullHeader) { - char *buffer = (char *)calloc(4096, 1); + char *buffer = static_cast(calloc(4096, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(4096); const toolbelt::StringHeader *header = nullptr; @@ -1132,7 +1114,7 @@ TEST(BufferTest, StringReadersReturnEmptyForNullHeader) { TEST(BufferTest, StringReadersRejectHeaderOffsetPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); const BufferOffset straddling = static_cast(kSize - 2); @@ -1154,7 +1136,7 @@ TEST(BufferTest, StringReadersRejectHeaderOffsetPastEnd) { TEST(BufferTest, StringWritersRejectHeaderOffsetPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, /*bitmap_allocator=*/false); const uint32_t free_len = pb->FreeList()->length; @@ -1178,7 +1160,7 @@ TEST(BufferTest, StringWritersRejectHeaderOffsetPastEnd) { TEST(BufferTest, AllocateStringReturnsWritableSpanAndStoresOffset) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize, /*bitmap_allocator=*/false); PayloadBuffer::AllocateMainMessage(&pb, sizeof(toolbelt::StringHeader)); @@ -1202,7 +1184,7 @@ TEST(BufferTest, AllocateStringReturnsWritableSpanAndStoresOffset) { TEST(BufferTest, StringWithinBoundsRejectsBodyOffsetPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); toolbelt::StringHeader header = static_cast(kSize - 2); @@ -1214,7 +1196,7 @@ TEST(BufferTest, StringWithinBoundsRejectsBodyOffsetPastEnd) { TEST(BufferTest, StringWithinBoundsRejectsBodyPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); toolbelt::StringHeader header = @@ -1229,7 +1211,7 @@ TEST(BufferTest, StringWithinBoundsRejectsBodyPastEnd) { TEST(BufferTest, VectorGetRejectsIndexPastEnd) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); VectorHeader hdr; @@ -1243,7 +1225,7 @@ TEST(BufferTest, VectorGetRejectsIndexPastEnd) { TEST(BufferTest, ToOffsetAndToAddressAgreeOnTrailingExtent) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); uint32_t *p = @@ -1257,7 +1239,7 @@ TEST(BufferTest, ToOffsetAndToAddressAgreeOnTrailingExtent) { TEST(BufferTest, ToAddressVoidStartOnlyBoundary) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); EXPECT_NE(pb->ToAddress(kSize - 1), nullptr); @@ -1268,9 +1250,9 @@ TEST(BufferTest, ToAddressVoidStartOnlyBoundary) { TEST(BufferTest, ToAddressAndToOffsetRejectOutOfRangeInputs) { constexpr size_t kSize = 4096; - char *buffer = (char *)calloc(kSize, 1); + char *buffer = static_cast(calloc(kSize, 1)); PayloadBuffer *pb = new (buffer) PayloadBuffer(kSize); - char *other_buffer = (char *)calloc(kSize, 1); + char *other_buffer = static_cast(calloc(kSize, 1)); const BufferOffset far_offset = std::numeric_limits::max(); EXPECT_EQ(pb->ToAddress(far_offset), nullptr); diff --git a/toolbelt/pipe.cc b/toolbelt/pipe.cc index 0cb8312..9010eb0 100644 --- a/toolbelt/pipe.cc +++ b/toolbelt/pipe.cc @@ -1,6 +1,7 @@ #include "toolbelt/pipe.h" #include "absl/strings/str_format.h" +#include #include namespace toolbelt { @@ -71,19 +72,26 @@ absl::StatusOr Pipe::GetPipeSize() { absl::Status Pipe::SetPipeSize(size_t size) { #if defined(__linux__) - int e = fcntl(write_.Fd(), F_SETPIPE_SZ, size); + if (size > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Pipe size too large"); + } + int e = fcntl(write_.Fd(), F_SETPIPE_SZ, static_cast(size)); if (e == -1) { return absl::InternalError( absl::StrFormat("Failed to set pipe size: %s", strerror(errno))); } return absl::OkStatus(); #else + (void)size; return absl::UnimplementedError("SetPipeSize is not implemented on this OS"); #endif } absl::StatusOr Pipe::Read(char *buffer, size_t length, const co::Coroutine *c) { + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Read size too large"); + } size_t total = 0; ScopedRead sc(*this, c); @@ -117,13 +125,18 @@ absl::StatusOr Pipe::Read(char *buffer, size_t length, } } } - total += n; + if (n > 0) { + total += static_cast(n); + } } - return total; + return static_cast(total); } absl::StatusOr Pipe::Write(const char *buffer, size_t length, const co::Coroutine *c) { + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Write size too large"); + } size_t total = 0; ScopedWrite sc(*this, c); @@ -158,9 +171,11 @@ absl::StatusOr Pipe::Write(const char *buffer, size_t length, } } } - total += n; + if (n > 0) { + total += static_cast(n); + } } - return total; + return static_cast(total); } } // namespace toolbelt \ No newline at end of file diff --git a/toolbelt/pipe.h b/toolbelt/pipe.h index cfe4749..bf56a05 100644 --- a/toolbelt/pipe.h +++ b/toolbelt/pipe.h @@ -3,11 +3,12 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_format.h" -#include "co/coroutine.h" +#include "toolbelt/coroutine.h" #include "toolbelt/fd.h" #include #include +#include #include #include #include @@ -154,14 +155,17 @@ template class SharedPtrPipe : public Pipe { const co::Coroutine * = nullptr) override { return absl::InternalError("Not supported on SharedPtrPipe"); } - absl::StatusOr Write(const char *, size_t , - const co::Coroutine *c = nullptr) override { + absl::StatusOr Write(const char *, size_t, + const co::Coroutine * = nullptr) override { return absl::InternalError("Not supported on SharedPtrPipe"); } absl::StatusOr> Read(const co::Coroutine *c = nullptr) { char buffer[sizeof(std::shared_ptr)]; - size_t length = sizeof(buffer); + const size_t length = sizeof(buffer); + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Read size too large"); + } size_t total = 0; ScopedRead sc(*this, c); @@ -195,7 +199,9 @@ template class SharedPtrPipe : public Pipe { } } } - total += n; + if (n > 0) { + total += static_cast(n); + } } // Ref count = N + 1. auto copy = *reinterpret_cast *>(buffer); @@ -230,8 +236,11 @@ template class SharedPtrPipe : public Pipe { ScopedReference sr(buffer); + const size_t length = sizeof(buffer); + if (length > static_cast(std::numeric_limits::max())) { + return absl::InternalError("Write size too large"); + } size_t total = 0; - size_t length = sizeof(buffer); while (total < length) { if (c != nullptr) { // When writing we use PollAndWait to cause the write to happen as soon @@ -263,7 +272,9 @@ template class SharedPtrPipe : public Pipe { } } } - total += n; + if (n > 0) { + total += static_cast(n); + } } // Prevent deref of pointer in buffer. sr.buffer_ = nullptr; diff --git a/toolbelt/pipe_test.cc b/toolbelt/pipe_test.cc index 30be8a5..8323b9b 100644 --- a/toolbelt/pipe_test.cc +++ b/toolbelt/pipe_test.cc @@ -3,7 +3,7 @@ // See LICENSE file for licensing information. #include "absl/status/status_matchers.h" -#include "co/coroutine.h" +#include "toolbelt/coroutine.h" #include "pipe.h" #include #include @@ -70,7 +70,7 @@ TEST(PipeTest, CoroutinePipeReadAndWrite) { auto r = pipe.Read(buffer, 5, c); ASSERT_OK(r); ASSERT_EQ(*r, 5); - ASSERT_EQ(std::string_view(buffer, *r), "Hello"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "Hello"); }); co::Coroutine writer(scheduler, [&pipe](co::Coroutine *c) { const char *msg = "Hello"; @@ -93,7 +93,7 @@ TEST(PipeTest, CoroutinePipeReadAndWriteNonblocking) { auto r = pipe.Read(buffer, 5, c); ASSERT_OK(r); ASSERT_EQ(*r, 5); - ASSERT_EQ(std::string_view(buffer, *r), "Hello"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "Hello"); }); co::Coroutine writer(scheduler, [&pipe](co::Coroutine *c) { const char *msg = "Hello"; @@ -159,7 +159,7 @@ TEST(PipeTest, CoroutineFullPipeReadAndWrite) { auto r = pipe.Read(buffer, kMessageSize, c); ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); - ASSERT_EQ(std::string_view(buffer, *r), "1234"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "1234"); } }); co::Coroutine writer(scheduler, [&pipe, kMessageSize](co::Coroutine *c) { @@ -194,7 +194,7 @@ TEST(PipeTest, CoroutineOverFullPipeReadAndWrite) { auto r = pipe.Read(buffer, kMessageSize, c); ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); - ASSERT_EQ(std::string_view(buffer, *r), "1234"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "1234"); } }); co::Coroutine writer(scheduler, [&pipe, kMessageSize](co::Coroutine *c) { @@ -226,7 +226,7 @@ TEST(PipeTest, CoroutineFullPipeReadAndWriteNonblocking) { auto r = pipe.Read(buffer, kMessageSize, c); ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); - ASSERT_EQ(std::string_view(buffer, *r), "1234"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "1234"); } }); co::Coroutine writer(scheduler, [&pipe, kMessageSize](co::Coroutine *c) { @@ -262,7 +262,7 @@ TEST(PipeTest, CoroutineOverFullPipeReadAndWriteNonblocking) { auto r = pipe.Read(buffer, kMessageSize, c); ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); - ASSERT_EQ(std::string_view(buffer, *r), "1234"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "1234"); } }); co::Coroutine writer(scheduler, [&pipe, kMessageSize](co::Coroutine *c) { @@ -288,12 +288,12 @@ TEST(PipeTest, CoroutinePipeReadAndMultiWrite) { auto r = pipe.Read(buffer, 5, c); ASSERT_OK(r); ASSERT_EQ(*r, 5); - ASSERT_EQ(std::string_view(buffer, *r), "12345"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "12345"); r = pipe.Read(buffer, 5, c); ASSERT_OK(r); ASSERT_EQ(*r, 5); - ASSERT_EQ(std::string_view(buffer, *r), "54321"); + ASSERT_EQ(std::string_view(buffer, static_cast(*r)), "54321"); }); co::Coroutine writer1(scheduler, [&pipe](co::Coroutine *c) { @@ -333,7 +333,7 @@ TEST(PipeTest, CoroutineOverFullPipeReadAndWriteMultiwriter) { ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); // Can be in either order. - std::string_view got(buffer, *r); + std::string_view got(buffer, static_cast(*r)); bool ok = got == "1234" || got == "4321"; ASSERT_TRUE(ok); } @@ -382,7 +382,7 @@ TEST(PipeTest, CoroutineOverFullPipeReadAndWriteMultiwriterNonblocking) { ASSERT_OK(r); ASSERT_EQ(*r, kMessageSize); // Can be in either order. - std::string_view got(buffer, *r); + std::string_view got(buffer, static_cast(*r)); bool ok = got == "1234" || got == "4321"; ASSERT_TRUE(ok); } diff --git a/toolbelt/sockets.cc b/toolbelt/sockets.cc index 965a0af..5bc93a6 100644 --- a/toolbelt/sockets.cc +++ b/toolbelt/sockets.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include "absl/strings/str_format.h" @@ -56,7 +57,9 @@ InetAddress::InetAddress(const std::string &hostname, int port) { struct hostent *entry = gethostbyname(hostname.c_str()); in_addr_t ipaddr; if (entry != NULL) { - ipaddr = ((struct in_addr *)entry->h_addr_list[0])->s_addr; + const struct in_addr *in_addr_ptr = + reinterpret_cast(entry->h_addr_list[0]); + ipaddr = in_addr_ptr->s_addr; } else { // No hostname found, try IP address. if (inet_pton(AF_INET, hostname.c_str(), &ipaddr) != 1) { @@ -159,7 +162,11 @@ std::string VirtualAddress::ToString() const { static ssize_t ReceiveFully(const co::Coroutine *c, int fd, size_t length, char *buffer, size_t buflen) { - int offset = 0; + if (length > static_cast(std::numeric_limits::max())) { + errno = EINVAL; + return -1; + } + size_t offset = 0; size_t remaining = length; while (remaining > 0) { size_t readlen = std::min(remaining, buflen); @@ -187,14 +194,18 @@ static ssize_t ReceiveFully(const co::Coroutine *c, int fd, size_t length, // Short read. return 0; } - remaining -= n; - offset += n; + remaining -= static_cast(n); + offset += static_cast(n); } - return length; + return static_cast(length); } static ssize_t SendFully(const co::Coroutine *c, int fd, const char *buffer, size_t length, bool blocking) { + if (length > static_cast(std::numeric_limits::max())) { + errno = EINVAL; + return -1; + } size_t remaining = length; size_t offset = 0; while (remaining > 0) { @@ -235,10 +246,10 @@ static ssize_t SendFully(const co::Coroutine *c, int fd, const char *buffer, // EOF on write. return -1; } - remaining -= n; - offset += n; + remaining -= static_cast(n); + offset += static_cast(n); } - return length; + return static_cast(length); } absl::StatusOr Socket::Receive(char *buffer, size_t buflen, @@ -293,7 +304,9 @@ absl::StatusOr Socket::ReceiveMessage(char *buffer, size_t buflen, return absl::InternalError(absl::StrFormat( "Failed to read length from socket %d: %s", fd_.Fd(), strerror(errno))); } - size_t length = ntohl(*reinterpret_cast(lenbuf)); + uint32_t encoded_length = 0; + std::memcpy(&encoded_length, lenbuf, sizeof(encoded_length)); + const size_t length = ntohl(encoded_length); n = ReceiveFully(c, fd_.Fd(), length, buffer, buflen); if (n == -1) { return absl::InternalError(absl::StrFormat( @@ -326,7 +339,9 @@ Socket::ReceiveVariableLengthMessage(const co::Coroutine *c) { return absl::InternalError(absl::StrFormat( "Failed to read length from socket %d: %s", fd_.Fd(), strerror(errno))); } - size_t length = ntohl(*reinterpret_cast(lenbuf)); + uint32_t encoded_length = 0; + std::memcpy(&encoded_length, lenbuf, sizeof(encoded_length)); + const size_t length = ntohl(encoded_length); std::vector buffer(length); n = ReceiveFully(c, fd_.Fd(), length, buffer.data(), buffer.size()); @@ -342,12 +357,19 @@ absl::StatusOr Socket::SendMessage(char *buffer, size_t length, if (!Connected()) { return absl::InternalError("Socket is not connected"); } + if (length > std::numeric_limits::max() || + length > + static_cast(std::numeric_limits::max()) - + sizeof(uint32_t)) { + return absl::InvalidArgumentError("Message is too large"); + } // Insert length in network byte order immediately before // the address passed as the buffer. - int32_t *lengthptr = reinterpret_cast(buffer) - 1; - *lengthptr = htonl(length); - ssize_t n = SendFully(c, fd_.Fd(), reinterpret_cast(lengthptr), - length + sizeof(int32_t), IsBlocking()); + char *message_start = buffer - sizeof(uint32_t); + const uint32_t encoded_length = htonl(static_cast(length)); + std::memcpy(message_start, &encoded_length, sizeof(encoded_length)); + ssize_t n = SendFully(c, fd_.Fd(), message_start, + length + sizeof(encoded_length), IsBlocking()); if (n == -1) { return absl::InternalError( absl::StrFormat("Failed to write to socket: %s", strerror(errno))); @@ -497,7 +519,7 @@ absl::Status UnixSocket::SendFds(const std::vector &fds, struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(fds_size); + cmsg->cmsg_len = static_cast(CMSG_LEN(fds_size)); int *fdptr = reinterpret_cast(CMSG_DATA(cmsg)); for (size_t i = first_fd; i < first_fd + fds_to_send; i++) { *fdptr++ = fds[i].Fd(); @@ -510,7 +532,7 @@ absl::Status UnixSocket::SendFds(const std::vector &fds, return absl::InternalError("Interrupted"); } } - int e = ::sendmsg(fd_.Fd(), &msg, 0); + ssize_t e = ::sendmsg(fd_.Fd(), &msg, 0); if (e == -1) { return absl::InternalError(absl::StrFormat( "Failed to write fds to unix socket: %s", strerror(errno))); @@ -936,7 +958,7 @@ absl::StatusOr UDPSocket::ReceiveFrom(InetAddress &sender, absl::StrFormat("Unable to receive UDP datagram: %s", strerror(errno))); } #if defined(__APPLE__) - sender_addr.sin_len = sender_addr_length; + sender_addr.sin_len = static_cast<__uint8_t>(sender_addr_length); #endif sender = {sender_addr}; return n; @@ -1037,7 +1059,7 @@ VirtualStreamSocket::LocalAddress(uint32_t port) const { // If we cannot get the local CID, return ANY. int32_t cid = VMADDR_CID_ANY; #endif - return VirtualAddress(cid, port); + return VirtualAddress(static_cast(cid), port); } absl::StatusOr VirtualStreamSocket::GetPeerName() const { diff --git a/toolbelt/sockets.h b/toolbelt/sockets.h index a159894..629d7e9 100644 --- a/toolbelt/sockets.h +++ b/toolbelt/sockets.h @@ -6,7 +6,7 @@ #define __TOOLBELT_SOCKETS_H #include "absl/status/status.h" #include "absl/status/statusor.h" -#include "co/coroutine.h" +#include "toolbelt/coroutine.h" #include "fd.h" #include #include @@ -277,7 +277,7 @@ class SocketAddress { } // What address type is in the variant. - int Type() const { return address_.index(); } + int Type() const { return static_cast(address_.index()); } int Port() const { return std::visit( @@ -757,7 +757,10 @@ class StreamSocket { return SocketAddress(*st); }, [&](const VirtualStreamSocket &s) -> absl::StatusOr { - auto st = s.LocalAddress(port); + if (port < 0) { + return absl::InvalidArgumentError("Port must be non-negative"); + } + auto st = s.LocalAddress(static_cast(port)); if (!st.ok()) { return st; } diff --git a/toolbelt/sockets_test.cc b/toolbelt/sockets_test.cc index f766c8c..29543ba 100644 --- a/toolbelt/sockets_test.cc +++ b/toolbelt/sockets_test.cc @@ -104,12 +104,12 @@ TEST(SocketsTest, UnixSocket) { ASSERT_TRUE(nbytes.ok()); auto n = nbytes.value(); ASSERT_EQ(12, n); // "hello world\0" - ASSERT_EQ("hello world", std::string(buffer + 4, n - 1)); + ASSERT_EQ("hello world", std::string(buffer + 4, static_cast(n - 1))); std::vector fds; absl::Status s2 = socket.ReceiveFds(fds, c); ASSERT_TRUE(s2.ok()); - ASSERT_EQ(3, fds.size()); + ASSERT_EQ(size_t{3}, fds.size()); }); co::Coroutine outgoing(scheduler, [&socket_name](co::Coroutine* c) { @@ -120,7 +120,8 @@ TEST(SocketsTest, UnixSocket) { // SendMessage uses the 4 bytes below the buffer for the length of the message. ssize_t n = snprintf(buffer + 4, sizeof(buffer) - 4, "hello world"); n += 1; // Include NUL at end. - absl::StatusOr nsent = socket.SendMessage(buffer + 4, n, c); + absl::StatusOr nsent = + socket.SendMessage(buffer + 4, static_cast(n), c); ASSERT_TRUE(nsent.ok()); ASSERT_EQ(n + 4, nsent.value()); @@ -198,10 +199,10 @@ TEST(SocketsTest, UnixSocketShortFdCountRead) { std::vector fds; absl::Status s2 = socket.ReceiveFds(fds, c); ASSERT_TRUE(s2.ok()); - ASSERT_EQ(1, fds.size()); + ASSERT_EQ(size_t{1}, fds.size()); }); - co::Coroutine outgoing(scheduler, [&socket_name](co::Coroutine* c) { + co::Coroutine outgoing(scheduler, [&socket_name](co::Coroutine* /*c*/) { toolbelt::UnixSocket socket; absl::Status s = socket.Connect(socket_name); ASSERT_TRUE(s.ok()); @@ -264,7 +265,7 @@ TEST(SocketsTest, TCPSocket) { absl::StatusOr> b = socket.ReceiveVariableLengthMessage(c); ASSERT_TRUE(b.ok()); auto buf = b.value(); - ASSERT_EQ(12, buf.size()); // "hello world\0" + ASSERT_EQ(size_t{12}, buf.size()); // "hello world\0" ASSERT_EQ("hello world", std::string(buf.data(), 11)); }); @@ -276,7 +277,8 @@ TEST(SocketsTest, TCPSocket) { // SendMessage uses the 4 bytes below the buffer for the length of the message. ssize_t n = snprintf(buffer + 4, sizeof(buffer) - 4, "hello world"); n += 1; // Include NUL at end. - absl::StatusOr nsent = socket.SendMessage(buffer + 4, n, c); + absl::StatusOr nsent = + socket.SendMessage(buffer + 4, static_cast(n), c); ASSERT_TRUE(nsent.ok()); ASSERT_EQ(n + 4, nsent.value()); }); @@ -310,7 +312,9 @@ TEST(SocketsTest, BigTCPSocketNonblocking) { std::cerr << "Mismatch at " << i << ": " << buf[i] << " != " << 'a' + (i % 26) << "\n"; } - ASSERT_EQ('a' + ((i + 4) % 26), buf[i]); + ASSERT_EQ(static_cast(static_cast('a') + + ((i + 4) % 26)), + buf[i]); } }); @@ -326,7 +330,7 @@ TEST(SocketsTest, BigTCPSocketNonblocking) { absl::StatusOr nsent = socket.SendMessage(buffer.data() + 4, buffer.size() - 4, c); ASSERT_TRUE(nsent.ok()); - ASSERT_EQ(buffer.size(), nsent.value()); + ASSERT_EQ(static_cast(buffer.size()), nsent.value()); }); scheduler.Run(); @@ -358,7 +362,10 @@ TEST(SocketsTest, BigTCPSocketBlocking) { std::cerr << "Mismatch at " << i << ": " << buf[i] << " != " << 'a' + (i % 26) << "\n"; } - ASSERT_EQ('a' + ((i + 4) % 26), buf[i]); + ASSERT_EQ( + static_cast(static_cast('a') + + ((i + 4) % 26)), + buf[i]); } }); @@ -373,7 +380,7 @@ TEST(SocketsTest, BigTCPSocketBlocking) { absl::StatusOr nsent = socket.SendMessage(buffer.data() + 4, buffer.size() - 4, c); ASSERT_TRUE(nsent.ok()); - ASSERT_EQ(buffer.size(), nsent.value()); + ASSERT_EQ(static_cast(buffer.size()), nsent.value()); }); std::thread sender([&sendScheduler]() { sendScheduler.Run(); }); @@ -430,7 +437,7 @@ TEST(SocketsTest, TCPSocket2) { ASSERT_TRUE(nbytes.ok()); auto n = nbytes.value(); ASSERT_EQ(12, n); // "hello world\0" - ASSERT_EQ("hello world", std::string(buffer, n - 1)); + ASSERT_EQ("hello world", std::string(buffer, static_cast(n - 1))); std::vector fds; }); @@ -441,7 +448,7 @@ TEST(SocketsTest, TCPSocket2) { char buffer[256]; ssize_t n = snprintf(buffer, sizeof(buffer), "hello world"); n += 1; // Include NUL at end. - absl::StatusOr nsent = socket.Send(buffer, n, c); + absl::StatusOr nsent = socket.Send(buffer, static_cast(n), c); ASSERT_TRUE(nsent.ok()); ASSERT_EQ(n, nsent.value()); }); @@ -471,7 +478,7 @@ TEST(SocketsTest, TCPSocket3) { ASSERT_TRUE(nbytes.ok()); auto n = nbytes.value(); ASSERT_EQ(12, n); // "hello world\0" - ASSERT_EQ("hello world", std::string(buffer, n - 1)); + ASSERT_EQ("hello world", std::string(buffer, static_cast(n - 1))); std::vector fds; }); @@ -482,7 +489,7 @@ TEST(SocketsTest, TCPSocket3) { char buffer[256]; ssize_t n = snprintf(buffer, sizeof(buffer), "hello world"); n += 1; // Include NUL at end. - absl::StatusOr nsent = socket.Send(buffer, n, c); + absl::StatusOr nsent = socket.Send(buffer, static_cast(n), c); ASSERT_TRUE(nsent.ok()); ASSERT_EQ(n, nsent.value()); }); @@ -528,7 +535,7 @@ TEST(SocketsTest, UDPSocket) { ASSERT_TRUE(nbytes.ok()); auto n = nbytes.value(); ASSERT_EQ(12, n); // "hello world\0" - ASSERT_EQ("hello world", std::string(buffer, n - 1)); + ASSERT_EQ("hello world", std::string(buffer, static_cast(n - 1))); }); co::Coroutine outgoing(scheduler, [&sender, &Receiver](co::Coroutine* c) { @@ -540,7 +547,7 @@ TEST(SocketsTest, UDPSocket) { ssize_t n = snprintf(buffer, sizeof(buffer), "hello world"); n += 1; // Include NUL at end. - absl::Status s2 = socket.SendTo(Receiver, buffer, n, c); + absl::Status s2 = socket.SendTo(Receiver, buffer, static_cast(n), c); ASSERT_TRUE(s2.ok()); }); @@ -565,7 +572,7 @@ TEST(SocketsTest, UDPSocket2) { ASSERT_TRUE(nbytes.ok()); auto n = nbytes.value(); ASSERT_EQ(12, n); // "hello world\0" - ASSERT_EQ("hello world", std::string(buffer, n - 1)); + ASSERT_EQ("hello world", std::string(buffer, static_cast(n - 1))); ASSERT_EQ(sender, from); }); @@ -578,7 +585,7 @@ TEST(SocketsTest, UDPSocket2) { ssize_t n = snprintf(buffer, sizeof(buffer), "hello world"); n += 1; // Include NUL at end. - absl::Status s2 = socket.SendTo(receiver, buffer, n, c); + absl::Status s2 = socket.SendTo(receiver, buffer, static_cast(n), c); ASSERT_TRUE(s2.ok()); }); @@ -609,7 +616,8 @@ TEST(SocketsTest, UDPSocket_SendAndReceiveUnicast) { ASSERT_TRUE(sender.SendTo(sendto_address, TEST_DATA.data(), TEST_DATA.size()).ok()); std::vector Receive_buffer(TEST_DATA.size()); - ASSERT_EQ(*Receiver.Receive(Receive_buffer.data(), Receive_buffer.size()), TEST_DATA.size()); + ASSERT_EQ(*Receiver.Receive(Receive_buffer.data(), Receive_buffer.size()), + static_cast(TEST_DATA.size())); ASSERT_EQ(std::string_view(Receive_buffer.data(), Receive_buffer.size()), TEST_DATA); } @@ -632,7 +640,8 @@ TEST(SocketsTest, UDPSocket_SendAndReceiveBroadcast) { ASSERT_TRUE(sender.SendTo(sendto_address, TEST_DATA.data(), TEST_DATA.size()).ok()); std::vector Receive_buffer(TEST_DATA.size()); - ASSERT_EQ(*Receiver.Receive(Receive_buffer.data(), Receive_buffer.size()), TEST_DATA.size()); + ASSERT_EQ(*Receiver.Receive(Receive_buffer.data(), Receive_buffer.size()), + static_cast(TEST_DATA.size())); ASSERT_EQ(std::string_view(Receive_buffer.data(), Receive_buffer.size()), TEST_DATA); } @@ -663,7 +672,8 @@ TEST(SocketsTest, UDPSocket_SendAndReceiveMulticast) { while (absl::Now() < timeout) { auto status_or_len = Receiver.Receive(Receive_buffer.data(), Receive_buffer.size()); if (status_or_len.ok()) { - ASSERT_EQ(*status_or_len, TEST_DATA.size()); + ASSERT_EQ(*status_or_len, + static_cast(TEST_DATA.size())); ASSERT_EQ(std::string_view(Receive_buffer.data(), Receive_buffer.size()), TEST_DATA); break; } diff --git a/toolbelt/table.cc b/toolbelt/table.cc index 3840688..1914531 100644 --- a/toolbelt/table.cc +++ b/toolbelt/table.cc @@ -4,13 +4,17 @@ #include "toolbelt/table.h" #include "absl/strings/str_format.h" +#include #include +#include namespace toolbelt { Table::Table( const std::vector titles, ssize_t sort_column, std::function comp) { - SortBy(sort_column, comp); + SortBy(sort_column < 0 ? std::numeric_limits::max() + : static_cast(sort_column), + comp); for (auto &title : titles) { cols_.push_back({.title = title}); } @@ -62,7 +66,7 @@ void Table::AddCell(size_t col, const Cell &cell) { } void Table::Print(int width, std::ostream &os) { - if (width == 0) { + if (width <= 1) { width = 80; } width -= 1; // Allow space for newline. @@ -74,7 +78,7 @@ void Table::Print(int width, std::ostream &os) { for (auto &col : cols_) { std::string title = col.title; if (title.size() > static_cast(col.width)) { - title = title.substr(0, col.width - 1); + title = title.substr(0, static_cast(col.width) - 1); } os << std::left << std::setw(col.width) << std::setfill(' ') << title; } @@ -83,12 +87,12 @@ void Table::Print(int width, std::ostream &os) { os << std::setw(width) << std::setfill('-') << "" << std::endl; // Print each row. - for (int i = 0; i < num_rows_; i++) { + for (size_t i = 0; i < num_rows_; i++) { for (auto &col : cols_) { std::string data = col.cells[i].data; if (data.size() > static_cast(col.width)) { // Truncate if too wide. - data = data.substr(0, col.width - 1); + data = data.substr(0, static_cast(col.width) - 1); } os << std::left << color::SetColor(col.cells[i].color) << std::setw(col.width) << std::setfill(' ') << data @@ -106,9 +110,12 @@ void Table::Clear() { } void Table::Render(int width) { + if (cols_.empty()) { + return; + } std::vector max_widths(cols_.size()); - for (int i = 0; i < num_rows_; i++) { - int col_index = 0; + for (size_t i = 0; i < num_rows_; i++) { + size_t col_index = 0; for (auto &col : cols_) { if (col.cells[i].data.size() > max_widths[col_index]) { max_widths[col_index] = col.cells[i].data.size(); @@ -121,18 +128,26 @@ void Table::Render(int width) { total_width += w; } // Pad the column widths out to the width we have. - ssize_t padding = width - total_width; - padding /= cols_.size(); - int index = 0; + const size_t available_width = + width > 0 ? static_cast(width) : size_t{0}; + const size_t padding = + total_width < available_width + ? (available_width - total_width) / cols_.size() + : size_t{0}; + size_t index = 0; for (auto &col : cols_) { - col.width = max_widths[index] + padding; + const size_t desired_width = std::max(size_t{1}, max_widths[index] + padding); + col.width = static_cast( + std::min(desired_width, + static_cast(std::numeric_limits::max()))); index++; } Sort(); } void Table::Sort() { - if (sort_column_ == -1ULL || sort_column_ >= cols_.size()) { + if (sort_column_ == std::numeric_limits::max() || + sort_column_ >= cols_.size()) { return; } struct Index { @@ -140,8 +155,8 @@ void Table::Sort() { std::string data; }; std::vector index(num_rows_); - for (int i = 0; i < num_rows_; i++) { - index[i] = {.row = static_cast(i), .data = cols_[sort_column_].cells[i].data}; + for (size_t i = 0; i < num_rows_; i++) { + index[i] = {.row = i, .data = cols_[sort_column_].cells[i].data}; } std::sort(index.begin(), index.end(), [this](const Index &a, const Index &b) { return sorter_(a.data, b.data); diff --git a/toolbelt/table.h b/toolbelt/table.h index 75df96b..f414870 100644 --- a/toolbelt/table.h +++ b/toolbelt/table.h @@ -72,7 +72,7 @@ class Table { void AddCell(size_t col, const Cell &cell); std::vector cols_ = {}; - int num_rows_ = 0; + size_t num_rows_ = 0; size_t sort_column_ = 0; std::function sorter_;