Skip to content

Commit

Permalink
chore(feat): probe bpf helper improve
Browse files Browse the repository at this point in the history
- create type BPFFunc in libbpfgo;
- improve tests with capabilities.
  • Loading branch information
rscampos committed Jul 11, 2024
1 parent 73f0dea commit 73c0323
Show file tree
Hide file tree
Showing 5 changed files with 830 additions and 34 deletions.
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ module github.com/aquasecurity/libbpfgo

go 1.21

require github.com/stretchr/testify v1.9.0
require (
github.com/stretchr/testify v1.9.0
kernel.org/pub/linux/libs/security/libcap/cap v1.2.70
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 h1:QnLPkuDWWbD5C+3DUA2IUXai5TK6w2zff+MAGccqdsw=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.70/go.mod h1:/iBwcj9nbLejQitYvUm9caurITQ6WyNHibJk6Q9fiS4=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.70/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
175 changes: 149 additions & 26 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,56 +1,179 @@
package libbpfgo

import (
"errors"
"fmt"
"syscall"
"testing"

"github.com/aquasecurity/libbpfgo/helpers"
"kernel.org/pub/linux/libs/security/libcap/cap"
)

// Reset only effective capabilites
func resetEffectiveCapabilities() error {
// current capability
existing := cap.GetProc()

// Clear all effective capabilites
if err := existing.ClearFlag(cap.Effective); err != nil {
return fmt.Errorf("error cleaning effective capabilites %w", err)
}

// set updated capabilitis to current process
if err := existing.SetProc(); err != nil {
return fmt.Errorf("error during update capabilites %w", err)
}

return nil
}

// Enforce effective capabilites only
func enforceEffectiveCapabilities(newCap []string) error {
existing := cap.GetProc()

// create a new empty capabilities
enforce := cap.NewSet()

// copy all/only permitted flags to new cap
enforce.FillFlag(cap.Permitted, existing, cap.Permitted)

values := []cap.Value{}

for _, name := range newCap {
value, err := cap.FromName(name)
if err != nil {
return fmt.Errorf("error getting capability %q: %w", name, err)
}

values = append(values, value)
}

// only set the given effetive capabilities
if err := enforce.SetFlag(cap.Effective, true, values...); err != nil {
return fmt.Errorf("error setting effective capabilities: %w", err)
}

if err := enforce.SetProc(); err != nil {
return fmt.Errorf("failed to drop capabilities: %q -> %q: %w", existing, enforce, err)
}

return nil
}

func TestFuncSupportbyType(t *testing.T) {
tt := []struct {
progType BPFProgType
funcId helpers.BPFFunc
supported bool
progType BPFProgType
funcId BPFFunc
supported bool
capability []string
errMsg error
}{
// func available but not enough permission (permission denied)
{
progType: BPFProgTypeKprobe,
funcId: BPFFuncGetCurrentUidGid,
supported: false,
capability: []string{},
errMsg: syscall.EPERM,
},
// func available and enough permission
{
progType: BPFProgTypeKprobe,
funcId: BPFFuncGetCurrentUidGid,
supported: true,
capability: []string{"cap_bpf", "cap_perfmon"},
errMsg: nil,
},
// func unavailable and enough permission
// When the function is unavailable, BPF returns "Invalid Argument".
// Therefore, ignore the error and proceed with validation.
{
progType: BPFProgTypeSkLookup,
funcId: BPFFuncGetCurrentCgroupId,
supported: false,
capability: []string{"cap_sys_admin"},
errMsg: syscall.EINVAL,
},
{
progType: BPFProgTypeKprobe,
funcId: helpers.BPFFuncMapLookupElem,
supported: true,
progType: BPFProgTypeSkLookup,
funcId: BPFFuncGetCurrentCgroupId,
supported: false,
capability: []string{},
errMsg: syscall.EPERM,
},
{
progType: BPFProgTypeKprobe,
funcId: helpers.BPFFuncLoop,
supported: true,
progType: BPFProgTypeKprobe,
funcId: BPFFuncKtimeGetNs,
supported: true,
capability: []string{"cap_bpf", "cap_perfmon"},
errMsg: nil,
},
{
progType: BPFProgTypeKprobe,
funcId: helpers.BPFFuncKtimeGetNs,
supported: true,
progType: BPFProgTypeKprobe,
funcId: BPFFuncKtimeGetNs,
supported: true,
capability: []string{"cap_sys_admin"},
errMsg: nil,
},
{
progType: BPFProgTypeKprobe,
funcId: helpers.BPFFuncSysBpf,
supported: false,
progType: BPFProgTypeKprobe,
funcId: BPFFuncSysBpf,
supported: false,
capability: []string{"cap_bpf", "cap_perfmon"},
errMsg: syscall.EINVAL,
},
{
progType: BPFProgTypeLsm,
funcId: helpers.BPFFuncGetCurrentCgroupId,
supported: false,
progType: BPFProgTypeSyscall,
funcId: BPFFuncGetCgroupClassid,
supported: false,
capability: []string{"cap_bpf"},
errMsg: syscall.EINVAL,
},
// Not able to probe helpers for some types (even with permission)
// https://github.com/libbpf/libbpf/blob/c1a6c770c46c6e78ad6755bf596c23a4e6f6b216/src/libbpf_probes.c#L430-L441
{
progType: BPFProgTypeSkLookup,
funcId: helpers.BPFFuncGetCurrentCgroupId,
supported: true,
progType: BPFProgTypeLsm,
funcId: BPFFuncGetCurrentCgroupId,
supported: false,
capability: []string{"cap_bpf", "cap_perfmon"},
errMsg: syscall.EOPNOTSUPP,
},
{
progType: BPFProgTypeKprobe,
funcId: helpers.BPFFuncSockMapUpdate,
supported: false,
progType: BPFProgTypeLsm,
funcId: BPFFuncGetCurrentCgroupId,
supported: false,
capability: []string{},
errMsg: syscall.EOPNOTSUPP,
},
{
progType: BPFProgTypeKprobe,
funcId: BPFFuncSockMapUpdate,
supported: false,
capability: []string{"cap_sys_admin"},
errMsg: syscall.EINVAL,
},
}

for _, tc := range tt {
support, _ := BPFHelperIsSupported(tc.progType, tc.funcId)
// reset all current effective capabilities
resetEffectiveCapabilities()

if tc.capability != nil {
enforceEffectiveCapabilities(tc.capability)
}

support, err := BPFHelperIsSupported(tc.progType, tc.funcId)

if tc.errMsg == nil {
if err != nil {
t.Errorf("expected no error, got %v", err)
}
} else {
if !errors.Is(err, tc.errMsg) {
t.Errorf("expected error %v, got %v", tc.errMsg, err)
}
}

// This may fail if the bpf helper support for a specific program changes in future.
if support != tc.supported {
t.Errorf("expected %v, got %v", tc.supported, support)
Expand Down
20 changes: 13 additions & 7 deletions libbpfgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import "C"
import (
"fmt"
"syscall"

"github.com/aquasecurity/libbpfgo/helpers"
)

//
Expand Down Expand Up @@ -98,13 +96,21 @@ func BPFMapTypeIsSupported(mapType MapType) (bool, error) {
return supportedC == 1, nil
}

func BPFHelperIsSupported(progType BPFProgType, funcId helpers.BPFFunc) (bool, error) {
supportedC := C.libbpf_probe_bpf_helper(C.enum_bpf_prog_type(int(progType)), C.enum_bpf_func_id(int(funcId)), nil)
if supportedC < 0 {
return false, syscall.Errno(-supportedC)
// BPFHelperIsSupported checks if a BPF helper function is supported for a given program type.
// Specific capabilities are required depending on the program type to probe the bpf helper function.
func BPFHelperIsSupported(progType BPFProgType, funcId BPFFunc) (bool, error) {
retC, errno := C.libbpf_probe_bpf_helper(C.enum_bpf_prog_type(int(progType)), C.enum_bpf_func_id(int(funcId)), nil)

if errno != nil {
return false, fmt.Errorf("operation failed for function `%s` with program type `%s`: %w", funcId, progType, errno)
}

return supportedC == 1, nil
// helper not supported
if retC < 0 {
return false, fmt.Errorf("operation failed for function `%s` with program type `%s`: %w", funcId, progType, syscall.Errno(-retC))
}

return retC == 1, nil
}

//
Expand Down
Loading

0 comments on commit 73c0323

Please sign in to comment.