/* * Copyright (C) 2020 The Android Open Source Project * * 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. */ #pragma once #include #include #include #include namespace android::nl { /** * Buffer wrapper containing netlink structure (e.g. nlmsghdr, nlattr). * * This is a C++-style, memory safe(r) and generic implementation of linux/netlink.h macros. * * While netlink structures contain information about their total length (with payload), they can * not be trusted - the value may either be larger than the buffer message is allocated in or * smaller than the header itself (so it couldn't even fit itself). * * As a solution, Buffer<> keeps track of two lengths (both attribute for header with payload): * - buffer length - how much memory was allocated to a given structure * - declared length - what nlmsg_len or nla_len says how long the structure is * * In most cases buffer length would be larger than declared length (or equal - modulo alignment - * for continuous data). If that's not the case, there is a potential of ouf-of-bounds read which * this template attempts to protect against. */ template class Buffer { public: /** * Constructs empty buffer of size 0. */ Buffer() : mData(nullptr), mBufferEnd(nullptr) {} /** * Buffer constructor. * * \param data A pointer to the data the Buffer wraps. * \param bufLen Length of the buffer. */ Buffer(const T* data, size_t bufLen) : mData(data), mBufferEnd(pointerAdd(data, bufLen)) {} const T* operator->() const { CHECK(firstOk()) << "buffer can't fit the first element's header"; return mData; } std::pair getFirst() const { if (!ok()) { static const T empty = {}; return {false, empty}; } return {true, *mData}; } /** * Copy the first element of the buffer. * * This is a memory-safe cast operation, useful for reading e.g. uint32_t values * from 1-byte buffer. If the buffer is smaller than the copied type, the rest is * padded with default constructor output (usually zeros). */ T copyFirst() const { T val = {}; memcpy(&val, mData, std::min(sizeof(val), remainingLength())); return val; } bool firstOk() const { return sizeof(T) <= remainingLength(); } template const Buffer data(size_t offset = 0) const { return {impl::data(mData, offset), dataEnd()}; } template std::optional getOffset(Buffer inner) const { const auto selfStart = uintptr_t(mData); const auto selfEnd = uintptr_t(mBufferEnd); const auto innerStart = uintptr_t(inner.mData); const auto innerEnd = uintptr_t(inner.mBufferEnd); if (innerStart < selfStart || innerEnd > selfEnd) return std::nullopt; return innerStart - selfStart; } class iterator { public: iterator() : mCurrent(nullptr, size_t(0)) { CHECK(isEnd()) << "end() iterator should indicate it's beyond end"; } iterator(const Buffer& buf) : mCurrent(buf) {} iterator operator++() { // mBufferEnd stays the same mCurrent.mData = reinterpret_cast( // uintptr_t(mCurrent.mData) + impl::align(mCurrent.declaredLength())); return *this; } bool operator==(const iterator& other) const { // all iterators beyond end are the same if (isEnd() && other.isEnd()) return true; return uintptr_t(other.mCurrent.mData) == uintptr_t(mCurrent.mData); } const Buffer& operator*() const { return mCurrent; } bool isEnd() const { return !mCurrent.ok(); } protected: Buffer mCurrent; }; iterator begin() const { return {*this}; } iterator end() const { return {}; } class raw_iterator : public iterator { public: iterator operator++() { this->mCurrent.mData++; // ignore alignment return *this; } const T& operator*() const { return *this->mCurrent.mData; } }; class raw_view { public: raw_view(const Buffer& buffer) : mBuffer(buffer) {} raw_iterator begin() const { return {mBuffer}; } raw_iterator end() const { return {}; } const T* ptr() const { return mBuffer.mData; } size_t len() const { return mBuffer.remainingLength(); } private: const Buffer mBuffer; }; raw_view getRaw() const { return {*this}; } private: const T* mData; const void* mBufferEnd; Buffer(const T* data, const void* bufferEnd) : mData(data), mBufferEnd(bufferEnd) {} bool ok() const { return declaredLength() <= remainingLength(); } // to be specialized individually for each T with payload after a header inline size_t declaredLengthImpl() const { return sizeof(T); } size_t declaredLength() const { // We can't even fit a header, so let's return some absurd high value to trip off // buffer overflow checks. static constexpr size_t badHeaderLength = std::numeric_limits::max() / 2; if (sizeof(T) > remainingLength()) return badHeaderLength; const auto len = declaredLengthImpl(); if (sizeof(T) > len) return badHeaderLength; return len; } size_t remainingLength() const { auto len = intptr_t(mBufferEnd) - intptr_t(mData); return (len >= 0) ? len : 0; } const void* dataEnd() const { auto declaredEnd = pointerAdd(mData, declaredLength()); return std::min(declaredEnd, mBufferEnd); } static const void* pointerAdd(const void* ptr, size_t len) { return reinterpret_cast(uintptr_t(ptr) + len); } template friend class Buffer; // calling private constructor of data buffers }; template <> inline size_t Buffer::declaredLengthImpl() const { return mData->nlmsg_len; } template <> inline size_t Buffer::declaredLengthImpl() const { return mData->nla_len; } } // namespace android::nl