[clang-tidy] std::addressof not checked by clang-analyzer-core.StackAddressEscape (original) (raw)
The code examples are all in C++23 (-std=c++23) and libc++ (-stdlib=libc++ -fexperimental-library).
Given a basic example:
#include #include #include
auto f1() { std::string a = "123"; return std::addressof(a); }
int main() { auto result = f1(); std::cout << *result; }
It isn't caught by clang-tidy, and instead ASan catches it in runtime:
==270312==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7f5177600020 at pc 0x557cec62fff6 bp 0x7ffcf239dcb0 sp 0x7ffcf239dca8
This is weird, given the fact that __builtin_addressof is checked by clang-tidy and (at least in libc++) std::addressof is simply a wrapper that calls __builtin_addressof.
Definition of std::addressof:
template inline _LIBCPP_CONSTEXPR_SINCE_CXX17 _LIBCPP_NO_CFI _LIBCPP_HIDE_FROM_ABI _Tp* addressof(_Tp& __x) _NOEXCEPT { return __builtin_addressof(__x); }
When changing the example to use __builtin_addressof:
auto f1() { std::string a = "123"; return __builtin_addressof(a); }
clang-tidy is able to figure out that it's returning a dangling pointer:
main.cpp:38: error: Address of stack memory associated with local variable 'a' returned to caller [clang-analyzer-core.StackAddressEscape,-warnings-as-errors]
This issue might be what caused std::reference_wrapper and std::ranges::ref_view to be unchecked, since (in libc++) they also used std::addressof in their constructors.
Example of faulty code utilizing std::ranges::ref_view:
#include #include #include
auto f2() { std::vector a{1, 2, 3}; return a | std::ranges::views::filter([](auto &&_) { return _ > 1; }); }
int main() { auto result = f2(); for (auto &&e : result) { std::cout << e; } }
clang-tidy doesn't catch it. ASan catches it (stack-use-after-return).
Example of faulty code utilizing std::reference_wrapper:
#include #include #include
auto f3() { std::string a = "123"; return std::ref(a); }
int main() { auto result = f3(); std::cout << result.get(); }
clang-tidy doesn't catch it. ASan catches it (stack-use-after-return).
More interestingly, if you implement your own reference wrapper that works similar to the one in libc++ by storing address, clang-tidy can detect the returning of dangling pointer/reference as long as std::addressof isn't used in constructor.
Definition of Ref:
template struct Ref { Ref(T &ref) : ptr(&ref) {} auto &get(this auto &&self) { return *self.ptr; } private: T *ptr; };
Faulty code utilizing Ref:
#include #include
auto f4() { std::string a = "123"; return Ref{a}; }
int main() { auto result = f4(); std::cout << result.get(); }
clang-tidy catches it:
main.cpp:28: error: Address of stack memory associated with local variable 'a' is still referred to by the stack variable 'result' upon returning to the caller. This will be a dangling reference [clang-analyzer-core.StackAddressEscape,-warnings-as-errors]
But, when you change the constructor of Ref to use std::addressof(ref), clang-tidy can no longer detect it, which leads to me thinking the case with std::reference_wrapper and std::ranges::ref_view.
Version:
> clang-tidy-18 --version
Debian LLVM version 18.1.6
Optimized build.
> clang++-18 --version
Debian clang version 18.1.6 (++20240518023138+1118c2e05e67-1~exp1~20240518143226.133)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin