github/test: Run tests with address sanitizer
We have lots of cgo interaction here so better to check things fully. This also requires manually checking for leaks, so add support for this.
This commit is contained in:
25
.github/workflows/test.yaml
vendored
25
.github/workflows/test.yaml
vendored
@@ -13,19 +13,38 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Install PAM
|
- name: Install PAM
|
||||||
run: sudo apt install -y libpam-dev
|
run: |
|
||||||
|
sudo apt update -y
|
||||||
|
sudo apt install -y libpam-dev
|
||||||
|
- name: Install Debug symbols
|
||||||
|
run: |
|
||||||
|
sudo apt install -y ubuntu-dev-tools
|
||||||
|
(cd /tmp && pull-lp-ddebs libpam0g $(lsb_release -c -s))
|
||||||
|
(cd /tmp && pull-lp-ddebs libpam-modules $(lsb_release -c -s))
|
||||||
|
sudo dpkg -i /tmp/libpam*-dbgsym_*.ddeb
|
||||||
- name: Add a test user
|
- name: Add a test user
|
||||||
run: sudo useradd -d /tmp/test -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' -s /bin/false test
|
run: sudo useradd -d /tmp/test -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' -s /bin/false test
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Test
|
||||||
|
run: sudo go test -v -cover -coverprofile=coverage.out ./...
|
||||||
|
- name: Test with Address Sanitizer
|
||||||
|
env:
|
||||||
|
GO_PAM_TEST_WITH_ASAN: true
|
||||||
|
CGO_CFLAGS: "-O0 -g3 -fno-omit-frame-pointer"
|
||||||
|
run: |
|
||||||
|
# Do not run sudo-requiring go tests because as PAM has some leaks in 22.04
|
||||||
|
go test -v -asan -cover -coverprofile=coverage-asan-tx.out -gcflags=all="-N -l"
|
||||||
|
|
||||||
|
# Run the rest of tests normally
|
||||||
|
sudo go test -v -cover -coverprofile=coverage-asan-module.out -asan -gcflags=all="-N -l" -run Module
|
||||||
|
sudo go test -C cmd -coverprofile=coverage-asan.out -v -asan -gcflags=all="-N -l" ./...
|
||||||
- name: Generate example module
|
- name: Generate example module
|
||||||
run: |
|
run: |
|
||||||
rm -f example-module/pam_go.so
|
rm -f example-module/pam_go.so
|
||||||
go generate -C example-module -v
|
go generate -C example-module -v
|
||||||
test -e example-module/pam_go.so
|
test -e example-module/pam_go.so
|
||||||
git diff --exit-code example-module
|
git diff --exit-code example-module
|
||||||
- name: Test
|
|
||||||
run: sudo go test -v -cover -coverprofile=coverage.out ./...
|
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
env:
|
env:
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
coverage.out
|
coverage*.out
|
||||||
example-module/*.so
|
example-module/*.so
|
||||||
example-module/*.h
|
example-module/*.h
|
||||||
cmd/pam-moduler/tests/*/*.so
|
cmd/pam-moduler/tests/*/*.so
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func ensureNoError(t *testing.T, err error) {
|
|||||||
|
|
||||||
func Test_NewNullModuleTransaction(t *testing.T) {
|
func Test_NewNullModuleTransaction(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
mt := moduleTransaction{}
|
mt := moduleTransaction{}
|
||||||
|
|
||||||
if mt.handle != nil {
|
if mt.handle != nil {
|
||||||
@@ -137,6 +138,7 @@ func Test_NewNullModuleTransaction(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(name+"-error-check", func(t *testing.T) {
|
t.Run(name+"-error-check", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
data, err := tc.testFunc(t)
|
data, err := tc.testFunc(t)
|
||||||
|
|
||||||
switch d := data.(type) {
|
switch d := data.(type) {
|
||||||
@@ -202,6 +204,7 @@ func Test_NewNullModuleTransaction(t *testing.T) {
|
|||||||
|
|
||||||
func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
|
func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
mt := &moduleTransaction{}
|
mt := &moduleTransaction{}
|
||||||
|
|
||||||
err := mt.InvokeHandler(nil, 0, nil)
|
err := mt.InvokeHandler(nil, 0, nil)
|
||||||
@@ -308,6 +311,7 @@ func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
|
|||||||
func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
|
func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
testFunc func(mock *mockModuleTransaction) (any, error)
|
testFunc func(mock *mockModuleTransaction) (any, error)
|
||||||
@@ -898,6 +902,7 @@ func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
mock := newMockModuleTransaction(&mockModuleTransaction{T: t,
|
mock := newMockModuleTransaction(&mockModuleTransaction{T: t,
|
||||||
Expectations: tc.mockExpectations, RetData: tc.mockRetData,
|
Expectations: tc.mockExpectations, RetData: tc.mockRetData,
|
||||||
ConversationHandler: tc.conversationHandler})
|
ConversationHandler: tc.conversationHandler})
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func ensureTransactionEnds(t *testing.T, tx *Transaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_001(t *testing.T) {
|
func TestPAM_001(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -67,6 +68,7 @@ func TestPAM_001(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_002(t *testing.T) {
|
func TestPAM_002(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -107,6 +109,7 @@ func (c Credentials) RespondPAM(s Style, msg string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_003(t *testing.T) {
|
func TestPAM_003(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -128,6 +131,7 @@ func TestPAM_003(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_004(t *testing.T) {
|
func TestPAM_004(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -148,10 +152,14 @@ func TestPAM_004(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_005(t *testing.T) {
|
func TestPAM_005(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
}
|
}
|
||||||
|
if _, found := os.LookupEnv("GO_PAM_TEST_WITH_ASAN"); found {
|
||||||
|
t.Skip("test fails under ASAN")
|
||||||
|
}
|
||||||
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
|
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
|
||||||
return "secret", nil
|
return "secret", nil
|
||||||
})
|
})
|
||||||
@@ -174,6 +182,7 @@ func TestPAM_005(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_006(t *testing.T) {
|
func TestPAM_006(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -197,6 +206,7 @@ func TestPAM_006(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_007(t *testing.T) {
|
func TestPAM_007(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
if u.Uid != "0" {
|
if u.Uid != "0" {
|
||||||
t.Skip("run this test as root")
|
t.Skip("run this test as root")
|
||||||
@@ -223,6 +233,7 @@ func TestPAM_007(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir(t *testing.T) {
|
func TestPAM_ConfDir(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
c := Credentials{
|
c := Credentials{
|
||||||
// the custom service always permits even with wrong password.
|
// the custom service always permits even with wrong password.
|
||||||
@@ -258,6 +269,7 @@ func TestPAM_ConfDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
|
func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
if !CheckPamHasStartConfdir() {
|
if !CheckPamHasStartConfdir() {
|
||||||
t.Skip("this requires PAM with Conf dir support")
|
t.Skip("this requires PAM with Conf dir support")
|
||||||
}
|
}
|
||||||
@@ -286,6 +298,7 @@ func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir_InfoMessage(t *testing.T) {
|
func TestPAM_ConfDir_InfoMessage(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
u, _ := user.Current()
|
u, _ := user.Current()
|
||||||
var infoText string
|
var infoText string
|
||||||
tx, err := StartConfDir("echo-service", u.Username,
|
tx, err := StartConfDir("echo-service", u.Username,
|
||||||
@@ -319,6 +332,7 @@ func TestPAM_ConfDir_InfoMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir_Deny(t *testing.T) {
|
func TestPAM_ConfDir_Deny(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
if !CheckPamHasStartConfdir() {
|
if !CheckPamHasStartConfdir() {
|
||||||
t.Skip("this requires PAM with Conf dir support")
|
t.Skip("this requires PAM with Conf dir support")
|
||||||
}
|
}
|
||||||
@@ -350,6 +364,7 @@ func TestPAM_ConfDir_Deny(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
|
func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
c := Credentials{
|
c := Credentials{
|
||||||
User: "testuser",
|
User: "testuser",
|
||||||
// the custom service only cares about correct user name.
|
// the custom service only cares about correct user name.
|
||||||
@@ -375,6 +390,7 @@ func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPAM_ConfDir_WrongUserName(t *testing.T) {
|
func TestPAM_ConfDir_WrongUserName(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
c := Credentials{
|
c := Credentials{
|
||||||
User: "wronguser",
|
User: "wronguser",
|
||||||
Password: "wrongsecret",
|
Password: "wrongsecret",
|
||||||
@@ -403,6 +419,7 @@ func TestPAM_ConfDir_WrongUserName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestItem(t *testing.T) {
|
func TestItem(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
|
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
})
|
})
|
||||||
@@ -442,6 +459,7 @@ func TestItem(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEnv(t *testing.T) {
|
func TestEnv(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
|
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
})
|
})
|
||||||
@@ -511,6 +529,7 @@ func TestEnv(t *testing.T) {
|
|||||||
|
|
||||||
func Test_Error(t *testing.T) {
|
func Test_Error(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
if !CheckPamHasStartConfdir() {
|
if !CheckPamHasStartConfdir() {
|
||||||
t.Skip("this requires PAM with Conf dir support")
|
t.Skip("this requires PAM with Conf dir support")
|
||||||
}
|
}
|
||||||
@@ -624,6 +643,7 @@ func Test_Error(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Finalizer(t *testing.T) {
|
func Test_Finalizer(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
if !CheckPamHasStartConfdir() {
|
if !CheckPamHasStartConfdir() {
|
||||||
t.Skip("this requires PAM with Conf dir support")
|
t.Skip("this requires PAM with Conf dir support")
|
||||||
}
|
}
|
||||||
@@ -643,6 +663,7 @@ func Test_Finalizer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_001(t *testing.T) {
|
func TestFailure_001(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
_, err := tx.GetEnvList()
|
_, err := tx.GetEnvList()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -651,6 +672,7 @@ func TestFailure_001(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_002(t *testing.T) {
|
func TestFailure_002(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.PutEnv("")
|
err := tx.PutEnv("")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -659,6 +681,7 @@ func TestFailure_002(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_003(t *testing.T) {
|
func TestFailure_003(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.CloseSession(0)
|
err := tx.CloseSession(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -667,6 +690,7 @@ func TestFailure_003(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_004(t *testing.T) {
|
func TestFailure_004(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.OpenSession(0)
|
err := tx.OpenSession(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -675,6 +699,7 @@ func TestFailure_004(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_005(t *testing.T) {
|
func TestFailure_005(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.ChangeAuthTok(0)
|
err := tx.ChangeAuthTok(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -683,6 +708,7 @@ func TestFailure_005(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_006(t *testing.T) {
|
func TestFailure_006(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.AcctMgmt(0)
|
err := tx.AcctMgmt(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -691,6 +717,7 @@ func TestFailure_006(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_007(t *testing.T) {
|
func TestFailure_007(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.SetCred(0)
|
err := tx.SetCred(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -699,6 +726,7 @@ func TestFailure_007(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_008(t *testing.T) {
|
func TestFailure_008(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.SetItem(User, "test")
|
err := tx.SetItem(User, "test")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -707,6 +735,7 @@ func TestFailure_008(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_009(t *testing.T) {
|
func TestFailure_009(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
_, err := tx.GetItem(User)
|
_, err := tx.GetItem(User)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -715,6 +744,7 @@ func TestFailure_009(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFailure_010(t *testing.T) {
|
func TestFailure_010(t *testing.T) {
|
||||||
|
t.Cleanup(maybeDoLeakCheck)
|
||||||
tx := Transaction{}
|
tx := Transaction{}
|
||||||
err := tx.End()
|
err := tx.End()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
31
utils.go
Normal file
31
utils.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Package pam provides a wrapper for the PAM application API.
|
||||||
|
package pam
|
||||||
|
|
||||||
|
/*
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
#include <sanitizer/lsan_interface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
maybe_do_leak_check (void)
|
||||||
|
{
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
__lsan_do_leak_check();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func maybeDoLeakCheck() {
|
||||||
|
runtime.GC()
|
||||||
|
time.Sleep(time.Millisecond * 20)
|
||||||
|
if os.Getenv("GO_PAM_SKIP_LEAK_CHECK") == "" {
|
||||||
|
C.maybe_do_leak_check()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user