]> git.feebdaed.xyz Git - 0xmirror/ebpf.git/commitdiff
info: return ErrRestrictedKernel when program info is restricted
authorDylan Reimerink <dylan.reimerink@isovalent.com>
Wed, 27 Aug 2025 16:20:06 +0000 (18:20 +0200)
committerTimo Beckers <ti-mo@users.noreply.github.com>
Wed, 15 Oct 2025 10:20:26 +0000 (12:20 +0200)
On systems where the `kernel.kptr_restrict` and
`net.core.bpf_jit_harden` sysctls are enabled, certain fields in the
program info may be restricted. When this is the case xlated and jitted
instruction, line info, and function info are unavailable.

When such fields are unavailable, we do get *Len fields set to non-zero
values, but the kernel will have not written any data to the pointers
provided.

At present when this happens we don't recognize this and attempt to
parse the empty buffers resulting in a `parse func info: offset 0:
type ID 0 is a *btf.Void, but expected a Func` error.

The only clue we have that this is happening is that the kernel will
zero out the insns pointer. This commit adds logic to detect when
this happens and then returns a dedicated `ErrRestrictedKernel` error
so this case can be handled gracefully.

Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
Co-authored-by: Timo Beckers <timo@isovalent.com>
info.go
internal/feature.go
internal/sys/ptr.go

diff --git a/info.go b/info.go
index b15202a0f5c52231eae7b09109ec1e01fc38bbfa..23c819aaa78a9311cc31c45a24acc7df15144739 100644 (file)
--- a/info.go
+++ b/info.go
@@ -301,6 +301,8 @@ type ProgramInfo struct {
        btf              btf.ID
        loadTime         time.Duration
 
+       restricted bool
+
        maps                 []MapID
        insns                []byte
        jitedSize            uint32
@@ -477,6 +479,14 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
                }
        }
 
+       if info.XlatedProgLen > 0 && info2.XlatedProgInsns.IsNil() {
+               pi.restricted = true
+               pi.insns = nil
+               pi.lineInfos = nil
+               pi.funcInfos = nil
+               pi.jitedInfo = programJitedInfo{}
+       }
+
        return &pi, nil
 }
 
@@ -556,14 +566,25 @@ func (pi *ProgramInfo) btfSpec() (*btf.Spec, error) {
        return spec, nil
 }
 
+// ErrRestrictedKernel is returned when kernel address information is restricted
+// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.
+var ErrRestrictedKernel = internal.ErrRestrictedKernel
+
 // LineInfos returns the BTF line information of the program.
 //
 // Available from 5.0.
 //
+// Returns an error wrapping [ErrRestrictedKernel] if line infos are restricted
+// by sysctls.
+//
 // Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns
 // ErrNotSupported if the program was created without BTF or if the kernel
 // doesn't support the field.
 func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) {
+       if pi.restricted {
+               return nil, fmt.Errorf("line infos: %w", ErrRestrictedKernel)
+       }
+
        if len(pi.lineInfos) == 0 {
                return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
        }
@@ -599,6 +620,9 @@ func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) {
 // this metadata requires CAP_SYS_ADMIN or equivalent. If capability is
 // unavailable, the instructions will be returned without metadata.
 //
+// Returns an error wrapping [ErrRestrictedKernel] if instructions are
+// restricted by sysctls.
+//
 // Available from 4.13. Requires CAP_BPF or equivalent for plain instructions.
 // Requires CAP_SYS_ADMIN for instructions with metadata.
 func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
@@ -606,6 +630,10 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
                return nil, fmt.Errorf("read instructions: %w", internal.ErrNotSupportedOnOS)
        }
 
+       if pi.restricted {
+               return nil, fmt.Errorf("instructions: %w", ErrRestrictedKernel)
+       }
+
        // If the calling process is not BPF-capable or if the kernel doesn't
        // support getting xlated instructions, the field will be zero.
        if len(pi.insns) == 0 {
@@ -671,22 +699,37 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
        return insns, nil
 }
 
-// JitedSize returns the size of the program's JIT-compiled machine code in bytes, which is the
-// actual code executed on the host's CPU. This field requires the BPF JIT compiler to be enabled.
+// JitedSize returns the size of the program's JIT-compiled machine code in
+// bytes, which is the actual code executed on the host's CPU. This field
+// requires the BPF JIT compiler to be enabled.
+//
+// Returns an error wrapping [ErrRestrictedKernel] if jited program size is
+// restricted by sysctls.
 //
 // Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
 func (pi *ProgramInfo) JitedSize() (uint32, error) {
+       if pi.restricted {
+               return 0, fmt.Errorf("jited size: %w", ErrRestrictedKernel)
+       }
+
        if pi.jitedSize == 0 {
                return 0, fmt.Errorf("insufficient permissions, unsupported kernel, or JIT compiler disabled: %w", ErrNotSupported)
        }
        return pi.jitedSize, nil
 }
 
-// TranslatedSize returns the size of the program's translated instructions in bytes, after it has
-// been verified and rewritten by the kernel.
+// TranslatedSize returns the size of the program's translated instructions in
+// bytes, after it has been verified and rewritten by the kernel.
+//
+// Returns an error wrapping [ErrRestrictedKernel] if translated instructions
+// are restricted by sysctls.
 //
 // Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.
 func (pi *ProgramInfo) TranslatedSize() (int, error) {
+       if pi.restricted {
+               return 0, fmt.Errorf("xlated size: %w", ErrRestrictedKernel)
+       }
+
        insns := len(pi.insns)
        if insns == 0 {
                return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
@@ -782,10 +825,17 @@ func (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) {
 //
 // Available from 5.0.
 //
+// Returns an error wrapping [ErrRestrictedKernel] if function information is
+// restricted by sysctls.
+//
 // Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns
 // ErrNotSupported if the program was created without BTF or if the kernel
 // doesn't support the field.
 func (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) {
+       if pi.restricted {
+               return nil, fmt.Errorf("func infos: %w", ErrRestrictedKernel)
+       }
+
        if len(pi.funcInfos) == 0 {
                return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
        }
index 82b8d93956ae9bbf927283cce69df9df1166c6c3..e27064c23e4ae6d4f40e35b4f5b204a8b61b80ae 100644 (file)
@@ -16,6 +16,10 @@ var ErrNotSupported = errors.New("not supported")
 // operating system.
 var ErrNotSupportedOnOS = fmt.Errorf("%w on %s", ErrNotSupported, runtime.GOOS)
 
+// ErrRestrictedKernel is returned when kernel address information is restricted
+// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.
+var ErrRestrictedKernel = errors.New("restricted by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls")
+
 // UnsupportedFeatureError is returned by FeatureTest() functions.
 type UnsupportedFeatureError struct {
        // The minimum version required for this feature.
index 173665c2a9dcdc3303ee2af0e5f1c2b6fdfd7112..aa6c2e91aa2eb3a3ff06b31ce70be7e1958dac33 100644 (file)
@@ -28,6 +28,10 @@ type TypedPointer[T any] struct {
        ptr Pointer
 }
 
+func (p TypedPointer[T]) IsNil() bool {
+       return p.ptr.ptr == nil
+}
+
 // SlicePointer creates a [TypedPointer] from a slice.
 func SlicePointer[T comparable](s []T) TypedPointer[T] {
        return TypedPointer[T]{ptr: UnsafeSlicePointer(s)}