diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-10-17 08:03:03 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-10-17 08:03:03 +0900 |
| commit | 82299d34c60928e4908c784e771c0fc4aa283a5b (patch) | |
| tree | 89396d712731e70899bbe082fb2f7bc023024564 /internal/app/sppulse_test.go | |
| parent | 792013cefbeb7413c60bd24e7014f8a9e0622540 (diff) | |
internal/app/sppulse: correctly handle small cookie
The trailing zero bytes need to be sliced off, so send cookie size alongside buffer content.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/sppulse_test.go')
| -rw-r--r-- | internal/app/sppulse_test.go | 203 |
1 files changed, 198 insertions, 5 deletions
diff --git a/internal/app/sppulse_test.go b/internal/app/sppulse_test.go index 75b1db0f..7c050bf5 100644 --- a/internal/app/sppulse_test.go +++ b/internal/app/sppulse_test.go @@ -2,7 +2,9 @@ package app import ( "bytes" + "errors" "os" + "syscall" "testing" "hakurei.app/container" @@ -28,7 +30,7 @@ func TestSpPulseOp(t *testing.T) { return c }, nil, nil, nil, nil, errNotEnabled, nil, nil, nil, nil, nil}, - {"socketDir stat", func(isShim bool, _ bool) outcomeOp { + {"socketDir stat", func(isShim, _ bool) outcomeOp { if !isShim { return new(spPulseOp) } @@ -99,25 +101,94 @@ func TestSpPulseOp(t *testing.T) { call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), - call("verbosef", stub.ExpectArgs{"loading up to %d bytes from %q", []any{1 << 8, "/proc/nonexistent/cookie"}}, nil, nil), call("stat", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubFi{isDir: false, size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"loading %d bytes from %q", []any{1 << 8, "/proc/nonexistent/cookie"}}, nil, nil), call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, (*stubOsFile)(nil), stub.UniqueError(0)), }, nil, nil, &hst.AppError{ Step: "open PulseAudio cookie", Err: stub.UniqueError(0), }, nil, nil, nil, nil, nil}, - {"success cookie", func(isShim bool, _ bool) outcomeOp { + {"cookie bad shim size", func(isShim, clearUnexported bool) outcomeOp { if !isShim { return new(spPulseOp) } - return &spPulseOp{Cookie: (*[256]byte)(sampleCookie)} + op := &spPulseOp{Cookie: (*[pulseCookieSizeMax]byte)(sampleCookie), CookieSize: pulseCookieSizeMax} + if clearUnexported { + op.CookieSize += +0xfd + } + return op + }, hst.Template, nil, []stub.Call{ + call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), + call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), + call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), + call("stat", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubFi{isDir: false, size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"loading %d bytes from %q", []any{1 << 8, "/proc/nonexistent/cookie"}}, nil, nil), + call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie)}, nil), + }, newI(). + // state.ensureRuntimeDir + Ensure(m(wantRunDirPath), 0700). + UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). + Ensure(m(wantRuntimePath), 0700). + UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). + // state.runtime + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + UpdatePerm(m(wantRuntimeSharePath), acl.Execute). + // toSystem + Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ + // this op configures the container state and does not make calls during toContainer + }, nil, nil, &hst.AppError{ + Step: "finalise", + Err: os.ErrInvalid, + Msg: "unexpected PulseAudio cookie size", + }}, + + {"success cookie short", func(isShim, _ bool) outcomeOp { + if !isShim { + return new(spPulseOp) + } + sampleCookieTrunc := make([]byte, pulseCookieSizeMax) + copy(sampleCookieTrunc, sampleCookie[:len(sampleCookie)-0xe]) + return &spPulseOp{Cookie: (*[pulseCookieSizeMax]byte)(sampleCookieTrunc), CookieSize: pulseCookieSizeMax - 0xe} + }, hst.Template, nil, []stub.Call{ + call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), + call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), + call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), + call("stat", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubFi{isDir: false, size: pulseCookieSizeMax - 0xe}, nil), + call("verbosef", stub.ExpectArgs{"%s at %q is %d bytes shorter than expected", []any{"PulseAudio cookie", "/proc/nonexistent/cookie", int64(0xe)}}, nil, nil), + call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie[:len(sampleCookie)-0xe])}, nil), + }, newI(). + // state.ensureRuntimeDir + Ensure(m(wantRunDirPath), 0700). + UpdatePermType(system.User, m(wantRunDirPath), acl.Execute). + Ensure(m(wantRuntimePath), 0700). + UpdatePermType(system.User, m(wantRuntimePath), acl.Execute). + // state.runtime + Ephemeral(system.Process, m(wantRuntimeSharePath), 0700). + UpdatePerm(m(wantRuntimeSharePath), acl.Execute). + // toSystem + Link(m(wantRuntimePath+"/pulse/native"), m(wantRuntimeSharePath+"/pulse")), sysUsesRuntime(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{ + // this op configures the container state and does not make calls during toContainer + }, &container.Params{ + Ops: new(container.Ops). + Bind(m(wantRuntimeSharePath+"/pulse"), m("/run/user/1000/pulse/native"), 0). + Place(m("/.hakurei/pulse-cookie"), sampleCookie[:len(sampleCookie)-0xe]), + }, paramsWantEnv(config, map[string]string{ + "PULSE_SERVER": "unix:/run/user/1000/pulse/native", + "PULSE_COOKIE": "/.hakurei/pulse-cookie", + }, nil), nil}, + + {"success cookie", func(isShim, _ bool) outcomeOp { + if !isShim { + return new(spPulseOp) + } + return &spPulseOp{Cookie: (*[pulseCookieSizeMax]byte)(sampleCookie), CookieSize: pulseCookieSizeMax} }, hst.Template, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), - call("verbosef", stub.ExpectArgs{"loading up to %d bytes from %q", []any{1 << 8, "/proc/nonexistent/cookie"}}, nil, nil), call("stat", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubFi{isDir: false, size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"loading %d bytes from %q", []any{1 << 8, "/proc/nonexistent/cookie"}}, nil, nil), call("open", stub.ExpectArgs{"/proc/nonexistent/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie)}, nil), }, newI(). // state.ensureRuntimeDir @@ -169,3 +240,125 @@ func TestSpPulseOp(t *testing.T) { }, nil), nil}, }) } + +func TestLoadFile(t *testing.T) { + t.Parallel() + + sampleCookie := bytes.Repeat([]byte{0xfc}, pulseCookieSizeMax) + checkSimple(t, "loadFile", []simpleTestCase{ + {"buf", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + nil) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, errors.New("invalid buffer")}, + + {"stat", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + make([]byte, 1<<8+0xfd)) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, (*stubFi)(nil), stub.UniqueError(3)), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{ + Step: "access simulated PulseAudio cookie", + Err: stub.UniqueError(3), + }}, + + {"dir", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + make([]byte, 1<<8+0xfd)) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{isDir: true}, nil), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{ + Step: "read simulated PulseAudio cookie", + Err: &os.PathError{Op: "stat", Path: "/home/ophestra/xdg/config/pulse/cookie", Err: syscall.EISDIR}, + }}, + + {"oob", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + make([]byte, 1<<8+0xfd)) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{size: 1<<8 + 0xff}, nil), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{ + Step: "finalise", + Err: &os.PathError{Op: "stat", Path: "/home/ophestra/xdg/config/pulse/cookie", Err: syscall.ENOMEM}, + Msg: `simulated PulseAudio cookie at "/home/ophestra/xdg/config/pulse/cookie" exceeds expected size`, + }}, + + {"open", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + make([]byte, 1<<8+0xfd)) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"%s at %q is %d bytes shorter than expected", []any{"simulated PulseAudio cookie", "/home/ophestra/xdg/config/pulse/cookie", int64(0xfd)}}, nil, nil), + call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, (*stubOsFile)(nil), stub.UniqueError(2)), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{Step: "open simulated PulseAudio cookie", Err: stub.UniqueError(2)}}, + + {"read", func(k *kstub) error { + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + make([]byte, 1<<8+0xfd)) + k.Verbose(n) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"%s at %q is %d bytes shorter than expected", []any{"simulated PulseAudio cookie", "/home/ophestra/xdg/config/pulse/cookie", int64(0xfd)}}, nil, nil), + call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubOsFile{Reader: errorReader{stub.UniqueError(1)}}, nil), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{Step: "read simulated PulseAudio cookie", Err: stub.UniqueError(1)}}, + + {"short close", func(k *kstub) error { + buf := make([]byte, 1<<8+0xfd) + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + buf) + k.Verbose(buf[:n]) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"%s at %q is %d bytes shorter than expected", []any{"simulated PulseAudio cookie", "/home/ophestra/xdg/config/pulse/cookie", int64(0xfd)}}, nil, nil), + call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubOsFile{closeErr: stub.UniqueError(0), Reader: bytes.NewReader(sampleCookie)}, nil), + call("verbose", stub.ExpectArgs{[]any{sampleCookie}}, nil, nil), + }}, &hst.AppError{Step: "close simulated PulseAudio cookie", Err: stub.UniqueError(0)}}, + + {"success", func(k *kstub) error { + buf := make([]byte, 1<<8) + n, err := loadFile(k, k, + "simulated PulseAudio cookie", + "/home/ophestra/xdg/config/pulse/cookie", + buf) + k.Verbose(buf[:n]) + return err + }, stub.Expect{Calls: []stub.Call{ + call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubFi{size: 1 << 8}, nil), + call("verbosef", stub.ExpectArgs{"loading %d bytes from %q", []any{1 << 8, "/home/ophestra/xdg/config/pulse/cookie"}}, nil, nil), + call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &stubOsFile{Reader: bytes.NewReader(sampleCookie)}, nil), + call("verbose", stub.ExpectArgs{[]any{sampleCookie}}, nil, nil), + }}, nil}, + }) +} |
