Skip to content

Commit 7acabf3

Browse files
fix(rerank): handle closed Darwin identity sockets after exec
1 parent 5b1596b commit 7acabf3

2 files changed

Lines changed: 95 additions & 14 deletions

File tree

‎pkg/rerank/supervisor_peer_darwin.go‎

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ import (
1818
"golang.org/x/sys/unix"
1919
)
2020

21-
func rerankerProcessExecutableIdentity(pid int) (uint64, uint64, error) {
22-
type uniqueIdentifierInfo struct {
23-
UUID [16]byte
24-
UniqueID uint64
25-
ParentUniqueID uint64
26-
IDVersion int32
27-
Reserved2 uint32
28-
Reserved3 uint64
29-
Reserved4 uint64
30-
}
21+
type uniqueIdentifierInfo struct {
22+
UUID [16]byte
23+
UniqueID uint64
24+
ParentUniqueID uint64
25+
IDVersion int32
26+
Reserved2 uint32
27+
Reserved3 uint64
28+
Reserved4 uint64
29+
}
30+
31+
func darwinProcessIdentity(pid int) (uniqueIdentifierInfo, error) {
3132
var identity uniqueIdentifierInfo
3233
const (
3334
procInfoCallPIDInfo = 0x2
@@ -44,10 +45,18 @@ func rerankerProcessExecutableIdentity(pid int) (uint64, uint64, error) {
4445
unsafe.Sizeof(identity),
4546
)
4647
if errno != 0 {
47-
return 0, 0, errno
48+
return identity, errno
4849
}
4950
if result != unsafe.Sizeof(identity) {
50-
return 0, 0, fmt.Errorf("unexpected Darwin executable identity size %d", result)
51+
return identity, fmt.Errorf("unexpected Darwin executable identity size %d", result)
52+
}
53+
return identity, nil
54+
}
55+
56+
func rerankerProcessExecutableIdentity(pid int) (uint64, uint64, error) {
57+
identity, err := darwinProcessIdentity(pid)
58+
if err != nil {
59+
return 0, 0, err
5160
}
5261
first := binary.LittleEndian.Uint64(identity.UUID[:8])
5362
second := binary.LittleEndian.Uint64(identity.UUID[8:])
@@ -177,10 +186,20 @@ type rerankerProcessHandle struct {
177186

178187
func openRerankerProcessHandle(pid int, started string, identity *os.File) (*rerankerProcessHandle, error) {
179188
token, err := peerAuditToken(identity)
180-
if err != nil {
189+
if errors.Is(err, unix.ENOTCONN) {
190+
// The executed program may close inherited descriptors before we
191+
// observe its image. Darwin's proc_find_audit_token (used by signal)
192+
// checks only PID and pidversion; obtain both from kernel process
193+
// information rather than falling back to a racy kill(pid).
194+
info, infoErr := darwinProcessIdentity(pid)
195+
if infoErr != nil {
196+
return nil, infoErr
197+
}
198+
token = [8]uint32{5: uint32(pid), 7: uint32(info.IDVersion)}
199+
} else if err != nil {
181200
return nil, err
182201
}
183-
if int(token[5]) != pid || rerankerProcessStartIdentity(pid) != started {
202+
if int(token[5]) != pid || started == "" || rerankerProcessStartIdentity(pid) != started {
184203
return nil, fmt.Errorf("reranker process generation changed while acquiring audit token")
185204
}
186205
return &rerankerProcessHandle{token: token}, nil
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//go:build darwin
2+
3+
package rerank
4+
5+
import (
6+
"errors"
7+
"os"
8+
"os/exec"
9+
"syscall"
10+
"testing"
11+
12+
"golang.org/x/sys/unix"
13+
)
14+
15+
func TestRerankerProcessHandleAfterIdentitySocketCloses(t *testing.T) {
16+
cmd := exec.Command("/bin/sleep", "30")
17+
if err := cmd.Start(); err != nil {
18+
t.Fatal(err)
19+
}
20+
t.Cleanup(func() {
21+
_ = cmd.Process.Kill()
22+
_ = cmd.Wait()
23+
})
24+
fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0)
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
identity := os.NewFile(uintptr(fds[0]), "closed-peer")
29+
defer func() { _ = identity.Close() }()
30+
if err := unix.Close(fds[1]); err != nil {
31+
t.Fatal(err)
32+
}
33+
if _, err := peerAuditToken(identity); !errors.Is(err, unix.ENOTCONN) {
34+
t.Fatalf("peer audit token error = %v, want ENOTCONN", err)
35+
}
36+
pid := cmd.Process.Pid
37+
started := rerankerProcessStartIdentity(pid)
38+
if started == "" {
39+
t.Fatal("missing process start identity")
40+
}
41+
for _, wrong := range []string{"", started + "-stale"} {
42+
if _, err := openRerankerProcessHandle(pid, wrong, identity); err == nil {
43+
t.Fatal("accepted incorrect process generation")
44+
}
45+
}
46+
handle, err := openRerankerProcessHandle(pid, started, identity)
47+
if err != nil {
48+
t.Fatal(err)
49+
}
50+
defer func() { _ = handle.Close() }()
51+
stale := *handle
52+
stale.token[7]++
53+
if err := stale.Signal(syscall.SIGKILL); !errors.Is(err, os.ErrProcessDone) {
54+
t.Fatalf("stale pidversion signal = %v, want ErrProcessDone", err)
55+
}
56+
if err := syscall.Kill(pid, 0); err != nil {
57+
t.Fatalf("stale handle killed the live process: %v", err)
58+
}
59+
if err := handle.Signal(syscall.SIGKILL); err != nil {
60+
t.Fatalf("terminate through handle: %v", err)
61+
}
62+
}

0 commit comments

Comments
 (0)