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
  • [Bug c++/127328] New: Acces... ivan.lazaric.gcc at gmail dot com via Gcc-bugs

Reply via email to