main f2d166b

security_startup_test.go RawHistoryFolder

1package drm
2
3import (
4	"context"
5	"errors"
6	"os"
7	"sync/atomic"
8	"testing"
9	"time"
10
11	portsmocks "github.com/bnema/neferwl/internal/mocks/ports"
12	"github.com/bnema/neferwl/internal/ports"
13	"github.com/stretchr/testify/mock"
14)
15
16func TestSecurityProtectedStartupBlackProofAndCaptureRejection(t *testing.T) {
17	o, k, commits, mu := testOutputMu(t)
18	o.fbs = [2]uint32{} // the first setup of a run: no image yet
19	o.cursor = nil
20	var state atomic.Uint64
21	state.Store(3)
22	securityGate(t, o, &state)
23	events := make(chan ports.SecurityBackendEvent, 2)
24	o.SecurityEvents = events
25	r := portsmocks.NewMockRenderer(t)
26	buf := func() ports.DMABuf {
27		f, w := protectionFence(t, false)
28		w.Close()
29		return ports.DMABuf{Planes: []ports.DMABufPlane{{File: f}}}
30	}
31	r.EXPECT().SetHDR(float64(0)).Return().Maybe()
32	r.EXPECT().ExportTargets(2, mock.Anything, false).Return([]ports.DMABuf{buf(), buf()}, nil).Once()
33	r.EXPECT().UseTarget(mock.Anything).Return()
34	r.EXPECT().Render(ports.Scene{Background: "#000000"}, map[ports.WindowID]ports.SurfaceContent(nil)).Return(nil, nil).Times(3)
35	r.EXPECT().Close().Return().Once()
36	k.EXPECT().addFB(mock.Anything, uint32(fourccXRGB)).Return(70, nil)
37	k.EXPECT().createBlob(mock.Anything).Return(99, nil)
38	k.EXPECT().destroyBlob(mock.Anything).Return(nil).Maybe()
39	captures := make(chan ports.CaptureRequest, 1)
40	captured := make(chan ports.CaptureDone, 1)
41	scenes := make(chan ports.Scene, 1)
42	scenes <- ports.Scene{Seq: 99} // never render stale desktop after startup
43	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
44	defer cancel()
45	done := make(chan error, 1)
46	go func() {
47		done <- o.Run(ctx, func(int, int) (ports.Renderer, error) { return r, nil }, nil, nil, scenes, nil, nil, nil, nil, captures, captured)
48	}()
49	select {
50	case ev := <-events:
51		if p, ok := ev.(ports.SecurityOutputProof); !ok || p.Proof.Generation != 1 || p.Proof.Instance != 77 || p.Proof.Kind != ports.ProtectionProtectedFrame {
52			t.Fatalf("proof %+v", ev)
53		}
54	case <-ctx.Done():
55		t.Fatal("no startup black proof")
56	}
57	mu.Lock()
58	for i, c := range *commits {
59		if c.flags&atomicTestOnly != 0 {
60			continue
61		}
62		active, _ := c.req.value(tCrtc, pActive)
63		if active != 0 && i != len(*commits)-1 {
64			t.Error("startup exposed an active target before protected transaction")
65		}
66	}
67	mu.Unlock()
68	f, w := protectionFence(t, false)
69	w.Close()
70	captures <- ports.CaptureRequest{ID: 9, Dst: ports.SHMBuffer{File: f}}
71	select {
72	case reply := <-captured:
73		if reply.ID != 9 || reply.Err == nil {
74			t.Fatalf("capture result %+v", reply)
75		}
76	case <-ctx.Done():
77		t.Fatal("protected capture not failed")
78	}
79	cancel()
80	if err := <-done; err != nil {
81		t.Fatal(err)
82	}
83}
84
85func TestSecurityStartupClearWaitUsesRunContext(t *testing.T) {
86	o, k, _ := testOutput(t)
87	o.cursor = nil
88	var state atomic.Uint64
89	state.Store(3)
90	securityGate(t, o, &state)
91	r := portsmocks.NewMockRenderer(t)
92	buf := func() ports.DMABuf {
93		f, w := protectionFence(t, false)
94		w.Close()
95		return ports.DMABuf{Planes: []ports.DMABufPlane{{File: f}}}
96	}
97	r.EXPECT().SetHDR(float64(0)).Return().Maybe()
98	r.EXPECT().ExportTargets(2, mock.Anything, false).Return([]ports.DMABuf{buf(), buf()}, nil).Once()
99	r.EXPECT().ExportTargets(0, []uint64(nil), false).Return(nil, nil).Once()
100	r.EXPECT().UseTarget(0).Return().Once()
101	fence, _ := protectionFence(t, false)
102	ctx, cancel := context.WithCancel(context.Background())
103	defer cancel()
104	r.EXPECT().Render(mock.Anything, mock.Anything).RunAndReturn(func(ports.Scene, map[ports.WindowID]ports.SurfaceContent) (*os.File, error) {
105		cancel()
106		return fence, nil
107	}).Once()
108	r.EXPECT().Close().Return().Once()
109	k.EXPECT().addFB(mock.Anything, uint32(fourccXRGB)).Return(70, nil)
110	k.EXPECT().rmFB(mock.Anything).Return(nil)
111	err := o.Run(ctx, func(int, int) (ports.Renderer, error) { return r, nil }, nil, nil, nil, nil, nil, nil, nil, nil, nil)
112	if !errors.Is(err, context.Canceled) {
113		t.Fatalf("startup cancellation: %v", err)
114	}
115	if _, err := fence.Stat(); !errors.Is(err, os.ErrClosed) {
116		t.Fatal("startup fence leaked")
117	}
118}
119
120func TestSecurityWiredUnlockedCloseNeverRestoresSavedFB(t *testing.T) {
121	o, k, commits := testOutput(t)
122	var state atomic.Uint64
123	securityGate(t, o, &state)
124	o.saved = modeCrtc{crtcID: tCrtc, fbID: 123, modeValid: 1, mode: o.mode}
125	k.EXPECT().rmFB(mock.Anything).Return(nil)
126	o.Close() // no protected snapshot needed: saved restoration is never admitted
127	checkProtectedDisable(t, (*commits)[0], tPrimary, tCursor)
128}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 26

Tags 10

↑↓ moveenter openesc close