From 83424db741b154bd13ba72952e1134fc3da5ddbe Mon Sep 17 00:00:00 2001 From: Jonathan Hui Date: Thu, 23 Jul 2026 12:58:51 -0700 Subject: [PATCH] [common] add Clang lifetime safety macros and annotate Heap::String (#13381) This commit introduces initial support for Clang's Lifetime Safety Analysis across the OpenThread codebase: 1. Defines abstraction macros in `toolchain.h`: - `OT_LIFETIME_BOUND` (`[[clang::lifetimebound]]`) - `OT_NOESCAPE` (`[[clang::noescape]]`) - `OT_GSL_OWNER` (`[[gsl::Owner]]`) - `OT_GSL_POINTER` (`[[gsl::Pointer]]`) These macros expand to Clang attributes under C++ LLVM builds and gracefully degrade to no-ops or GNU attributes elsewhere. 2. Enables Clang lifetime warning flags in `CMakeLists.txt`: - `-Wdangling` and `-Wdangling-gsl` - `-Wdangling-assignment` and `-Wdangling-field` - `-Wreturn-stack-address` 3. Annotates `Heap::String` (`heap_string.hpp`): - Marks class `String` with `OT_GSL_OWNER`. - Marks `AsCString()` and `Move()` with `OT_LIFETIME_BOUND` to ensure returned C string pointers do not outlive the owner. --- CMakeLists.txt | 2 +- include/openthread/instance.h | 2 +- include/openthread/platform/toolchain.h | 51 +++++++++++++++++++++++++ src/core/common/heap_string.hpp | 6 +-- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c9250bc..c2a1435ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,7 +99,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang") set(OT_CFLAGS $<$:${OT_CFLAGS} -Wall -Wformat-nonliteral -Wextra -Wshadow> $<$:${OT_CFLAGS} -Wall -Wformat-nonliteral -Wextra -Wshadow -Wno-c++14-compat -fno-exceptions> - $<$:-Wc99-extensions> + $<$:-Wc99-extensions -Wno-unknown-warning-option -Wdangling -Wdangling-gsl -Wdangling-assignment -Wdangling-field -Wreturn-stack-address> ) endif() diff --git a/include/openthread/instance.h b/include/openthread/instance.h index eae225a80..72b0926d2 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (613) +#define OPENTHREAD_API_VERSION (614) /** * @addtogroup api-instance diff --git a/include/openthread/platform/toolchain.h b/include/openthread/platform/toolchain.h index 0084f9f92..28cbbd659 100644 --- a/include/openthread/platform/toolchain.h +++ b/include/openthread/platform/toolchain.h @@ -310,6 +310,57 @@ extern "C" { #endif +/** + * @def OT_LIFETIME_BOUND + * + * Compiler-specific indication that a function or method return value's lifetime is bound to a parameter or `this`. + */ + +/** + * @def OT_NOESCAPE + * + * Compiler-specific indication that a pointer or reference parameter does not escape the function scope. + */ + +/** + * @def OT_GSL_OWNER + * + * Compiler-specific indication that a class or struct is a resource owner for lifetime safety analysis. + */ + +/** + * @def OT_GSL_POINTER + * + * Compiler-specific indication that a class or struct is a non-owning view or pointer for lifetime safety analysis. + */ +#if defined(__cplusplus) && defined(__clang__) && defined(__has_cpp_attribute) +#if __has_cpp_attribute(clang::lifetimebound) +#define OT_LIFETIME_BOUND [[clang::lifetimebound]] +#endif +#if __has_cpp_attribute(clang::noescape) +#define OT_NOESCAPE [[clang::noescape]] +#endif +#if __has_cpp_attribute(gsl::Owner) +#define OT_GSL_OWNER [[gsl::Owner]] +#endif +#if __has_cpp_attribute(gsl::Pointer) +#define OT_GSL_POINTER [[gsl::Pointer]] +#endif +#endif + +#ifndef OT_LIFETIME_BOUND +#define OT_LIFETIME_BOUND +#endif +#ifndef OT_NOESCAPE +#define OT_NOESCAPE +#endif +#ifndef OT_GSL_OWNER +#define OT_GSL_OWNER +#endif +#ifndef OT_GSL_POINTER +#define OT_GSL_POINTER +#endif + /** * @} */ diff --git a/src/core/common/heap_string.hpp b/src/core/common/heap_string.hpp index 0a019e50a..a5f60329b 100644 --- a/src/core/common/heap_string.hpp +++ b/src/core/common/heap_string.hpp @@ -50,7 +50,7 @@ namespace Heap { * be reused and/or freed and reallocated when the string is set. The `Heap::String` destructor will always free the * allocated buffer. */ -class String : public Unequatable +class OT_GSL_OWNER String : public Unequatable { public: /** @@ -92,7 +92,7 @@ public: * * @returns A pointer to C string buffer or `nullptr` if the `String` is null (never set or freed). */ - const char *AsCString(void) const { return mStringBuffer; } + const char *AsCString(void) const OT_LIFETIME_BOUND { return mStringBuffer; } /** * Sets the string from a given C string. @@ -132,7 +132,7 @@ public: * * @returns An rvalue reference to this `String`. */ - String &&Move(void) { return static_cast(*this); } + String &&Move(void) OT_LIFETIME_BOUND { return static_cast(*this); } /** * Frees any buffer allocated by the `String`.