Skip to content

Commit

Permalink
github/test: Run tests with address sanitizer
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
3v1n0 committed Nov 27, 2023
1 parent 326f24e commit 6fecdd0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 1 deletion.
21 changes: 20 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,32 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- 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
run: sudo useradd -d /tmp/test -p '$1$Qd8H95T5$RYSZQeoFbEB.gS19zS99A0' -s /bin/false test
- name: Checkout code
uses: actions/checkout@v4
- name: Test
run: sudo go test -v ./...
- 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 -gcflags=all="-N -l"
# Run the rest of tests normally
sudo go test -v -asan -gcflags=all="-N -l" -run Module
sudo go test -C cmd -v -asan -gcflags=all="-N -l" ./...
- name: Generate example module
run: |
go generate -C example-module -v
Expand Down
5 changes: 5 additions & 0 deletions module-transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func ensureNoError(t *testing.T, err error) {

func Test_NewNullModuleTransaction(t *testing.T) {
t.Parallel()
t.Cleanup(maybeDoLeakCheck)
mt := moduleTransaction{}

if mt.handle != nil {
Expand Down Expand Up @@ -137,6 +138,7 @@ func Test_NewNullModuleTransaction(t *testing.T) {
tc := tc
t.Run(name+"-error-check", func(t *testing.T) {
t.Parallel()
t.Cleanup(maybeDoLeakCheck)
data, err := tc.testFunc(t)

switch d := data.(type) {
Expand Down Expand Up @@ -202,6 +204,7 @@ func Test_NewNullModuleTransaction(t *testing.T) {

func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
t.Parallel()
t.Cleanup(maybeDoLeakCheck)
mt := &moduleTransaction{}

err := mt.InvokeHandler(nil, 0, nil)
Expand Down Expand Up @@ -314,6 +317,7 @@ func Test_ModuleTransaction_InvokeHandler(t *testing.T) {
func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
t.Helper()
t.Parallel()
t.Cleanup(maybeDoLeakCheck)

tests := map[string]struct {
testFunc func(mock *mockModuleTransaction) (any, error)
Expand Down Expand Up @@ -904,6 +908,7 @@ func testMockModuleTransaction(t *testing.T, mt *moduleTransaction) {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
t.Cleanup(maybeDoLeakCheck)
mock := newMockModuleTransaction(&mockModuleTransaction{T: t,
Expectations: tc.mockExpectations, RetData: tc.mockRetData,
ConversationHandler: tc.conversationHandler})
Expand Down
31 changes: 31 additions & 0 deletions transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func maybeEndTransaction(t *testing.T, tx *Transaction) {
}

func TestPAM_001(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand Down Expand Up @@ -52,6 +53,7 @@ func TestPAM_001(t *testing.T) {
}

func TestPAM_002(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand Down Expand Up @@ -91,6 +93,7 @@ func (c Credentials) RespondPAM(s Style, msg string) (string, error) {
}

func TestPAM_003(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand All @@ -111,6 +114,7 @@ func TestPAM_003(t *testing.T) {
}

func TestPAM_004(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand All @@ -130,10 +134,14 @@ func TestPAM_004(t *testing.T) {
}

func TestPAM_005(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
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) {
return "secret", nil
})
Expand All @@ -155,6 +163,7 @@ func TestPAM_005(t *testing.T) {
}

func TestPAM_006(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand All @@ -177,6 +186,7 @@ func TestPAM_006(t *testing.T) {
}

func TestPAM_007(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
if u.Uid != "0" {
t.Skip("run this test as root")
Expand All @@ -202,6 +212,7 @@ func TestPAM_007(t *testing.T) {
}

func TestPAM_ConfDir(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
c := Credentials{
// the custom service always permits even with wrong password.
Expand Down Expand Up @@ -237,6 +248,7 @@ func TestPAM_ConfDir(t *testing.T) {
}

func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
if !CheckPamHasStartConfdir() {
t.Skip("this requires PAM with Conf dir support")
}
Expand Down Expand Up @@ -265,6 +277,7 @@ func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
}

func TestPAM_ConfDir_InfoMessage(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
u, _ := user.Current()
var infoText string
tx, err := StartConfDir("echo-service", u.Username,
Expand Down Expand Up @@ -297,6 +310,7 @@ func TestPAM_ConfDir_InfoMessage(t *testing.T) {
}

func TestPAM_ConfDir_Deny(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
if !CheckPamHasStartConfdir() {
t.Skip("this requires PAM with Conf dir support")
}
Expand Down Expand Up @@ -327,6 +341,7 @@ func TestPAM_ConfDir_Deny(t *testing.T) {
}

func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
c := Credentials{
User: "testuser",
// the custom service only cares about correct user name.
Expand All @@ -351,6 +366,7 @@ func TestPAM_ConfDir_PromptForUserName(t *testing.T) {
}

func TestPAM_ConfDir_WrongUserName(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
c := Credentials{
User: "wronguser",
Password: "wrongsecret",
Expand Down Expand Up @@ -378,6 +394,7 @@ func TestPAM_ConfDir_WrongUserName(t *testing.T) {
}

func TestItem(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx, err := StartFunc("passwd", "test", func(s Style, msg string) (string, error) {
return "", nil
})
Expand Down Expand Up @@ -416,6 +433,7 @@ func TestItem(t *testing.T) {
}

func TestEnv(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx, err := StartFunc("", "", func(s Style, msg string) (string, error) {
return "", nil
})
Expand Down Expand Up @@ -484,6 +502,7 @@ func TestEnv(t *testing.T) {

func Test_StatusError(t *testing.T) {
t.Parallel()
t.Cleanup(maybeDoLeakCheck)
if !CheckPamHasStartConfdir() {
t.Skip("this requires PAM with Conf dir support")
}
Expand Down Expand Up @@ -596,6 +615,7 @@ func Test_StatusError(t *testing.T) {
}

func Test_Finalizer(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
if !CheckPamHasStartConfdir() {
t.Skip("this requires PAM with Conf dir support")
}
Expand All @@ -614,6 +634,7 @@ func Test_Finalizer(t *testing.T) {
}

func Test_FinalizerNotCleanedUp(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
if !CheckPamHasStartConfdir() {
t.Skip("this requires PAM with Conf dir support")
}
Expand Down Expand Up @@ -664,6 +685,7 @@ func Test_FinalizerNotCleanedUp(t *testing.T) {
}

func TestFailure_001(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
_, err := tx.GetEnvList()
if err == nil {
Expand All @@ -672,6 +694,7 @@ func TestFailure_001(t *testing.T) {
}

func TestFailure_002(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.PutEnv("")
if err == nil {
Expand All @@ -680,6 +703,7 @@ func TestFailure_002(t *testing.T) {
}

func TestFailure_003(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.CloseSession(0)
if err == nil {
Expand All @@ -688,6 +712,7 @@ func TestFailure_003(t *testing.T) {
}

func TestFailure_004(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.OpenSession(0)
if err == nil {
Expand All @@ -696,6 +721,7 @@ func TestFailure_004(t *testing.T) {
}

func TestFailure_005(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.ChangeAuthTok(0)
if err == nil {
Expand All @@ -704,6 +730,7 @@ func TestFailure_005(t *testing.T) {
}

func TestFailure_006(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.AcctMgmt(0)
if err == nil {
Expand All @@ -712,6 +739,7 @@ func TestFailure_006(t *testing.T) {
}

func TestFailure_007(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.SetCred(0)
if err == nil {
Expand All @@ -720,6 +748,7 @@ func TestFailure_007(t *testing.T) {
}

func TestFailure_008(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.SetItem(User, "test")
if err == nil {
Expand All @@ -728,6 +757,7 @@ func TestFailure_008(t *testing.T) {
}

func TestFailure_009(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
_, err := tx.GetItem(User)
if err == nil {
Expand All @@ -736,6 +766,7 @@ func TestFailure_009(t *testing.T) {
}

func TestFailure_010(t *testing.T) {
t.Cleanup(maybeDoLeakCheck)
tx := Transaction{}
err := tx.End()
if err != nil {
Expand Down
31 changes: 31 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -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()
}
}

0 comments on commit 6fecdd0

Please sign in to comment.