<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/94193>94193</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[clang-tidy] `std::addressof` not checked by `clang-analyzer-core.StackAddressEscape`
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang-tidy
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
dfrvfmst
</td>
</tr>
</table>
<pre>
The code examples are all in C++23 (`-std=c++23`) and libc++ (`-stdlib=libc++ -fexperimental-library`).
---
Given a basic example:
```cpp
#include <iostream>
#include <memory>
#include <string>
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`:
```cpp
template <class _Tp>
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`:
```cpp
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`:
```cpp
#include <iostream>
#include <ranges>
#include <vector>
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`:
```cpp
#include <iostream>
#include <functional>
#include <string>
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`:
```cpp
template<typename T>
struct Ref
{
Ref(T &ref)
: ptr(&ref)
{}
auto &get(this auto &&self) { return *self.ptr; }
private:
T *ptr;
};
```
Faulty code utilizing `Ref`:
```cpp
#include <iostream>
#include <string>
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`.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzEWN1u2zwSfRr6ZmBDpmzJvvCF_7IbYL-2SAJs7wyaGtnc0qRAUnHcp1-QVGw5UdJm2-4HBI7En-HM8MzhoZi1YqcQZ2S8IONVj9Vur82sKM1jebCut9XFafawR-C6QMAndqgkWmAGgUkJQsGS0AWhC5oCoROSJX3rCpKu-HMzyRJCp8BUAVJsm-bWWCm2JF21uvolPlVoxAGVY7IvxdYwc4pmBiRZkWQef_v9fvv1H-IRFTDYMiv4s6sknbfHeCvhj1dV00JTobisCwSSLoW2ziA7kHTd1X3AgzanNzqtM0LtLp3hl9VOQzn04dJp05Ev4gMAQEjWnKTzOBsYkHQFhNIhTQmlJG2NNehqoy5TWFEYtFaXhE6YN_88mOSrtg9COTgwobqdCB4atLV0Ye2zs2mXm1zXftiSpEsgdB7ndSz8nOe2H7cOhFWE5g44q3d7B9sT-L2QTO36ThRxk5cBK0JZh6yA-T1TwJnje7QgnEecqZUThze3tnlNVyRd0TxJhzQ-r-_uPt-RdA7zmLd7poQT39H4NusY_9avLfZZ6dD0LdcVglbQJBmSp7wcD_M8S5KEJsAcVBySp_E458gzWpZlBtsqDCt5SdNpwbcJ2OsWNnknPQ97YUFYOKIwhU_DLgDa7RFKxh24PXM-XZvNthbSCbW5ACBL_Ey-R_4Ni660hpx6nDiQyGzII8mSS9k1ZUqypANf0bwVh0qegMHRsKpCEz3iTEr7tl9XFbvCUvikawW6fHOtH9asw0MlmQtVxyWzFjYP1bnwhJJCIWz-dbtYfvmyWX7-dP-w_vrlbnN_-2m53iy_fh3m595PnzfLm9vz6z9vV-vNzd3nvzbzxW2wSufQrrPQlMFm8-Sztfn0ef11uf7yAK2ibsq0Kx10Eif-ZMH8e48K-J6pnacGD4SG1cBpqC2-nfUfpvAP8FJ3wG8QU1e4rzArLLBtjLYUu9ogePYJqBOO0Nw2S0cHC6Z20j9WWigXyvodfvCEOPDpSOfpxDMAGqNNix48QgMrQGR9YNZqLpjDAo7C7UFqziQ8MiOCk4TmjNC8cQkL77WvDTRAxosYGFNMnr6j6XNtcHDvrTerrS1nFRK67B9ZCMj2me0HnywZv5e1F6dgwyK2RjiIwLEIx1intcXiqugMlmhQcdw0FX0mivYgpnZozxM2jwKPfpzT3nStGtLxhGWF4j4RE6FaZ70vFLfHEzBpA2yLd1gm8J0wwLWyztTcaWOvKGTdVIAuoWS1dKeoTGonpPjud_8nfP-9ouB5kc7OR_QhdIoC-uPii7OBkXwx9Amm_if1VdRRfwxIvoTO2H3czWMppK8NOomCz1eo94bQjNBsE46AfHEuaSDpGoYkXYBf9H-VGACvVQbtUBmlNnDtkM_hvJnX-NYM71QkeGXu4uPHaKfQeJYpju9BuMErGRL160vVENPWIVU_htqOsrxA9tfAWtaK-_OXyY-r2PSPqFiDv6Zfu8CVvgRXJ1rijMEO3Yvhfz9eXpD6X9oghEMNrRNqJ0-eCEQJJ12DV2XoL0v-zYA-Kjgj6FqqHbX5FmSckMx4AveSQitvun03257AOh03MpKzX-1V2JwpKNBhkKbYOop1-fospjcXn5gFqb1x-57cjBkNx4VQ7fPgfUV5hx-SkCRdulOFih0QHs6oj0uBt_UabXcBsA-eoQJ2p5euwDrpHCoXGPb1AG_oGVxn5BKaRQw6f3a32M-iLF8wMqFz3zrwKzSsHIxVRjyGcOYX497FeRx4wfX5uQvaN29x009m9UNU9A7TjH4n03jf8wW7Cv5XCWb0fyWYC4_8tKalv1XT-sufE1JGYjFR3G5Poe6jvfaU5-M6h7rSqkUMDeNEVTwACFr16O1usS3gL1TxR5Xzonae2I7-luWJNNy0MLp4oZsrXmldvDo_wzT1Hj9jdDKm0oH80Dxzp2icEHzvr-aF9WscvBtCfXu-93FmMe7Q79LvVyzaK2ZpMU2nrIezYT7MR0k6Tqa9_aycZMgKzAuWjMucpmmCWxxjPh2PsoKmWU_MaEJHSZakySQdjiaDKZ-Uk3GGwxHjjPIxGSV4YEIOpHw8DLTZ9cLlZDYdDadpT7ItShs-PlLaShalZLzqmZmf1N_WO0tGiRTW2YsZJ5wMny1b08art08UpV3nB5IfIitLerWRs71zVUgjvSH0Zifcvt4OuD4QeuN9av71K6P_g9wRehPitITexFAfZ_S_AQAA___ickJG">