Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 54 additions & 4 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ type ProgramInfo struct {
btf btf.ID
loadTime time.Duration

restricted bool

maps []MapID
insns []byte
jitedSize uint32
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -599,13 +620,20 @@ 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) {
if platform.IsWindows && len(pi.insns) == 0 {
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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down
4 changes: 4 additions & 0 deletions internal/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions internal/kallsyms/kallsyms.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ func assignAddresses(f io.Reader, symbols map[string]uint64) error {
return fmt.Errorf("symbol %s(0x%x): duplicate found at address 0x%x: %w", s.name, existing, s.addr, errAmbiguousKsym)
}
if requested {
// Reading a symbol with a zero address is a strong indication that
// kptr_restrict is set and the process doesn't have CAP_SYSLOG, or
// kptr_restrict is set to 2 (never show addresses).
//
// When running the kernel with KASLR disabled (like CI kernels running in
// microVMs), kallsyms will display many absolute symbols at address 0.
// This memory is unlikely to contain anything useful, and production
// machines are unlikely to run without KASLR.
//
// Return a helpful error instead of silently returning zero addresses.
if s.addr == 0 {
return fmt.Errorf("symbol %s: %w", s.name, internal.ErrRestrictedKernel)
}
symbols[string(s.name)] = s.addr
}
}
Expand Down
4 changes: 4 additions & 0 deletions internal/sys/ptr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)}
Expand Down
12 changes: 10 additions & 2 deletions linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,11 @@ func resolveKsymReferences(insns asm.Instructions) error {
return nil
}

if err := kallsyms.AssignAddresses(symbols); err != nil {
return fmt.Errorf("resolve ksym addresses: %w", err)
err := kallsyms.AssignAddresses(symbols)
// Tolerate ErrRestrictedKernel during initial lookup, user may have all weak
// ksyms and a fallback path.
if err != nil && !errors.Is(err, ErrRestrictedKernel) {
return fmt.Errorf("resolve ksyms: %w", err)
}

var missing []string
Expand All @@ -542,6 +545,11 @@ func resolveKsymReferences(insns asm.Instructions) error {
}

if len(missing) > 0 {
if err != nil {
// Program contains required ksyms, return the error from above.
return fmt.Errorf("resolve required ksyms: %s: %w", strings.Join(missing, ","), err)
}

return fmt.Errorf("kernel is missing symbol: %s", strings.Join(missing, ","))
}

Expand Down
Loading