// Minimal reproducer for Qt 6 Wayland screen use-after-free. // // Bug: libwayland resolves wl_output proxy pointers at demarshal time (when // events are read from the socket). If wl_registry.global_remove and // wl_surface.enter/leave for the same output arrive in the same dispatch // batch, the surface event handler receives a dangling proxy pointer. // QWaylandScreen::fromWlOutput() reads stale userdata from the freed proxy // and returns a garbage QWaylandScreen*, which crashes on dereference. // // Build: // cmake -B build && cmake --build build // // Run (on any wlroots-based compositor or Niri): // ./build/screen-uaf-reproducer & // # then toggle an output off/on rapidly, e.g.: // # wlr-randr --output eDP-1 --off && wlr-randr --output eDP-1 --on // # niri msg action power-off-monitors (then move mouse to wake) // # or just unplug/replug an external monitor // // The crash typically occurs within seconds of the output toggle. // Creating many surfaces increases the odds that a surface.leave event // lands in the same batch as the global_remove. #include #include #include #include #include #include int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // Many surfaces = higher chance of batched enter/leave events constexpr int kWindows = 20; std::vector> windows; windows.reserve(kWindows); for (int i = 0; i < kWindows; ++i) { auto w = std::make_unique(); w->setTitle(QStringLiteral("uaf-%1").arg(i)); w->resize(1, 1); w->setFlag(Qt::ToolTip); // small, no decoration, no focus w->show(); windows.push_back(std::move(w)); } std::fprintf(stderr, "screen-uaf-reproducer: %d surfaces created on %lld screen(s).\n" "Toggle an output off/on to trigger the crash.\n", kWindows, QGuiApplication::screens().size()); // Log screen changes so we know when the toggle happens QObject::connect(&app, &QGuiApplication::screenAdded, [](QScreen *s) { std::fprintf(stderr, " screenAdded: %s\n", qPrintable(s->name())); }); QObject::connect(&app, &QGuiApplication::screenRemoved, [](QScreen *s) { std::fprintf(stderr, " screenRemoved: %s\n", qPrintable(s->name())); }); return app.exec(); }