From 85c223e65488c6e24832f8332e78fa55a7294dca Mon Sep 17 00:00:00 2001 From: orbisai0security Date: Mon, 11 May 2026 08:34:41 +0000 Subject: [PATCH 1/2] fix: V-003 security vulnerability Automated security fix generated by Orbis Security AI --- pkg/trace/tracer.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/trace/tracer.go b/pkg/trace/tracer.go index 4438b9e..d33be98 100644 --- a/pkg/trace/tracer.go +++ b/pkg/trace/tracer.go @@ -10,6 +10,7 @@ import ( "sync/atomic" "github.com/pkg/errors" + "golang.org/x/sys/unix" "github.com/maxgio92/xcover/internal/settings" "github.com/maxgio92/xcover/internal/utils" @@ -117,6 +118,11 @@ func (t *UserTracer) Run(ctx context.Context) error { t.logger.Debug().Msg("attaching trace to selected functions") t.attachProbe(ctx) + // Drop elevated capabilities after BPF programs are loaded and attached. + if err := dropElevatedCapabilities(); err != nil { + t.logger.Warn().Err(err).Msg("failed to drop elevated capabilities after BPF attach") + } + feedCh := make(chan []byte, feedChBufSize) eventsCh, err := t.probe.InitEventBuf(ctx) @@ -281,3 +287,18 @@ func (t *UserTracer) writeReport(reportPath string) error { return report.WriteReport(file) } + +func dropElevatedCapabilities() error { + hdr := unix.CapUserHeader{Version: unix.LINUX_CAPABILITY_VERSION_3} + var data [2]unix.CapUserData + if err := unix.Capget(&hdr, &data[0]); err != nil { + return err + } + // CAP_SYS_ADMIN (21) lives in the first 32-bit capability word. + data[0].Effective &^= uint32(1) << unix.CAP_SYS_ADMIN + data[0].Permitted &^= uint32(1) << unix.CAP_SYS_ADMIN + // CAP_BPF (39) lives in the second 32-bit capability word. + data[1].Effective &^= uint32(1) << (unix.CAP_BPF - 32) + data[1].Permitted &^= uint32(1) << (unix.CAP_BPF - 32) + return unix.Capset(&hdr, &data[0]) +} From e3918e52ad4b045d6661d5858aedb846ab4782f2 Mon Sep 17 00:00:00 2001 From: OrbisAI Security Date: Thu, 4 Jun 2026 20:54:17 +0530 Subject: [PATCH 2/2] fix: use AllThreadsSyscall6 for process-wide capability drop capset() only affects the calling OS thread. With Go's M:N scheduler, goroutines can migrate between threads, leaving other threads with elevated capabilities. Use syscall.AllThreadsSyscall6 to execute capset on all runtime threads atomically. Co-Authored-By: Claude Opus 4.6 --- pkg/trace/tracer.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/trace/tracer.go b/pkg/trace/tracer.go index d33be98..cc68cc3 100644 --- a/pkg/trace/tracer.go +++ b/pkg/trace/tracer.go @@ -8,6 +8,8 @@ import ( "os" "sync" "sync/atomic" + "syscall" + "unsafe" "github.com/pkg/errors" "golang.org/x/sys/unix" @@ -294,11 +296,23 @@ func dropElevatedCapabilities() error { if err := unix.Capget(&hdr, &data[0]); err != nil { return err } - // CAP_SYS_ADMIN (21) lives in the first 32-bit capability word. + data[0].Effective &^= uint32(1) << unix.CAP_SYS_ADMIN data[0].Permitted &^= uint32(1) << unix.CAP_SYS_ADMIN - // CAP_BPF (39) lives in the second 32-bit capability word. data[1].Effective &^= uint32(1) << (unix.CAP_BPF - 32) data[1].Permitted &^= uint32(1) << (unix.CAP_BPF - 32) - return unix.Capset(&hdr, &data[0]) + + // Apply to ALL OS threads in the process, not just the calling thread. + // This is necessary because Go's scheduler can move goroutines between + // threads, and capset() alone only affects the current thread. + _, _, errno := syscall.AllThreadsSyscall6( + unix.SYS_CAPSET, + uintptr(unsafe.Pointer(&hdr)), + uintptr(unsafe.Pointer(&data[0])), + 0, 0, 0, 0, + ) + if errno != 0 { + return errno + } + return nil }