Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

features: remove EPERM wrapping exception, automatically wrap errors #749

Merged
merged 2 commits into from Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 38 additions & 0 deletions features/errors.go
@@ -0,0 +1,38 @@
package features

import (
"errors"
"fmt"

"github.com/cilium/ebpf"
)

// wrapProbeErrors wraps err to prevent callers from directly comparing
// it to exported sentinels. Error rewriting in Go can be implemented by
// deferring a closure over a named error return variable. This gives the
// closure access to the stack space where the return value will be written,
// allowing it to intercept and rewrite all returns, regardless of whether
// or not the return statements use the named return variable.
//
// func foo() (err error) {
// defer func() {
// err = wrapProbeErrors(err)
// }
// return errors.New("this error will be wrapped")
// }
func wrapProbeErrors(err error) error {
if err == nil {
return nil
}

// Wrap all errors to prevent them from being compared directly
// to exported sentinels by the caller.
errStr := "%w"

if !errors.Is(err, ebpf.ErrNotSupported) {
// Wrap unexpected errors with an appropriate error string.
errStr = "unexpected error during feature probe: %w"
}

return fmt.Errorf(errStr, err)
}
26 changes: 11 additions & 15 deletions features/map.go
Expand Up @@ -2,7 +2,6 @@ package features

import (
"errors"
"fmt"
"os"
"sync"
"unsafe"
Expand Down Expand Up @@ -98,7 +97,12 @@ func createMapTypeAttr(mt ebpf.MapType) *sys.MapCreateAttr {
// HaveMapType probes the running kernel for the availability of the specified map type.
//
// See the package documentation for the meaning of the error return value.
func HaveMapType(mt ebpf.MapType) error {
func HaveMapType(mt ebpf.MapType) (err error) {
defer func() {
// This closure modifies a named return variable.
err = wrapProbeErrors(err)
}()

if err := validateMaptype(mt); err != nil {
return err
}
Expand All @@ -108,7 +112,7 @@ func HaveMapType(mt ebpf.MapType) error {

func validateMaptype(mt ebpf.MapType) error {
if mt > mt.Max() {
return fmt.Errorf("%w", os.ErrInvalid)
return os.ErrInvalid
}

if mt == ebpf.StructOpsMap {
Expand All @@ -130,6 +134,9 @@ func haveMapType(mt ebpf.MapType) error {
}

fd, err := sys.MapCreate(createMapTypeAttr(mt))
if err == nil {
fd.Close()
}

switch {
// For nested and storage map types we accept EBADF as indicator that these maps are supported
Expand All @@ -143,18 +150,7 @@ func haveMapType(mt ebpf.MapType) error {
// of the struct known by the running kernel, meaning the kernel is too old
// to support the given map type.
case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
err = fmt.Errorf("%w", ebpf.ErrNotSupported)

// EPERM is kept as-is and is not converted or wrapped.
case errors.Is(err, unix.EPERM):
break

// Wrap unexpected errors.
case err != nil:
err = fmt.Errorf("unexpected error during feature probe: %w", err)

default:
fd.Close()
err = ebpf.ErrNotSupported
}

mc.mapTypes[mt] = err
Expand Down
23 changes: 10 additions & 13 deletions features/misc.go
Expand Up @@ -78,7 +78,12 @@ func HaveV3ISA() error {

// probeMisc checks the kernel for a given supported misc by creating
// a specialized program probe and loading it.
func probeMisc(mt miscType) error {
func probeMisc(mt miscType) (err error) {
defer func() {
// This closure modifies a named return variable.
err = wrapProbeErrors(err)
}()

mc.Lock()
defer mc.Unlock()
err, ok := miscs.miscTypes[mt]
Expand All @@ -92,25 +97,17 @@ func probeMisc(mt miscType) error {
}

fd, err := sys.ProgLoad(attr)
if err == nil {
fd.Close()
}

switch {
// EINVAL occurs when attempting to create a program with an unknown type.
// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
// of the struct known by the running kernel, meaning the kernel is too old
// to support the given map type.
case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
err = fmt.Errorf("%w", ebpf.ErrNotSupported)

// EPERM is kept as-is and is not converted or wrapped.
case errors.Is(err, unix.EPERM):
break

// Wrap unexpected errors.
case err != nil:
err = fmt.Errorf("unexpected error during feature probe: %w", err)

default:
fd.Close()
err = ebpf.ErrNotSupported
}

miscs.miscTypes[mt] = err
Expand Down
51 changes: 22 additions & 29 deletions features/prog.go
Expand Up @@ -101,7 +101,12 @@ var HaveProgType = HaveProgramType
// HaveProgramType probes the running kernel for the availability of the specified program type.
//
// See the package documentation for the meaning of the error return value.
func HaveProgramType(pt ebpf.ProgramType) error {
func HaveProgramType(pt ebpf.ProgramType) (err error) {
defer func() {
// This closure modifies a named return variable.
err = wrapProbeErrors(err)
}()

if err := validateProgramType(pt); err != nil {
return err
}
Expand All @@ -112,7 +117,7 @@ func HaveProgramType(pt ebpf.ProgramType) error {

func validateProgramType(pt ebpf.ProgramType) error {
if pt > pt.Max() {
return fmt.Errorf("%w", os.ErrInvalid)
return os.ErrInvalid
}

if progLoadProbeNotImplemented(pt) {
Expand All @@ -138,25 +143,17 @@ func haveProgramType(pt ebpf.ProgramType) error {
}

fd, err := sys.ProgLoad(attr)
if fd != nil {
fd.Close()
}

switch {
// EINVAL occurs when attempting to create a program with an unknown type.
// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
// of the struct known by the running kernel, meaning the kernel is too old
// to support the given prog type.
case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
err = fmt.Errorf("%w", ebpf.ErrNotSupported)

// EPERM is kept as-is and is not converted or wrapped.
case errors.Is(err, unix.EPERM):
break

// Wrap unexpected errors.
case err != nil:
err = fmt.Errorf("unexpected error during feature probe: %w", err)

default:
fd.Close()
err = ebpf.ErrNotSupported
}

pc.types[pt] = err
Expand All @@ -177,7 +174,12 @@ func haveProgramType(pt ebpf.ProgramType) error {
// Only `nil` and `ebpf.ErrNotSupported` are conclusive.
//
// Probe results are cached and persist throughout any process capability changes.
func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) (err error) {
defer func() {
// This closure modifies a named return variable.
err = wrapProbeErrors(err)
}()

if err := validateProgramType(pt); err != nil {
return err
}
Expand All @@ -191,7 +193,7 @@ func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {

func validateProgramHelper(helper asm.BuiltinFunc) error {
if helper > helper.Max() {
return fmt.Errorf("%w", os.ErrInvalid)
return os.ErrInvalid
}

return nil
Expand All @@ -210,12 +212,11 @@ func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
}

fd, err := sys.ProgLoad(attr)

switch {
// If there is no error we need to close the FD of the prog.
case err == nil:
if fd != nil {
fd.Close()
}

switch {
// EACCES occurs when attempting to create a program probe with a helper
// while the register args when calling this helper aren't set up properly.
// We interpret this as the helper being available, because the verifier
Expand All @@ -230,15 +231,7 @@ func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
// to support the given prog type.
case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
// TODO: possibly we need to check verifier output here to be sure
err = fmt.Errorf("%w", ebpf.ErrNotSupported)

// EPERM is kept as-is and is not converted or wrapped.
case errors.Is(err, unix.EPERM):
break

// Wrap unexpected errors.
case err != nil:
err = fmt.Errorf("unexpected error during feature probe: %w", err)
err = ebpf.ErrNotSupported
}

pc.helpers[pt][helper] = err
Expand Down