/** * Copyright (C) 2021-2024 Saturneric * * This file is part of GpgFrontend. * * GpgFrontend is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GpgFrontend is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GpgFrontend. If not, see . * * The initial version of the source code is inherited from * the gpg4usb project, which is under GPL-3.0-or-later. * * All the source code of GpgFrontend was modified and released by * Saturneric starting on May 12, 2021. * * SPDX-License-Identifier: GPL-3.0-or-later * */ #pragma once #include #include #include #include #include #include #include /** * @file GFModuleMemory.h * @brief Who frees what, and the helpers that make it obvious. * * ## The rule, in one place * * **SDK arguments are borrowed.** Pass a pointer straight through -- * `s.toUtf8().constData()` for a QString -- and keep owning it. Do *not* * pre-allocate one to hand over: nothing on the other side will free it. That * is what the old `DUP()`/`QDUP()` at call sites was for, and why doing it now * simply leaks. * * The one exception is a struct rather than an argument: the few aggregates a * module builds and hands over whole -- `GFModuleEvent`, `GFModuleEventParam`, * `GFModuleMetaData`, `GFCommandExecuteContext` -- still transfer, so their * `char*` members are still allocated with GFModuleStrDup. * * **SDK return values are owned.** Reclaim them, which is what the `U...` * family below is for. */ /// Allocate a copy of @p v that a transferring struct will own. #define DUP(v) GFModuleStrDup(v) /// The same, from the wiping allocator, for a value that is a secret. #define SECDUP(v) GFModuleSecStrDup(v) /// Take ownership of an SDK string and return it as a QString. #define UDUP(v) UnStrDup(v) /// The same, releasing it back to the wiping allocator. #define USECDUP(v) UnSecStrDup(v) /// Take ownership of a sized SDK buffer and return its exact octets. #define UDUPN(v, n) UnBytesDup(v, n) /// Takes ownership of a sized SDK buffer and returns its octets verbatim. /// /// The QString forms below decode UTF-8 and stop at the first NUL, which is /// right for identifiers and error strings and wrong for message data: a MIME /// entity may be 8bit or binary, and a signature covers exact octets. Anything /// that crosses the crypto boundary must use this and the matching @c *N SDK /// entry points instead. inline auto UnBytesDup(const char* s, size_t size) -> QByteArray { if (s == nullptr) return {}; QByteArray bytes(s, static_cast(size)); GFFreeMemory(static_cast(const_cast(s))); return bytes; } inline auto UnStrDup(const char* s) -> QString { auto q_s = QString::fromUtf8(s == nullptr ? "" : s); if (s != nullptr) GFFreeMemory(static_cast(const_cast(s))); return q_s; } inline auto UnSecStrDup(const char* s) -> QString { auto q_s = QString::fromUtf8(s == nullptr ? "" : s); if (s != nullptr) GFSecFreeMemory(static_cast(const_cast(s))); return q_s; } inline auto QStrDup(const QString& str) -> char* { return DUP(str.toUtf8()); } inline auto QSecStrDup(const QString& str) -> char* { return SECDUP(str.toUtf8()); } /// Copies @p b into an SDK buffer, NUL-terminated one past the end. /// /// The terminator is not optional politeness: the buffer is handed to the /// host, and a caller that treats it as a C string would otherwise read past /// the allocation. @p b .size() stays authoritative for the byte count. inline auto AllocBufferAndCopy(const QByteArray& b) -> char* { auto* p = static_cast(GFAllocateMemory(sizeof(char) * (b.size() + 1))); if (p == nullptr) return nullptr; memcpy(p, b.constData(), b.size()); p[b.size()] = '\0'; return p; } template class PointerConverter { public: explicit PointerConverter(void* ptr) : ptr_(ptr) {} auto AsType() const -> T* { return static_cast(ptr_); } private: void* ptr_; }; /** * @brief Construct a T in SDK-allocated memory, freed through the SDK. * * NAMING. These four used to be called `Secure...`, which said the wrong * thing: they allocate from the ORDINARY SDK arena (GFAllocateMemory), not the * wiping one (GFSecAllocateMemory). The old names had a comment saying so, * which is the weakest possible place to put it -- a name is read every time * and a comment once. For anything that must actually be erased, use the * secure entry points explicitly or a GFBuf handle. */ template auto SdkCreateSharedObject(Args&&... args) -> std::shared_ptr { void* mem = GFAllocateMemory(sizeof(T)); if (!mem) throw std::bad_alloc(); try { T* obj = new (mem) T(std::forward(args)...); return std::shared_ptr(obj, [](T* ptr) { ptr->~T(); GFFreeMemory(ptr); }); } catch (...) { GFFreeMemory(mem); throw; } } template auto SdkCreateQSharedObject(Args&&... args) -> QSharedPointer { void* mem = GFAllocateMemory(sizeof(T)); if (!mem) throw std::bad_alloc(); try { T* obj = new (mem) T(std::forward(args)...); return QSharedPointer(obj, [](T* ptr) { ptr->~T(); GFFreeMemory(ptr); }); } catch (...) { GFFreeMemory(mem); throw; } } /// Allocate @p size bytes from the ordinary SDK arena, typed. template auto SdkMallocAsType(std::size_t size) -> T* { return PointerConverter(GFAllocateMemory(size)).AsType(); } /// Resize an SDK allocation, typed. template auto SdkReallocAsType(T* ptr, std::size_t size) -> T* { return PointerConverter(GFReallocateMemory(ptr, size)).AsType(); } // Deprecated spellings, kept for one cycle so a module can be ported without // being rewritten. They allocate from the ordinary arena, exactly as the new // names say and the old ones did not. template [[deprecated("renamed to SdkCreateSharedObject: it is not a secure allocation")]] auto SecureCreateSharedObject(Args&&... args) -> std::shared_ptr { return SdkCreateSharedObject(std::forward(args)...); } template [[deprecated("renamed to SdkCreateQSharedObject: it is not a secure allocation")]] auto SecureCreateQSharedObject(Args&&... args) -> QSharedPointer { return SdkCreateQSharedObject(std::forward(args)...); } template [[deprecated("renamed to SdkMallocAsType: it is not a secure allocation")]] auto SecureMallocAsType(std::size_t size) -> T* { return SdkMallocAsType(size); } template [[deprecated("renamed to SdkReallocAsType: it is not a secure allocation")]] auto SecureReallocAsType(T* ptr, std::size_t size) -> T* { return SdkReallocAsType(ptr, size); }