mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
gVisor: Read syscall seccheck enablement as part of per-syscall flags.
Prior to this CL, each of the 4 syscall points (raw enter, enter, raw exit, exit) each resulted in 1 atomic read, on top of the existing atomic read to read non-seccheck-related per-syscall flags. This CL propagates seccheck's syscall-related points to the existing per-syscall flags bitfield, such that this data can be loaded in a single atomic read per syscall, rather than 5. This shaves off a few precious nanoseconds (-3%) from the hot syscall path. PiperOrigin-RevId: 463190096
This commit is contained in:
committed by
gVisor bot
parent
d542d45a28
commit
a96af5dd61
@@ -113,6 +113,18 @@ const (
|
||||
|
||||
// ExternalAfterEnable enables the external hook after syscall execution.
|
||||
ExternalAfterEnable
|
||||
|
||||
// SecCheckEnter represents a schematized/enter syscall seccheck event.
|
||||
SecCheckEnter
|
||||
|
||||
// SecCheckExit represents a schematized/exit syscall seccheck event.
|
||||
SecCheckExit
|
||||
|
||||
// SecCheckRawEnter represents raw/enter syscall seccheck event.
|
||||
SecCheckRawEnter
|
||||
|
||||
// SecCheckRawExit represents raw/exit syscall seccheck event.
|
||||
SecCheckRawExit
|
||||
)
|
||||
|
||||
// StraceEnableBits combines both strace log and event flags.
|
||||
@@ -142,7 +154,48 @@ type SyscallFlagsTable struct {
|
||||
// max is the largest syscall number in table.
|
||||
func (e *SyscallFlagsTable) init(table map[uintptr]Syscall) {
|
||||
for num := range table {
|
||||
e.enable[num] = atomicbitops.FromUint32(syscallPresent)
|
||||
enableFlags := uint32(syscallPresent)
|
||||
e.enable[num] = atomicbitops.FromUint32(enableFlags)
|
||||
}
|
||||
seccheck.Global.AddSyscallFlagListener(e)
|
||||
e.UpdateSecCheck(&seccheck.Global)
|
||||
}
|
||||
|
||||
// UpdateSecCheck implements seccheck.SyscallFlagListener.
|
||||
//
|
||||
// It is called when per-syscall seccheck event enablement changes.
|
||||
func (e *SyscallFlagsTable) UpdateSecCheck(state *seccheck.State) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
for sysno := uintptr(0); sysno < maxSyscallNum; sysno++ {
|
||||
oldFlags := e.enable[sysno].Load()
|
||||
if !bits.IsOn32(oldFlags, syscallPresent) {
|
||||
continue
|
||||
}
|
||||
flags := oldFlags
|
||||
if state.SyscallEnabled(seccheck.SyscallEnter, sysno) {
|
||||
flags |= SecCheckEnter
|
||||
} else {
|
||||
flags &^= SecCheckEnter
|
||||
}
|
||||
if state.SyscallEnabled(seccheck.SyscallExit, sysno) {
|
||||
flags |= SecCheckExit
|
||||
} else {
|
||||
flags &^= SecCheckExit
|
||||
}
|
||||
if state.SyscallEnabled(seccheck.SyscallRawEnter, sysno) {
|
||||
flags |= SecCheckRawEnter
|
||||
} else {
|
||||
flags &^= SecCheckRawEnter
|
||||
}
|
||||
if state.SyscallEnabled(seccheck.SyscallRawExit, sysno) {
|
||||
flags |= SecCheckRawExit
|
||||
} else {
|
||||
flags &^= SecCheckRawExit
|
||||
}
|
||||
if flags != oldFlags {
|
||||
e.enable[sysno].Store(flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,13 +204,12 @@ func (e *SyscallFlagsTable) Word(sysno uintptr) uint32 {
|
||||
if sysno <= maxSyscallNum {
|
||||
return e.enable[sysno].Load()
|
||||
}
|
||||
|
||||
return e.missingEnable.Load()
|
||||
}
|
||||
|
||||
// Enable sets enable bit bit for all syscalls based on s.
|
||||
// Enable sets enable bit `bit` for all syscalls based on s.
|
||||
//
|
||||
// Syscalls missing from s are disabled.
|
||||
// Syscalls missing from `s` are disabled.
|
||||
//
|
||||
// Syscalls missing from the initial table passed to Init cannot be added as
|
||||
// individual syscalls. If present in s they will be ignored.
|
||||
|
||||
@@ -90,7 +90,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
straceContext = s.Stracer.SyscallEnter(t, sysno, args, fe)
|
||||
}
|
||||
|
||||
if seccheck.Global.SyscallEnabled(seccheck.SyscallRawEnter, sysno) {
|
||||
if bits.IsAnyOn32(fe, SecCheckRawEnter) {
|
||||
info := pb.Syscall{
|
||||
Sysno: uint64(sysno),
|
||||
Arg1: args[0].Uint64(),
|
||||
@@ -109,7 +109,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
return c.RawSyscall(t, fields, &info)
|
||||
})
|
||||
}
|
||||
if seccheck.Global.SyscallEnabled(seccheck.SyscallEnter, sysno) {
|
||||
if bits.IsAnyOn32(fe, SecCheckEnter) {
|
||||
fields := seccheck.Global.GetFieldSet(seccheck.GetPointForSyscall(seccheck.SyscallEnter, sysno))
|
||||
var ctxData *pb.ContextData
|
||||
if !fields.Context.Empty() {
|
||||
@@ -120,7 +120,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
Sysno: sysno,
|
||||
Args: args,
|
||||
}
|
||||
cb := t.SyscallTable().LookupSyscallToProto(sysno)
|
||||
cb := s.LookupSyscallToProto(sysno)
|
||||
msg, msgType := cb(t, fields, ctxData, info)
|
||||
seccheck.Global.SentToSinks(func(c seccheck.Sink) error {
|
||||
return c.Syscall(t, fields, ctxData, msgType, msg)
|
||||
@@ -158,7 +158,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
s.Stracer.SyscallExit(straceContext, t, sysno, rval, err)
|
||||
}
|
||||
|
||||
if seccheck.Global.SyscallEnabled(seccheck.SyscallRawExit, sysno) {
|
||||
if bits.IsAnyOn32(fe, SecCheckRawExit) {
|
||||
info := pb.Syscall{
|
||||
Sysno: uint64(sysno),
|
||||
Arg1: args[0].Uint64(),
|
||||
@@ -181,7 +181,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
return c.RawSyscall(t, fields, &info)
|
||||
})
|
||||
}
|
||||
if seccheck.Global.SyscallEnabled(seccheck.SyscallExit, sysno) {
|
||||
if bits.IsAnyOn32(fe, SecCheckExit) {
|
||||
fields := seccheck.Global.GetFieldSet(seccheck.GetPointForSyscall(seccheck.SyscallExit, sysno))
|
||||
var ctxData *pb.ContextData
|
||||
if !fields.Context.Empty() {
|
||||
@@ -195,7 +195,7 @@ func (t *Task) executeSyscall(sysno uintptr, args arch.SyscallArguments) (rval u
|
||||
Rval: rval,
|
||||
Errno: ExtractErrno(err, int(sysno)),
|
||||
}
|
||||
cb := t.SyscallTable().LookupSyscallToProto(sysno)
|
||||
cb := s.LookupSyscallToProto(sysno)
|
||||
msg, msgType := cb(t, fields, ctxData, info)
|
||||
seccheck.Global.SentToSinks(func(c seccheck.Sink) error {
|
||||
return c.Syscall(t, fields, ctxData, msgType, msg)
|
||||
|
||||
@@ -30,7 +30,8 @@ type Point uint
|
||||
// PointX represents the checkpoint X.
|
||||
const (
|
||||
totalPoints = int(pointLengthBeforeSyscalls) + syscallPoints
|
||||
numPointBitmaskUint32s = (totalPoints-1)/32 + 1
|
||||
numPointsPerUint32 = 32
|
||||
numPointBitmaskUint32s = (totalPoints-1)/numPointsPerUint32 + 1
|
||||
)
|
||||
|
||||
// FieldSet contains all optional fields to be collected by a given Point.
|
||||
@@ -209,6 +210,12 @@ type State struct {
|
||||
// Mutation of sinks is serialized by registrationMu.
|
||||
sinks []Sink
|
||||
|
||||
// syscallFlagListeners is the set of registered SyscallFlagListeners.
|
||||
//
|
||||
// They are notified when the enablement of a syscall point changes.
|
||||
// Mutation of syscallFlagListeners is serialized by registrationMu.
|
||||
syscallFlagListeners []SyscallFlagListener
|
||||
|
||||
pointFields map[Point]FieldSet
|
||||
}
|
||||
|
||||
@@ -223,20 +230,38 @@ func (s *State) AppendSink(c Sink, reqs []PointReq) {
|
||||
if s.pointFields == nil {
|
||||
s.pointFields = make(map[Point]FieldSet)
|
||||
}
|
||||
updateSyscalls := false
|
||||
for _, req := range reqs {
|
||||
word, bit := req.Pt/32, req.Pt%32
|
||||
word, bit := req.Pt/numPointsPerUint32, req.Pt%numPointsPerUint32
|
||||
s.enabledPoints[word].Store(s.enabledPoints[word].RacyLoad() | (uint32(1) << bit))
|
||||
|
||||
if req.Pt >= pointLengthBeforeSyscalls {
|
||||
updateSyscalls = true
|
||||
}
|
||||
s.pointFields[req.Pt] = req.Fields
|
||||
}
|
||||
if updateSyscalls {
|
||||
for _, listener := range s.syscallFlagListeners {
|
||||
listener.UpdateSecCheck(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) clearSink() {
|
||||
s.registrationMu.Lock()
|
||||
defer s.registrationMu.Unlock()
|
||||
|
||||
updateSyscalls := false
|
||||
for i := range s.enabledPoints {
|
||||
s.enabledPoints[i].Store(0)
|
||||
// We use i+1 here because we want to check the last bit that may have been changed within i.
|
||||
if Point((i+1)*numPointsPerUint32) >= pointLengthBeforeSyscalls {
|
||||
updateSyscalls = true
|
||||
}
|
||||
}
|
||||
if updateSyscalls {
|
||||
for _, listener := range s.syscallFlagListeners {
|
||||
listener.UpdateSecCheck(s)
|
||||
}
|
||||
}
|
||||
s.pointFields = nil
|
||||
|
||||
@@ -249,9 +274,18 @@ func (s *State) clearSink() {
|
||||
}
|
||||
}
|
||||
|
||||
// AddSyscallFlagListener adds a listener to the State.
|
||||
//
|
||||
// The listener will be notified whenever syscall point enablement changes.
|
||||
func (s *State) AddSyscallFlagListener(listener SyscallFlagListener) {
|
||||
s.registrationMu.Lock()
|
||||
defer s.registrationMu.Unlock()
|
||||
s.syscallFlagListeners = append(s.syscallFlagListeners, listener)
|
||||
}
|
||||
|
||||
// Enabled returns true if any Sink is registered for the given checkpoint.
|
||||
func (s *State) Enabled(p Point) bool {
|
||||
word, bit := p/32, p%32
|
||||
word, bit := p/numPointsPerUint32, p%numPointsPerUint32
|
||||
if int(word) >= len(s.enabledPoints) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -35,6 +35,17 @@ const (
|
||||
syscallTypesCount
|
||||
)
|
||||
|
||||
// SyscallFlagListener is an interface that is notified when syscall point enablement changes.
|
||||
//
|
||||
// It is used to notify the kernel's syscall table about syscall points, without introducing a
|
||||
// direct dependency on it.
|
||||
type SyscallFlagListener interface {
|
||||
// UpdateSecCheck is called each time the system call point enablement may have changed.
|
||||
// This is called with seccheck.State.mu held, so it is expected to be fast and not re-entrant
|
||||
// with seccheck.State functions that attempt to re-lock it.
|
||||
UpdateSecCheck(state *State)
|
||||
}
|
||||
|
||||
const (
|
||||
// Copied from kernel.maxSyscallNum to avoid reverse dependency.
|
||||
syscallsMax = 2000
|
||||
|
||||
Reference in New Issue
Block a user