detached c541a90

conversation_review_linux_test.go RawHistoryFolder

1//go:build linux && (amd64 || arm64)
2
3package pam
4
5import (
6	"testing"
7	"unsafe"
8
9	"github.com/bnema/purego"
10	"github.com/stretchr/testify/mock"
11	"github.com/stretchr/testify/require"
12)
13
14func TestRawCallbackRejectsUnknownAndRetiredArbitraryBits(t *testing.T) {
15	l, _, _ := testLibrary(t)
16	s := callbackState(t, NewMockConversation(t), l.memory)
17	s.retire()
18	address, err := callbackAddress()
19	require.NoError(t, err)
20	for _, cookie := range []uintptr{0, s.token, ^uintptr(0)} {
21		for _, count := range []uintptr{1, 0, ^uintptr(0), uintptr(1)<<32 | 1} {
22			for _, bits := range []uintptr{0, 1, 8, ^uintptr(0), uintptr(1) << 63} {
23				// All values stay integers throughout unknown-cookie admission.
24				// Both aligned and noncanonical addresses must be ignored.
25				result, _, _ := purego.SyscallN(address, count, bits, ^bits, cookie)
26				require.Equal(t, uintptr(ConvErr), result)
27			}
28		}
29	}
30}
31
32func TestCookieExhaustionNeverWrapsOrReuses(t *testing.T) {
33	l, _, _ := testLibrary(t)
34	handler := NewMockConversation(t)
35	conversations.Lock()
36	require.Empty(t, conversations.states)
37	previous := conversations.next
38	conversations.next = ^uintptr(0) - 1
39	conversations.Unlock()
40	// Test-only counter isolation; production never resets the counter.
41	t.Cleanup(func() {
42		conversations.Lock()
43		defer conversations.Unlock()
44		require.Empty(t, conversations.states)
45		conversations.next = previous
46	})
47	last, err := registerConversation(handler, l.memory)
48	require.NoError(t, err)
49	require.Equal(t, ^uintptr(0), last.token)
50	last.retire()
51	for range 2 {
52		s, err := registerConversation(handler, l.memory)
53		require.Error(t, err)
54		require.Nil(t, s)
55		conversations.Lock()
56		require.Equal(t, ^uintptr(0), conversations.next)
57		require.Empty(t, conversations.states)
58		conversations.Unlock()
59	}
60}
61
62func TestAllocatorPanicsStillWipeAndReleasePartialResponses(t *testing.T) {
63	for _, panicFree := range []bool{false, true} {
64		t.Run(map[bool]string{false: "alloc-panic", true: "alloc-and-free-panic"}[panicFree], func(t *testing.T) {
65			l, _, _ := testLibrary(t)
66			memory := newMocknativeMemory(t)
67			handler := NewMockConversation(t)
68			s := callbackState(t, handler, memory)
69			messages, output := nativeMessages(t, l.memory, PromptEchoOff, PromptEchoOn)
70			allocated := make(map[unsafe.Pointer]uintptr)
71			index := 0
72			memory.EXPECT().alloc(mock.Anything).RunAndReturn(func(size uintptr) (unsafe.Pointer, error) {
73				index++
74				if index == 3 {
75					panic("allocator payload must not escape")
76				}
77				p, err := l.memory.alloc(size)
78				allocated[p] = size
79				return p, err
80			}).Times(3)
81			memory.EXPECT().free(mock.Anything).Run(func(p unsafe.Pointer) {
82				size, ok := allocated[p]
83				require.True(t, ok)
84				if size == 4 {
85					require.Equal(t, make([]byte, 4), unsafe.Slice((*byte)(p), 4))
86				}
87				delete(allocated, p)
88				l.memory.free(p)
89				if panicFree && size == 4 {
90					panic("free payload must not escape")
91				}
92			}).Times(2)
93			var answers, prompts [][]byte
94			handler.EXPECT().Respond(mock.Anything, mock.Anything).RunAndReturn(func(message Message, answer []byte) (int, error) {
95				answers = append(answers, answer)
96				prompts = append(prompts, message.Text)
97				copy(answer, "abc")
98				return 3, nil
99			}).Times(2)
100			require.Equal(t, ConvErr, invokeConversation(t, s.token, 2, messages, output))
101			require.Nil(t, *(*unsafe.Pointer)(output))
102			require.Empty(t, allocated)
103			for _, bytes := range append(answers, prompts...) {
104				require.Equal(t, make([]byte, len(bytes)), bytes)
105			}
106			// Reference release also survives allocator and cleanup panics.
107			conversations.Lock()
108			require.Zero(t, s.refs)
109			conversations.Unlock()
110			s.retire()
111		})
112	}
113}

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 1

Tags 1

↑↓ moveenter openesc close