| /* |
| * |
| * Copyright (c) 2020-2021 Project CHIP Authors |
| * Copyright (c) 2016-2017 Nest Labs, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file defines the member functions and private data for |
| * the chip::System::PacketBuffer class, which provides the |
| * mechanisms for manipulating packets of octet-serialized |
| * data. |
| */ |
| |
| // Include module header |
| #include <system/SystemPacketBuffer.h> |
| |
| // Include local headers |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <system/SystemFaultInjection.h> |
| #include <system/SystemMutex.h> |
| #include <system/SystemStats.h> |
| |
| #include <stdint.h> |
| |
| #include <limits.h> |
| #include <limits> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <utility> |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <lwip/mem.h> |
| #include <lwip/pbuf.h> |
| #if LWIP_VERSION_MAJOR == 2 && LWIP_VERSION_MINOR < 1 |
| #define PBUF_STRUCT_DATA_CONTIGUOUS(pbuf) (pbuf)->type == PBUF_RAM || (pbuf)->type == PBUF_POOL |
| #else |
| #define PBUF_STRUCT_DATA_CONTIGUOUS(pbuf) (pbuf)->type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS |
| #endif |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| #include <lib/support/CHIPMem.h> |
| #endif |
| |
| namespace chip { |
| namespace System { |
| |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL |
| // |
| // Pool allocation for PacketBuffer objects. |
| // |
| |
| PacketBuffer::BufferPoolElement PacketBuffer::sBufferPool[CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE]; |
| |
| PacketBuffer * PacketBuffer::sFreeList = PacketBuffer::BuildFreeList(); |
| |
| #if !CHIP_SYSTEM_CONFIG_NO_LOCKING |
| static Mutex sBufferPoolMutex; |
| |
| #define LOCK_BUF_POOL() \ |
| do \ |
| { \ |
| sBufferPoolMutex.Lock(); \ |
| } while (0) |
| #define UNLOCK_BUF_POOL() \ |
| do \ |
| { \ |
| sBufferPoolMutex.Unlock(); \ |
| } while (0) |
| #endif // !CHIP_SYSTEM_CONFIG_NO_LOCKING |
| |
| PacketBuffer * PacketBuffer::BuildFreeList() |
| { |
| pbuf * lHead = nullptr; |
| |
| for (int i = 0; i < CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE; i++) |
| { |
| pbuf * lCursor = &sBufferPool[i].Header; |
| lCursor->next = lHead; |
| lCursor->ref = 0; |
| lHead = lCursor; |
| } |
| |
| #if !CHIP_SYSTEM_CONFIG_NO_LOCKING |
| Mutex::Init(sBufferPoolMutex); |
| #endif // !CHIP_SYSTEM_CONFIG_NO_LOCKING |
| |
| return static_cast<PacketBuffer *>(lHead); |
| } |
| |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| // |
| // Heap allocation for PacketBuffer objects. |
| // |
| |
| #if CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK |
| void PacketBuffer::InternalCheck(const PacketBuffer * buffer) |
| { |
| if (buffer) |
| { |
| VerifyOrDieWithMsg(::chip::Platform::MemoryDebugCheckPointer(buffer, buffer->alloc_size + kStructureSize), chipSystemLayer, |
| "invalid packet buffer pointer"); |
| VerifyOrDieWithMsg(buffer->alloc_size >= buffer->ReservedSize() + buffer->len, chipSystemLayer, |
| "packet buffer overflow %" PRIu32 " < %" PRIu32 " +%" PRIu32, static_cast<uint32_t>(buffer->alloc_size), |
| static_cast<uint32_t>(buffer->ReservedSize()), static_cast<uint32_t>(buffer->len)); |
| } |
| } |
| #endif // CHIP_SYSTEM_PACKETBUFFER_HAS_CHECK |
| |
| // Number of unused bytes below which \c RightSize() won't bother reallocating. |
| constexpr uint16_t kRightSizingThreshold = 16; |
| |
| void PacketBufferHandle::InternalRightSize() |
| { |
| // Require a single buffer with no other references. |
| if ((mBuffer == nullptr) || mBuffer->HasChainedBuffer() || (mBuffer->ref != 1)) |
| { |
| return; |
| } |
| |
| // Reallocate only if enough space will be saved. |
| const uint8_t * const start = mBuffer->ReserveStart(); |
| const uint8_t * const payload = mBuffer->Start(); |
| const size_t usedSize = static_cast<size_t>(payload - start + static_cast<ptrdiff_t>(mBuffer->len)); |
| if (usedSize + kRightSizingThreshold > mBuffer->alloc_size) |
| { |
| return; |
| } |
| |
| const size_t blockSize = usedSize + PacketBuffer::kStructureSize; |
| PacketBuffer * newBuffer = reinterpret_cast<PacketBuffer *>(chip::Platform::MemoryAlloc(blockSize)); |
| if (newBuffer == nullptr) |
| { |
| ChipLogError(chipSystemLayer, "PacketBuffer: pool EMPTY."); |
| return; |
| } |
| |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kSystemLayer_NumPacketBufs); |
| |
| uint8_t * const newStart = newBuffer->ReserveStart(); |
| newBuffer->next = nullptr; |
| newBuffer->payload = newStart + (payload - start); |
| newBuffer->tot_len = mBuffer->tot_len; |
| newBuffer->len = mBuffer->len; |
| newBuffer->ref = 1; |
| newBuffer->alloc_size = usedSize; |
| memcpy(newStart, start, usedSize); |
| |
| PacketBuffer::Free(mBuffer); |
| mBuffer = newBuffer; |
| } |
| |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_LWIP_CUSTOM_POOL |
| |
| void PacketBufferHandle::InternalRightSize() |
| { |
| PacketBuffer * lNewPacket = static_cast<PacketBuffer *>(pbuf_rightsize((struct pbuf *) mBuffer, -1)); |
| if (lNewPacket != mBuffer) |
| { |
| mBuffer = lNewPacket; |
| SYSTEM_STATS_UPDATE_LWIP_PBUF_COUNTS(); |
| ChipLogDetail(chipSystemLayer, "PacketBuffer: RightSize Copied"); |
| } |
| } |
| |
| #endif |
| |
| #ifndef LOCK_BUF_POOL |
| #define LOCK_BUF_POOL() \ |
| do \ |
| { \ |
| } while (0) |
| #endif // !defined(LOCK_BUF_POOL) |
| |
| #ifndef UNLOCK_BUF_POOL |
| #define UNLOCK_BUF_POOL() \ |
| do \ |
| { \ |
| } while (0) |
| #endif // !defined(UNLOCK_BUF_POOL) |
| |
| void PacketBuffer::SetStart(uint8_t * aNewStart) |
| { |
| uint8_t * const kStart = ReserveStart(); |
| uint8_t * const kEnd = this->Start() + this->MaxDataLength(); |
| |
| if (aNewStart < kStart) |
| aNewStart = kStart; |
| else if (aNewStart > kEnd) |
| aNewStart = kEnd; |
| |
| ptrdiff_t lDelta = aNewStart - static_cast<uint8_t *>(this->payload); |
| if (lDelta > 0 && this->len < static_cast<size_t>(lDelta)) |
| lDelta = static_cast<ptrdiff_t>(this->len); |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| VerifyOrDieWithMsg((static_cast<ptrdiff_t>(this->len) - lDelta) <= UINT16_MAX, chipSystemLayer, |
| "LwIP buffer length cannot exceed UINT16_MAX"); |
| this->len = static_cast<uint16_t>(static_cast<ptrdiff_t>(this->len) - lDelta); |
| VerifyOrDieWithMsg((static_cast<ptrdiff_t>(this->tot_len) - lDelta) <= UINT16_MAX, chipSystemLayer, |
| "LwIP buffer length cannot exceed UINT16_MAX"); |
| this->tot_len = static_cast<uint16_t>(static_cast<ptrdiff_t>(this->tot_len) - lDelta); |
| #else |
| this->len = static_cast<size_t>(static_cast<ptrdiff_t>(this->len) - lDelta); |
| this->tot_len = static_cast<size_t>(static_cast<ptrdiff_t>(this->tot_len) - lDelta); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| this->payload = aNewStart; |
| } |
| |
| void PacketBuffer::SetDataLength(size_t aNewLen, PacketBuffer * aChainHead) |
| { |
| const size_t kMaxDataLen = this->MaxDataLength(); |
| |
| if (aNewLen > kMaxDataLen) |
| aNewLen = kMaxDataLen; |
| |
| ssize_t lDelta = static_cast<ssize_t>(aNewLen) - static_cast<ssize_t>(this->len); |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| VerifyOrDieWithMsg(aNewLen <= UINT16_MAX, chipSystemLayer, "LwIP buffer length cannot exceed UINT16_MAX"); |
| this->len = static_cast<uint16_t>(aNewLen); |
| VerifyOrDieWithMsg((static_cast<ssize_t>(this->tot_len) + lDelta) <= UINT16_MAX, chipSystemLayer, |
| "LwIP buffer length cannot exceed UINT16_MAX"); |
| this->tot_len = static_cast<uint16_t>(static_cast<ssize_t>(this->tot_len) + lDelta); |
| #else |
| this->len = aNewLen; |
| this->tot_len = static_cast<size_t>(static_cast<ssize_t>(this->tot_len) + lDelta); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| // SetDataLength is often called after a client finished writing to the buffer, |
| // so it's a good time to check for possible corruption. |
| Check(this); |
| |
| while (aChainHead != nullptr && aChainHead != this) |
| { |
| Check(aChainHead); |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| VerifyOrDieWithMsg((static_cast<ssize_t>(aChainHead->tot_len) + lDelta) <= UINT16_MAX, chipSystemLayer, |
| "LwIP buffer length cannot exceed UINT16_MAX"); |
| aChainHead->tot_len = static_cast<uint16_t>(static_cast<ssize_t>(aChainHead->tot_len) + lDelta); |
| #else |
| aChainHead->tot_len = static_cast<size_t>(static_cast<ssize_t>(aChainHead->tot_len) + lDelta); |
| #endif |
| aChainHead = aChainHead->ChainedBuffer(); |
| } |
| } |
| |
| size_t PacketBuffer::MaxDataLength() const |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!(PBUF_STRUCT_DATA_CONTIGUOUS(this))) |
| { |
| return DataLength(); |
| } |
| #endif |
| return static_cast<size_t>(AllocSize() - ReservedSize()); |
| } |
| |
| size_t PacketBuffer::AvailableDataLength() const |
| { |
| return (this->MaxDataLength() - this->DataLength()); |
| } |
| |
| uint16_t PacketBuffer::ReservedSize() const |
| { |
| // Cast to uint16_t is safe because Start() always points to "after" |
| // ReserveStart(). At least when the payload is stored inline. |
| return static_cast<uint16_t>(Start() - ReserveStart()); |
| } |
| |
| uint8_t * PacketBuffer::ReserveStart() |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!(PBUF_STRUCT_DATA_CONTIGUOUS(this))) |
| { |
| return reinterpret_cast<uint8_t *>(this->Start()); |
| } |
| #endif |
| return reinterpret_cast<uint8_t *>(this) + kStructureSize; |
| } |
| |
| const uint8_t * PacketBuffer::ReserveStart() const |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!(PBUF_STRUCT_DATA_CONTIGUOUS(this))) |
| { |
| return reinterpret_cast<const uint8_t *>(this->Start()); |
| } |
| #endif |
| return reinterpret_cast<const uint8_t *>(this) + kStructureSize; |
| } |
| |
| void PacketBuffer::AddToEnd(PacketBufferHandle && aPacketHandle) |
| { |
| // Ownership of aPacketHandle's buffer is transferred to the end of the chain. |
| PacketBuffer * aPacket = std::move(aPacketHandle).UnsafeRelease(); |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| pbuf_cat(this, aPacket); |
| #else // !CHIP_SYSTEM_CONFIG_USE_LWIP |
| PacketBuffer * lCursor = this; |
| |
| while (true) |
| { |
| size_t old_total_length = lCursor->tot_len; |
| lCursor->tot_len = lCursor->tot_len + aPacket->tot_len; |
| VerifyOrDieWithMsg(lCursor->tot_len >= old_total_length, chipSystemLayer, "buffer chain too large"); |
| if (!lCursor->HasChainedBuffer()) |
| { |
| lCursor->next = aPacket; |
| break; |
| } |
| |
| lCursor = lCursor->ChainedBuffer(); |
| } |
| #endif // !CHIP_SYSTEM_CONFIG_USE_LWIP |
| } |
| |
| void PacketBuffer::CompactHead() |
| { |
| uint8_t * const kStart = ReserveStart(); |
| |
| if (this->payload != kStart) |
| { |
| memmove(kStart, this->payload, this->len); |
| this->payload = kStart; |
| } |
| |
| size_t lAvailLength = this->AvailableDataLength(); |
| |
| while (lAvailLength > 0 && HasChainedBuffer()) |
| { |
| PacketBuffer & lNextPacket = *ChainedBuffer(); |
| VerifyOrDieWithMsg(lNextPacket.ref == 1, chipSystemLayer, "next buffer %p is not exclusive to this chain", &lNextPacket); |
| |
| size_t lMoveLength = lNextPacket.len; |
| if (lMoveLength > lAvailLength) |
| lMoveLength = lAvailLength; |
| |
| memcpy(static_cast<uint8_t *>(this->payload) + this->len, lNextPacket.payload, lMoveLength); |
| |
| lNextPacket.payload = static_cast<uint8_t *>(lNextPacket.payload) + lMoveLength; |
| lAvailLength = lAvailLength - lMoveLength; |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| VerifyOrDieWithMsg(CanCastTo<uint16_t>(this->len + lMoveLength), chipSystemLayer, |
| "LwIP buffer length cannot exceed UINT16_MAX"); |
| this->len = static_cast<uint16_t>(this->len + lMoveLength); |
| lNextPacket.len = static_cast<uint16_t>(lNextPacket.len - lMoveLength); |
| lNextPacket.tot_len = static_cast<uint16_t>(lNextPacket.tot_len - lMoveLength); |
| #else |
| this->len = this->len + lMoveLength; |
| lNextPacket.len = lNextPacket.len - lMoveLength; |
| lNextPacket.tot_len = lNextPacket.tot_len - lMoveLength; |
| #endif |
| |
| if (lNextPacket.len == 0) |
| this->next = this->FreeHead(&lNextPacket); |
| } |
| } |
| |
| void PacketBuffer::ConsumeHead(size_t aConsumeLength) |
| { |
| if (aConsumeLength > this->len) |
| aConsumeLength = this->len; |
| this->payload = static_cast<uint8_t *>(this->payload) + aConsumeLength; |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| this->len = static_cast<uint16_t>(this->len - aConsumeLength); |
| this->tot_len = static_cast<uint16_t>(this->tot_len - aConsumeLength); |
| #else |
| this->len = this->len - aConsumeLength; |
| this->tot_len = this->tot_len - aConsumeLength; |
| #endif |
| } |
| |
| /** |
| * Consume data in a chain of buffers. |
| * |
| * Consume data in a chain of buffers starting with the current buffer and proceeding through the remaining buffers in the |
| * chain. Each buffer that is completely consumed is freed and the function returns the first buffer (if any) containing the |
| * remaining data. The current buffer must be the head of the buffer chain. |
| * |
| * @param[in] aConsumeLength - number of bytes to consume from the current chain. |
| * |
| * @return the first buffer from the current chain that contains any remaining data. If no data remains, nullptr is returned. |
| */ |
| PacketBuffer * PacketBuffer::Consume(size_t aConsumeLength) |
| { |
| PacketBuffer * lPacket = this; |
| |
| while (lPacket != nullptr && aConsumeLength > 0) |
| { |
| const size_t kLength = lPacket->DataLength(); |
| |
| if (aConsumeLength >= kLength) |
| { |
| lPacket = PacketBuffer::FreeHead(lPacket); |
| aConsumeLength = aConsumeLength - kLength; |
| } |
| else |
| { |
| lPacket->ConsumeHead(aConsumeLength); |
| break; |
| } |
| } |
| |
| return lPacket; |
| } |
| |
| CHIP_ERROR PacketBuffer::Read(uint8_t * aDestination, size_t aReadLength) const |
| { |
| const PacketBuffer * lPacket = this; |
| |
| if (aReadLength > TotalLength()) |
| { |
| return CHIP_ERROR_BUFFER_TOO_SMALL; |
| } |
| while (aReadLength > 0) |
| { |
| if (lPacket == nullptr) |
| { |
| // TotalLength() or an individual buffer's DataLength() must have been wrong. |
| return CHIP_ERROR_INTERNAL; |
| } |
| size_t lToReadFromCurrentBuf = lPacket->DataLength(); |
| if (aReadLength < lToReadFromCurrentBuf) |
| { |
| lToReadFromCurrentBuf = aReadLength; |
| } |
| memcpy(aDestination, lPacket->Start(), lToReadFromCurrentBuf); |
| aDestination += lToReadFromCurrentBuf; |
| aReadLength -= lToReadFromCurrentBuf; |
| lPacket = lPacket->ChainedBuffer(); |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| bool PacketBuffer::EnsureReservedSize(uint16_t aReservedSize) |
| { |
| const uint16_t kCurrentReservedSize = this->ReservedSize(); |
| if (aReservedSize <= kCurrentReservedSize) |
| return true; |
| |
| if ((aReservedSize + this->len) > this->AllocSize()) |
| return false; |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!(PBUF_STRUCT_DATA_CONTIGUOUS(this)) && aReservedSize > 0) |
| { |
| return false; |
| } |
| #endif |
| // Cast is safe because aReservedSize > kCurrentReservedSize. |
| const uint16_t kMoveLength = static_cast<uint16_t>(aReservedSize - kCurrentReservedSize); |
| memmove(static_cast<uint8_t *>(this->payload) + kMoveLength, this->payload, this->len); |
| payload = static_cast<uint8_t *>(this->payload) + kMoveLength; |
| |
| return true; |
| } |
| |
| bool PacketBuffer::AlignPayload(uint16_t aAlignBytes) |
| { |
| if (aAlignBytes == 0) |
| return false; |
| |
| const uint16_t kPayloadOffset = static_cast<uint16_t>(reinterpret_cast<uintptr_t>(this->payload) % aAlignBytes); |
| |
| if (kPayloadOffset == 0) |
| return true; |
| |
| // Cast is safe because by construction kPayloadOffset < aAlignBytes. |
| const uint16_t kPayloadShift = static_cast<uint16_t>(aAlignBytes - kPayloadOffset); |
| |
| if (!CanCastTo<uint16_t>(this->ReservedSize() + kPayloadShift)) |
| { |
| return false; |
| } |
| |
| return (this->EnsureReservedSize(static_cast<uint16_t>(this->ReservedSize() + kPayloadShift))); |
| } |
| |
| /** |
| * Increment the reference count of the current buffer. |
| */ |
| void PacketBuffer::AddRef() |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| pbuf_ref(this); |
| #else // !CHIP_SYSTEM_CONFIG_USE_LWIP |
| LOCK_BUF_POOL(); |
| VerifyOrDieWithMsg(this->ref < std::numeric_limits<decltype(this->ref)>::max(), chipSystemLayer, |
| "packet buffer refcount overflow"); |
| ++this->ref; |
| UNLOCK_BUF_POOL(); |
| #endif // !CHIP_SYSTEM_CONFIG_USE_LWIP |
| } |
| |
| PacketBufferHandle PacketBufferHandle::New(size_t aAvailableSize, uint16_t aReservedSize) |
| { |
| // Sanity check for kStructureSize to ensure that it matches the PacketBuffer size. |
| static_assert(PacketBuffer::kStructureSize == sizeof(PacketBuffer), "PacketBuffer size mismatch"); |
| // Setting a static upper bound on kStructureSize to ensure the summation of all the sizes does not overflow. |
| static_assert(PacketBuffer::kStructureSize <= UINT16_MAX, "kStructureSize should not exceed UINT16_MAX."); |
| // Setting a static upper bound on the maximum buffer size allocation for regular sized messages (not large). |
| static_assert(PacketBuffer::kMaxSizeWithoutReserve <= UINT16_MAX, "kMaxSizeWithoutReserve should not exceed UINT16_MAX."); |
| |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
| // Setting a static upper bound on the maximum buffer size allocation for |
| // large messages. |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| // LwIP based systems are internally limited to using a u16_t type as the size of a buffer. |
| static_assert(PacketBuffer::kLargeBufMaxSizeWithoutReserve <= UINT16_MAX, |
| "In LwIP, max size for Large payload buffers cannot exceed UINT16_MAX!"); |
| #else |
| // Messages over TCP are framed using a length field that is 32 bits in |
| // length. |
| static_assert(PacketBuffer::kLargeBufMaxSizeWithoutReserve <= UINT32_MAX, |
| "Max size for Large payload buffers cannot exceed UINT32_MAX"); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
| |
| // Ensure that aAvailableSize is bound within a max and is not big enough to cause overflow during |
| // subsequent addition of all the sizes. |
| if (aAvailableSize > UINT32_MAX) |
| { |
| ChipLogError(chipSystemLayer, |
| "PacketBuffer: AvailableSize of a buffer cannot exceed UINT32_MAX. aAvailableSize = 0x" ChipLogFormatX64, |
| ChipLogValueX64(static_cast<uint64_t>(aAvailableSize))); |
| return PacketBufferHandle(); |
| } |
| |
| // Cast all to uint64_t and add. This cannot overflow because we have |
| // ensured that the maximal value of the summation is |
| // UINT32_MAX + UINT16_MAX + UINT16_MAX, which should always fit in |
| // a uint64_t variable. |
| uint64_t sumOfSizes = static_cast<uint64_t>(aAvailableSize) + static_cast<uint64_t>(aReservedSize) + |
| static_cast<uint64_t>(PacketBuffer::kStructureSize); |
| uint64_t sumOfAvailAndReserved = static_cast<uint64_t>(aAvailableSize) + static_cast<uint64_t>(aReservedSize); |
| |
| // Ensure that the sum fits in a size_t so that casting into size_t variables, |
| // viz., lBlockSize and lAllocSize, is safe. |
| if (!CanCastTo<size_t>(sumOfSizes)) |
| { |
| ChipLogError(chipSystemLayer, |
| "PacketBuffer: Sizes of allocation request are invalid. (aAvailableSize = " ChipLogFormatX64 |
| ", aReservedSize = " ChipLogFormatX64 ")", |
| ChipLogValueX64(static_cast<uint64_t>(aAvailableSize)), ChipLogValueX64(static_cast<uint64_t>(aReservedSize))); |
| return PacketBufferHandle(); |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| // LwIP based APIs have a maximum buffer size of UINT16_MAX. Ensure that |
| // limit is met during allocation. |
| if (sumOfAvailAndReserved > UINT16_MAX) |
| { |
| ChipLogError(chipSystemLayer, |
| "LwIP based systems require total buffer size to be less than UINT16_MAX!" |
| "Attempted allocation size = " ChipLogFormatX64, |
| ChipLogValueX64(sumOfAvailAndReserved)); |
| return PacketBufferHandle(); |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| // sumOfAvailAndReserved is no larger than sumOfSizes, which we checked can be cast to |
| // size_t. |
| const size_t lAllocSize = static_cast<size_t>(sumOfAvailAndReserved); |
| PacketBuffer * lPacket; |
| |
| CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_PacketBufferNew, return PacketBufferHandle()); |
| |
| if (lAllocSize > PacketBuffer::kMaxAllocSize) |
| { |
| ChipLogError(chipSystemLayer, "PacketBuffer: allocation exceeding buffer capacity limits: %lu > %lu", |
| static_cast<unsigned long>(lAllocSize), static_cast<unsigned long>(PacketBuffer::kMaxAllocSize)); |
| return PacketBufferHandle(); |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| // This cast is safe because lAllocSize is no larger than |
| // kMaxSizeWithoutReserve, which fits in uint16_t. |
| lPacket = static_cast<PacketBuffer *>( |
| pbuf_alloc(PBUF_RAW, static_cast<uint16_t>(lAllocSize), CHIP_SYSTEM_PACKETBUFFER_LWIP_PBUF_TYPE)); |
| |
| SYSTEM_STATS_UPDATE_LWIP_PBUF_COUNTS(); |
| |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL |
| |
| #if !CHIP_SYSTEM_CONFIG_NO_LOCKING && CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING |
| if (!sBufferPoolMutex.isInitialized()) |
| { |
| Mutex::Init(sBufferPoolMutex); |
| } |
| #endif |
| LOCK_BUF_POOL(); |
| |
| lPacket = PacketBuffer::sFreeList; |
| if (lPacket != nullptr) |
| { |
| PacketBuffer::sFreeList = lPacket->ChainedBuffer(); |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kSystemLayer_NumPacketBufs); |
| } |
| |
| UNLOCK_BUF_POOL(); |
| |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| // sumOfSizes is essentially (kStructureSize + lAllocSize) which we already |
| // checked to fit in a size_t. |
| const size_t lBlockSize = static_cast<size_t>(sumOfSizes); |
| lPacket = reinterpret_cast<PacketBuffer *>(chip::Platform::MemoryAlloc(lBlockSize)); |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kSystemLayer_NumPacketBufs); |
| |
| #else |
| #error "Unimplemented PacketBuffer storage case" |
| #endif |
| |
| if (lPacket == nullptr) |
| { |
| ChipLogError(chipSystemLayer, "PacketBuffer: pool EMPTY."); |
| return PacketBufferHandle(); |
| } |
| |
| lPacket->payload = lPacket->ReserveStart() + aReservedSize; |
| lPacket->len = lPacket->tot_len = 0; |
| lPacket->next = nullptr; |
| lPacket->ref = 1; |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| lPacket->alloc_size = lAllocSize; |
| #endif |
| |
| return PacketBufferHandle(lPacket); |
| } |
| |
| PacketBufferHandle PacketBufferHandle::NewWithData(const void * aData, size_t aDataSize, size_t aAdditionalSize, |
| uint16_t aReservedSize) |
| { |
| // Since `aDataSize` fits in uint16_t, the sum `aDataSize + aAdditionalSize` will not overflow. |
| // `New()` will only return a non-null buffer if the total allocation size does not overflow. |
| PacketBufferHandle buffer = New(aDataSize + aAdditionalSize, aReservedSize); |
| if (buffer.mBuffer != nullptr) |
| { |
| memcpy(buffer.mBuffer->payload, aData, aDataSize); |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| // Checks in the New() call catch buffer allocations greater |
| // than UINT16_MAX for LwIP based platforms. |
| buffer.mBuffer->len = buffer.mBuffer->tot_len = static_cast<uint16_t>(aDataSize); |
| #else |
| buffer.mBuffer->len = buffer.mBuffer->tot_len = aDataSize; |
| #endif |
| } |
| return buffer; |
| } |
| |
| /** |
| * Free all packet buffers in a chain. |
| * |
| * Decrement the reference count to all the buffers in the current chain. If the reference count reaches 0, the respective buffers |
| * are freed or returned to allocation pools as appropriate. As a rule, users should treat this method as an equivalent of |
| * `free()` function and not use the argument after the call. |
| * |
| * @param[in] aPacket - packet buffer to be freed. |
| */ |
| void PacketBuffer::Free(PacketBuffer * aPacket) |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| if (aPacket != nullptr) |
| { |
| pbuf_free(aPacket); |
| |
| SYSTEM_STATS_UPDATE_LWIP_PBUF_COUNTS(); |
| } |
| |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP || CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL |
| |
| LOCK_BUF_POOL(); |
| |
| while (aPacket != nullptr) |
| { |
| PacketBuffer * lNextPacket = aPacket->ChainedBuffer(); |
| |
| VerifyOrDieWithMsg(aPacket->ref > 0, chipSystemLayer, "SystemPacketBuffer::Free: aPacket->ref = 0"); |
| |
| aPacket->ref--; |
| if (aPacket->ref == 0) |
| { |
| SYSTEM_STATS_DECREMENT(chip::System::Stats::kSystemLayer_NumPacketBufs); |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| ::chip::Platform::MemoryDebugCheckPointer(aPacket, aPacket->alloc_size + kStructureSize); |
| #endif |
| aPacket->Clear(); |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL |
| aPacket->next = sFreeList; |
| sFreeList = aPacket; |
| #elif CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| chip::Platform::MemoryFree(aPacket); |
| #endif |
| aPacket = lNextPacket; |
| } |
| else |
| { |
| aPacket = nullptr; |
| } |
| } |
| |
| UNLOCK_BUF_POOL(); |
| |
| #else |
| #error "Unimplemented PacketBuffer storage case" |
| #endif |
| } |
| |
| /** |
| * Clear content of the packet buffer. |
| * |
| * This method is called by Free(), before the buffer is released to the free buffer pool. |
| */ |
| void PacketBuffer::Clear() |
| { |
| tot_len = 0; |
| len = 0; |
| #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP |
| alloc_size = 0; |
| #endif |
| } |
| |
| /** |
| * Free the first buffer in a chain, returning a pointer to the remaining buffers. |
| `* |
| * @note When the buffer chain is referenced by multiple callers, `FreeHead()` will detach the head, but will not forcibly |
| * deallocate the head buffer. |
| * |
| * @param[in] aPacket - buffer chain. |
| * |
| * @return packet buffer chain consisting of the tail of the input buffer (may be \c nullptr). |
| */ |
| PacketBuffer * PacketBuffer::FreeHead(PacketBuffer * aPacket) |
| { |
| PacketBuffer * lNextPacket = aPacket->ChainedBuffer(); |
| aPacket->next = nullptr; |
| PacketBuffer::Free(aPacket); |
| return lNextPacket; |
| } |
| |
| PacketBufferHandle PacketBufferHandle::PopHead() |
| { |
| PacketBuffer * head = mBuffer; |
| |
| // This takes ownership from the `next` link. |
| mBuffer = mBuffer->ChainedBuffer(); |
| |
| head->next = nullptr; |
| head->tot_len = head->len; |
| |
| // The returned handle takes ownership from this. |
| return PacketBufferHandle(head); |
| } |
| |
| PacketBufferHandle PacketBufferHandle::CloneData() const |
| { |
| PacketBufferHandle cloneHead; |
| |
| for (PacketBuffer * original = mBuffer; original != nullptr; original = original->ChainedBuffer()) |
| { |
| size_t originalDataSize = original->MaxDataLength(); |
| uint16_t originalReservedSize = original->ReservedSize(); |
| |
| if (originalDataSize + originalReservedSize > PacketBuffer::kMaxAllocSize) |
| { |
| // The original memory allocation may have provided a larger block than requested (e.g. when using a shared pool), |
| // and in particular may have provided a larger block than we are able to request from PackBufferHandle::New(). |
| // It is a genuine error if that extra space has been used. |
| if (originalReservedSize + original->DataLength() > PacketBuffer::kMaxAllocSize) |
| { |
| return PacketBufferHandle(); |
| } |
| // Otherwise, reduce the requested data size. This subtraction can not underflow because the above test |
| // guarantees originalReservedSize <= PacketBuffer::kMaxAllocSize. |
| originalDataSize = PacketBuffer::kMaxAllocSize - originalReservedSize; |
| } |
| |
| PacketBufferHandle clone = PacketBufferHandle::New(originalDataSize, originalReservedSize); |
| if (clone.IsNull()) |
| { |
| return PacketBufferHandle(); |
| } |
| clone.mBuffer->tot_len = clone.mBuffer->len = original->len; |
| memcpy(clone->ReserveStart(), original->ReserveStart(), originalDataSize + originalReservedSize); |
| |
| if (cloneHead.IsNull()) |
| { |
| cloneHead = std::move(clone); |
| } |
| else |
| { |
| cloneHead->AddToEnd(std::move(clone)); |
| } |
| } |
| |
| return cloneHead; |
| } |
| |
| } // namespace System |
| |
| namespace Encoding { |
| |
| System::PacketBufferHandle PacketBufferWriterUtil::Finalize(BufferWriter & aBufferWriter, System::PacketBufferHandle & aPacket) |
| { |
| if (!aPacket.IsNull() && aBufferWriter.Fit()) |
| { |
| // Since mPacket was successfully allocated to hold the maximum length, |
| // we know that the actual length fits in a uint16_t. |
| aPacket->SetDataLength(aBufferWriter.Needed()); |
| } |
| else |
| { |
| aPacket = nullptr; |
| } |
| aBufferWriter = Encoding::BufferWriter(nullptr, 0); |
| return std::move(aPacket); |
| } |
| |
| } // namespace Encoding |
| } // namespace chip |