https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127328
Bug ID: 127328
Summary: Accessing out-of-scope variables via reflection
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: ivan.lazaric.gcc at gmail dot com
Target Milestone: ---
We can create a reflection of a variable,
leak it out of scope of the variable,
and splice it.
I give two examples, they do the same thing,
they just differ in how they leak the reflection.
One goes through `define_aggregate()`,
while other goes though good old friend injection.
```cpp
// via define_aggregate()
#include <meta>
#include <print>
#include <string>
#include <string_view>
#include <vector>
template<auto>
struct storage {};
consteval void store(std::meta::info type, std::meta::info value) {
auto wrapped = std::meta::reflect_constant(value);
auto stored = substitute((^^storage), {wrapped});
auto spec = data_member_spec(stored, {.name = "storage"});
define_aggregate(type, {spec});
}
consteval std::meta::info load(std::meta::info type) {
auto stored = nonstatic_data_members_of(type,
std::meta::access_context::unchecked())[0];
auto wrapped = template_arguments_of(type_of(stored))[0];
auto value = extract<std::meta::info>(wrapped);
return value;
}
std::string_view bytes_of(const auto& arg) { //
return std::string_view((const char*)&arg, (const char*)(&arg + 1));
}
void access_out_of_scope_static() {
struct S;
if (0) {
static std::vector<std::string> x = {"hello", " ", "world", "\n"};
consteval { store(^^S, ^^x); }
}
std::println("{} -- {}", __func__, (const void*)&[:load(^^S):]);
std::println("{} -- {:?}", __func__, bytes_of([:load(^^S):]));
}
void access_out_of_scope_automatic() {
struct S;
if (0) {
long x = 1;
static constexpr auto r = ^^x;
consteval { store(^^S, r); }
}
std::println("{} -- {} {}", __func__, (const void*)&[:load(^^S):],
[:load(^^S):]);
[:load(^^S):] = {};
std::println("{} -- {} {}", __func__, (const void*)&[:load(^^S):],
[:load(^^S):]);
}
int main() {
access_out_of_scope_static();
access_out_of_scope_automatic();
}
```
```cpp
// via friend injection
#include <meta>
#include <print>
#include <string>
#include <string_view>
#include <vector>
template<typename T>
struct A {
friend consteval auto fn(A);
};
template<typename T, auto V>
struct B {
friend consteval auto fn(A<T>) { return V; }
};
std::string_view bytes_of(const auto& arg) { //
return std::string_view((const char*)&arg, (const char*)(&arg + 1));
}
void access_out_of_scope_static() {
if (0) {
static std::vector<std::string> x = {"hello", " ", "world", "\n"};
consteval { size_of(substitute((^^B), {(^^int),
std::meta::reflect_constant(^^x)})); }
}
std::println("{} -- {}", __func__, (const void*)&[:fn(A<int>{}):]);
std::println("{} -- {:?}", __func__, bytes_of([:fn(A<int>{}):]));
}
void access_out_of_scope_automatic() {
if (0) {
long x = 1;
static constexpr auto r = ^^x;
consteval { size_of(substitute((^^B), {(^^char),
std::meta::reflect_constant(r)})); }
}
std::println("{} -- {} {}", __func__, (const void*)&[:fn(A<char>{}):],
[:fn(A<char>{}):]);
[:fn(A<char>{}):] = {};
std::println("{} -- {} {}", __func__, (const void*)&[:fn(A<char>{}):],
[:fn(A<char>{}):]);
}
int main() {
access_out_of_scope_static();
access_out_of_scope_automatic();
}
```
Flags: "-std=c++26 -freflection -Wno-non-template-friend -O3"
Godbolts:
https://godbolt.org/z/qb6KxsYjM
https://godbolt.org/z/886E1afbs
Both snippets compile.
Probably should be ill-formed.
I am not very fluent in relevant wording here though.
Probably relevant: https://eel.is/c++draft/expr.prim.splice#2.5