Skip to content

devices: drop cilium/ebpf{,link} deps#64

Open
kolyshkin wants to merge 1 commit into
opencontainers:mainfrom
kolyshkin:drop-cilium-ebpf
Open

devices: drop cilium/ebpf{,link} deps#64
kolyshkin wants to merge 1 commit into
opencontainers:mainfrom
kolyshkin:drop-cilium-ebpf

Conversation

@kolyshkin

@kolyshkin kolyshkin commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Replace the use of the cilium/ebpf and cilium/ebpf/link with direct
bpf(2) syscalls. Keep cilium/ebpf/asm for instruction assembly.

Notes:

  • The eBPF device-filter programs are now tracked by raw file
    descriptors instead of *ebpf.Program handles;
  • asm.Instructions.Marshal requires a concrete binary.LittleEndian or
    binary.BigEndian, thus endian_{le,be}.go are introduced as a
    workaround.

This reduces the runc binary size by about ~1M.

Being tested in opencontainers/runc#5340.


For initial discussion about this, see opencontainers/runc#5218.

@kolyshkin kolyshkin requested a review from a team as a code owner June 23, 2026 06:30
@kolyshkin kolyshkin force-pushed the drop-cilium-ebpf branch 2 times, most recently from d5f40e9 to 733c596 Compare June 23, 2026 06:56
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Jun 23, 2026
…ranch

Temporarily point the opencontainers/cgroups dependency at the
drop-cilium-ebpf branch (opencontainers/cgroups#64) via a go.mod replace,
and re-vendor, so CI can exercise the cilium/ebpf main+link package
removal end-to-end in runc.

This must not be merged: the replace directive points at a personal fork
branch. Once opencontainers/cgroups#64 lands and is tagged, this should be
replaced by a normal dependency bump.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Jun 23, 2026
Let's drop the second biggest (by size) runc dependency and reduce the
binary size by another ~1MB.

Draft/DNM until opencontainers/cgroups#64 is merged/released.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the devices eBPF cgroup-device filter implementation to stop importing github.com/cilium/ebpf’s main and link packages, and instead perform the required operations via direct bpf(2) syscalls while continuing to use github.com/cilium/ebpf/asm for instruction assembly. This aligns with the stated goal of reducing consumer binary size by avoiding heavy transitive dependencies.

Changes:

  • Replaced cilium/ebpf program/link usage with thin wrappers around BPF_PROG_* commands and raw program fds.
  • Updated cgroup device filter attach/query logic to operate on fds (with explicit closes) instead of *ebpf.Program.
  • Added nativeEndian selection via build-tagged endian-specific files to satisfy asm.Instructions.Marshal requirements.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.

File Description
go.sum Removes now-unused transitive dependencies after dropping cilium/ebpf main/link usage.
devices/endian_le.go Provides nativeEndian = binary.LittleEndian under little-endian arch build tags.
devices/endian_be.go Provides nativeEndian = binary.BigEndian under big-endian arch build tags.
devices/ebpf_linux.go Replaces ebpf/link usage with direct bpf(2) syscall wrappers and fd-based program management.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread devices/ebpf_linux.go
Comment on lines +159 to 160
_, err := bpf(unix.BPF_PROG_QUERY, unsafe.Pointer(&query), unsafe.Sizeof(query))
size = int(query.ProgCnt)
Comment thread devices/ebpf_linux.go
Comment on lines +304 to 307
err = bpfProgAttach(dirFd, progFd, attachFlags, replaceFd)
if err != nil {
return fmt.Errorf("failed to call BPF_PROG_ATTACH (BPF_CGROUP_DEVICE, BPF_F_ALLOW_MULTI): %w", err)
}
@kolyshkin kolyshkin changed the title devices: drop cilium/ebpf main and link package dependency devices: drop cilium/ebpf{,link} deps Jun 23, 2026
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Jun 23, 2026
Let's drop the second biggest (by size) runc dependency and reduce the
binary size by another ~1MB.

Draft/DNM until opencontainers/cgroups#64 is merged/released.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Jun 23, 2026
Let's drop the second biggest (by size) runc dependency and reduce the
binary size by another ~1MB.

Draft/DNM until opencontainers/cgroups#64 is merged/released.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Comment thread devices/ebpf_linux.go
Comment on lines -102 to -103
//
// TODO: move this logic to cilium/ebpf

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there anything in our code that would still be worth upstreaming (even if we don't use it?) not sure how generic our changes are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was/is there, just not as a public API.

I guess the only "unique" code we have here is how we use BPF_PROG_REPLACE. Also, it's not too much code.

See also opencontainers/runc#5218 (comment)

Comment thread devices/endian_be.go
Comment on lines +7 to +9
// nativeEndian is used as a workaround for cilium/ebpf/asm
// which does not accept binary.NativeEndian.
var nativeEndian = binary.BigEndian

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unfortunate; makes our code more brittle (if there would ever be more build-tags added). Would this be something that could be fixed in ebpf?

My AI-buddy suggested something like this could work (but haven't verified);

func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
	var b [2]byte
	bo.PutUint16(b[:], 0x0102)

	switch b {
	case [2]byte{0x02, 0x01}: // little
		return bpfRegisters((src << 4) | (dst & 0xf)), nil
	case [2]byte{0x01, 0x02}: // big
		return bpfRegisters((dst << 4) | (src & 0xf)), nil
	default:
		return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
	}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I guess we could do this locally as well if we don't want to depend on our build-tags being complete;

var nativeEndian = detectNativeEndian()

func detectNativeEndian() binary.ByteOrder {
	var b [2]byte
	binary.NativeEndian.PutUint16(b[:], 0x0102)

	switch b {
	case [2]byte{0x02, 0x01}:
		return binary.LittleEndian
	case [2]byte{0x01, 0x02}:
		return binary.BigEndian
	default:
		panic("unreachable: invalid native byte order")
	}
}

or

var nativeEndian = detectNativeEndian()

func detectNativeEndian() binary.ByteOrder {
	var x uint16 = 0x0102
	if *(*byte)(unsafe.Pointer(&x)) == 0x02 {
		return binary.LittleEndian
	}
	return binary.BigEndian
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I had a similar code but I prefer to determine that during compile time rather than run time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if a whole new architecture is to be added to Golang, fixing this code is trivial.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, we can have a runtime test to ensure our endian-ness is correct.

Added.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I prefer to determine that during compile time rather than run time.

Yes, generally agreed. I wish there was a builtin endian-ness build-tag or something (and, yes, initially I thought; why not use binary.NativeEndian before realising ... why we had to do this.

The long list of platforms made me hesitate a bit (easy to miss one!)

But, we can have a runtime test to ensure our endian-ness is correct.

Works for me (for now); still would be great if upstream cilium didn't require us to do this 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still would be great if upstream cilium didn't require us to do this 😅

I tried: cilium/ebpf#1523

Replace the use of the cilium/ebpf and cilium/ebpf/link with direct
bpf(2) syscalls. Keep cilium/ebpf/asm for instruction assembly.

Notes:
 - The eBPF device-filter programs are now tracked by raw file
   descriptors instead of *ebpf.Program handles;
 - asm.Instructions.Marshal requires a concrete binary.LittleEndian or
   binary.BigEndian, thus endian_{le,be}.go are introduced as a
   workaround.

This reduces the runc binary size by about ~1M.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
@kolyshkin

Copy link
Copy Markdown
Contributor Author

@cyphar @rata PTAL 🙏🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants