replace reproducer with nova-shell-unpatched flake output for A/B testing
This commit is contained in:
parent
c26e79458a
commit
90c3bd3da2
3 changed files with 7 additions and 110 deletions
16
flake.nix
16
flake.nix
|
|
@ -89,21 +89,19 @@
|
||||||
docs = pkgs.callPackage ./nix/docs.nix { inherit self; };
|
docs = pkgs.callPackage ./nix/docs.nix { inherit self; };
|
||||||
default = nova-shell;
|
default = nova-shell;
|
||||||
|
|
||||||
# Reproducer wrappers: run test/screen-uaf-reproducer/shell.qml
|
# nova-shell on unpatched Qt for A/B crash testing
|
||||||
# with patched vs unpatched quickshell to confirm the fix.
|
nova-shell-unpatched =
|
||||||
screen-uaf-reproducer-patched = pkgs.writeShellScriptBin "screen-uaf-reproducer-patched" ''
|
|
||||||
exec ${qs}/bin/quickshell -p ${./test/screen-uaf-reproducer/shell.qml}
|
|
||||||
'';
|
|
||||||
screen-uaf-reproducer-unpatched =
|
|
||||||
let
|
let
|
||||||
qsUnpatched = (rawPkgs.extend quickshell.overlays.default).quickshell.override {
|
qsUnpatched = (rawPkgs.extend quickshell.overlays.default).quickshell.override {
|
||||||
withX11 = false;
|
withX11 = false;
|
||||||
withI3 = false;
|
withI3 = false;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
rawPkgs.writeShellScriptBin "screen-uaf-reproducer-unpatched" ''
|
rawPkgs.callPackage ./nix/package.nix {
|
||||||
exec ${qsUnpatched}/bin/quickshell -p ${./test/screen-uaf-reproducer/shell.qml}
|
quickshell = qsUnpatched;
|
||||||
'';
|
nova-stats = rawPkgs.callPackage ./nix/stats-daemon.nix { };
|
||||||
|
nova-shaders = rawPkgs.callPackage ./nix/shaders.nix { };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
// Minimal reproducer for Qt 6 Wayland screen use-after-free (QTBUG-XXXXXX).
|
|
||||||
//
|
|
||||||
// Creates many layer-shell surfaces bound to each screen. When an output is
|
|
||||||
// removed, the compositor sends wl_surface.leave + wl_registry.global_remove
|
|
||||||
// in the same event batch. libwayland resolves the wl_output proxy pointer at
|
|
||||||
// demarshal time; if the global_remove handler destroys the proxy first, the
|
|
||||||
// surface.leave handler receives a dangling pointer and crashes in
|
|
||||||
// QWaylandScreen::fromWlOutput / QPlatformScreen::screen().
|
|
||||||
//
|
|
||||||
// Run with unpatched Qt 6.10.2:
|
|
||||||
// quickshell -p ./shell.qml
|
|
||||||
//
|
|
||||||
// Then toggle an output:
|
|
||||||
// niri msg action power-off-monitors (move mouse to wake)
|
|
||||||
// wlr-randr --output eDP-1 --off && sleep 0.3 && wlr-randr --output eDP-1 --on
|
|
||||||
// or unplug/replug an external monitor
|
|
||||||
//
|
|
||||||
// Should crash within a few seconds of the output toggle.
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
ShellRoot {
|
|
||||||
// 20 layer-shell surfaces per screen - all bound to the output,
|
|
||||||
// so the compositor must send enter/leave for each on removal.
|
|
||||||
Variants {
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: 20
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
}
|
|
||||||
width: 60
|
|
||||||
height: 20
|
|
||||||
margins.top: index * 22
|
|
||||||
margins.left: 4
|
|
||||||
exclusiveZone: 0
|
|
||||||
color: "#80ff00ff"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Trigger rapid output power-cycling to provoke the Qt Wayland screen UAF.
|
|
||||||
#
|
|
||||||
# First, in another terminal:
|
|
||||||
# nix run .#screen-uaf-reproducer-unpatched (should crash)
|
|
||||||
# nix run .#screen-uaf-reproducer-patched (should survive)
|
|
||||||
#
|
|
||||||
# Then run this script to toggle the output.
|
|
||||||
#
|
|
||||||
# Usage: ./trigger.sh [iterations]
|
|
||||||
# iterations: number of off/on cycles (default 20)
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
cycles="${1:-20}"
|
|
||||||
|
|
||||||
if command -v niri &>/dev/null && niri msg version &>/dev/null; then
|
|
||||||
echo "Detected Niri - using niri msg"
|
|
||||||
for i in $(seq 1 "$cycles"); do
|
|
||||||
echo "cycle $i/$cycles"
|
|
||||||
niri msg action power-off-monitors
|
|
||||||
sleep 0.3
|
|
||||||
niri msg action power-on-monitors
|
|
||||||
sleep 0.5
|
|
||||||
done
|
|
||||||
elif command -v wlr-randr &>/dev/null; then
|
|
||||||
output=$(wlr-randr --json 2>/dev/null | python3 -c \
|
|
||||||
"import sys,json; print(next(o['name'] for o in json.load(sys.stdin) if o['enabled']))" 2>/dev/null \
|
|
||||||
|| wlr-randr | grep -oP '^\S+' | head -1)
|
|
||||||
if [ -z "$output" ]; then
|
|
||||||
echo "error: could not detect an output via wlr-randr" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Detected wlroots compositor - toggling output $output"
|
|
||||||
for i in $(seq 1 "$cycles"); do
|
|
||||||
echo "cycle $i/$cycles"
|
|
||||||
wlr-randr --output "$output" --off
|
|
||||||
sleep 0.3
|
|
||||||
wlr-randr --output "$output" --on
|
|
||||||
sleep 0.5
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "error: neither niri nor wlr-randr found" >&2
|
|
||||||
echo "Manually unplug/replug a monitor while the reproducer is running." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Done. If the reproducer is still alive, the bug did not trigger."
|
|
||||||
echo "Try increasing iterations or adding more surfaces."
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue