Skip to content

Commit

Permalink
fix(ebpf): normalize time only after the hash calc
Browse files Browse the repository at this point in the history
Some timestamps were normalized before computing the hash, leading to
rogue proctree entries - mismatched hashes.
  • Loading branch information
geyslan committed Feb 5, 2025
1 parent 99498e2 commit e01fed9
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 30 deletions.
19 changes: 12 additions & 7 deletions pkg/ebpf/controlplane/processes.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,19 @@ func (ctrl *Controller) procTreeForkProcessor(args []trace.Argument) error {
return err
}

forkFeed.TimeStamp = time.BootToEpochNS(forkFeed.TimeStamp)
forkFeed.ChildStartTime = time.BootToEpochNS(forkFeed.ChildStartTime)
forkFeed.ParentStartTime = time.BootToEpochNS(forkFeed.ParentStartTime)
forkFeed.LeaderStartTime = time.BootToEpochNS(forkFeed.LeaderStartTime)

// Compute hashes using raw kernel start times without normalization.
// The sched_process_fork signal is the only one that doesn't compute the hash in the kernel,
// so it must be handled here.
forkFeed.ChildHash = utils.HashTaskID(uint32(forkFeed.ChildTid), forkFeed.ChildStartTime)
forkFeed.ParentHash = utils.HashTaskID(uint32(forkFeed.ParentTid), forkFeed.ParentStartTime)
forkFeed.LeaderHash = utils.HashTaskID(uint32(forkFeed.LeaderTid), forkFeed.LeaderStartTime)

// Normalize times
forkFeed.ChildStartTime = time.BootToEpochNS(forkFeed.ChildStartTime)
forkFeed.ParentStartTime = time.BootToEpochNS(forkFeed.ParentStartTime)
forkFeed.LeaderStartTime = time.BootToEpochNS(forkFeed.LeaderStartTime)
forkFeed.TimeStamp = time.BootToEpochNS(forkFeed.TimeStamp)

return ctrl.processTree.FeedFromFork(forkFeed)
}

Expand All @@ -123,6 +127,8 @@ func (ctrl *Controller) procTreeExecProcessor(args []trace.Argument) error {
if err != nil {
return err
}
execFeed.TimeStamp = time.BootToEpochNS(execFeed.TimeStamp) // normalize time

execFeed.TaskHash, _ = parse.ArgVal[uint32](args, "task_hash")
execFeed.ParentHash, _ = parse.ArgVal[uint32](args, "parent_hash")
execFeed.LeaderHash, _ = parse.ArgVal[uint32](args, "leader_hash")
Expand Down Expand Up @@ -202,8 +208,7 @@ func (ctrl *Controller) procTreeExitProcessor(args []trace.Argument) error {
if err != nil {
return err
}
// time of exit is already a timestamp)
exitFeed.TimeStamp = time.BootToEpochNS(exitFeed.TimeStamp)
exitFeed.TimeStamp = time.BootToEpochNS(exitFeed.TimeStamp) // normalize time

exitFeed.TaskHash, err = parse.ArgVal[uint32](args, "task_hash")
if err != nil {
Expand Down
17 changes: 6 additions & 11 deletions pkg/ebpf/events_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,8 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch

// populate all the fields of the event used in this stage, and reset the rest

// normalize timestamp context fields for later use
normalizedTs := time.BootToEpochNS(eCtx.Ts)
normalizedThreadStartTime := time.BootToEpochNS(eCtx.StartTime)
normalizedLeaderStartTime := time.BootToEpochNS(eCtx.LeaderStartTime)
normalizedParentStartTime := time.BootToEpochNS(eCtx.ParentStartTime)

evt.Timestamp = int(normalizedTs)
evt.ThreadStartTime = int(normalizedThreadStartTime)
evt.Timestamp = int(time.BootToEpochNS(eCtx.Ts)) // normalize time
evt.ThreadStartTime = int(time.BootToEpochNS(eCtx.StartTime)) // normalize time
evt.ProcessorID = int(eCtx.ProcessorId)
evt.ProcessID = int(eCtx.Pid)
evt.ThreadID = int(eCtx.Tid)
Expand Down Expand Up @@ -279,9 +273,10 @@ func (t *Tracee) decodeEvents(ctx context.Context, sourceChan chan []byte) (<-ch
evt.ContextFlags = flags
evt.Syscall = syscall
evt.Metadata = nil
evt.ThreadEntityId = utils.HashTaskID(eCtx.HostTid, normalizedThreadStartTime)
evt.ProcessEntityId = utils.HashTaskID(eCtx.HostPid, normalizedLeaderStartTime)
evt.ParentEntityId = utils.HashTaskID(eCtx.HostPpid, normalizedParentStartTime)
// compute hashes using kernel start times as-is, without normalization
evt.ThreadEntityId = utils.HashTaskID(eCtx.HostTid, eCtx.StartTime)
evt.ProcessEntityId = utils.HashTaskID(eCtx.HostPid, eCtx.LeaderStartTime)
evt.ParentEntityId = utils.HashTaskID(eCtx.HostPpid, eCtx.ParentStartTime)

// If there aren't any policies that need filtering in userland, tracee **may** skip
// this event, as long as there aren't any derivatives or signatures that depend on it.
Expand Down
28 changes: 17 additions & 11 deletions pkg/ebpf/processor_proctree.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,18 @@ func (t *Tracee) procTreeForkProcessor(event *trace.Event) error {
if err != nil {
return err
}
forkFeed.TimeStamp = forkFeed.ChildStartTime // event timestamp is the same

// Calculate hashes
// Compute hashes using raw kernel start times without normalization
forkFeed.ParentHash = utils.HashTaskID(uint32(forkFeed.ParentTid), uint64(forkFeed.ParentStartTime))
forkFeed.ChildHash = utils.HashTaskID(uint32(forkFeed.ChildTid), uint64(forkFeed.ChildStartTime))
forkFeed.LeaderHash = utils.HashTaskID(uint32(forkFeed.LeaderTid), uint64(forkFeed.LeaderStartTime))
forkFeed.ChildHash = utils.HashTaskID(uint32(forkFeed.ChildTid), uint64(forkFeed.ChildStartTime))

// Normalize times
forkFeed.ParentStartTime = traceetime.BootToEpochNS(forkFeed.ParentStartTime)
forkFeed.LeaderStartTime = traceetime.BootToEpochNS(forkFeed.LeaderStartTime)
forkFeed.ChildStartTime = traceetime.BootToEpochNS(forkFeed.ChildStartTime)

forkFeed.TimeStamp = uint64(event.Timestamp) // already normalized at decode stage

return t.processTree.FeedFromFork(forkFeed)
}
Expand Down Expand Up @@ -169,10 +175,10 @@ func (t *Tracee) procTreeExecProcessor(event *trace.Event) error {
return err
}

execFeed.TimeStamp = uint64(event.Timestamp)
execFeed.TaskHash = utils.HashTaskID(uint32(event.HostThreadID), uint64(event.ThreadStartTime))
execFeed.ParentHash = 0 // regular pipeline does not have parent hash
execFeed.LeaderHash = 0 // regular pipeline does not have leader hash
execFeed.TimeStamp = uint64(event.Timestamp) // already normalized at decode stage
execFeed.TaskHash = event.ThreadEntityId // already computed at decode stage
execFeed.ParentHash = event.ParentEntityId // already computed at decode stage
execFeed.LeaderHash = event.ProcessEntityId // already computed at decode stage

return t.processTree.FeedFromExec(execFeed)
}
Expand Down Expand Up @@ -206,10 +212,10 @@ func (t *Tracee) procTreeExitProcessor(event *trace.Event) error {
// return err
// }

exitFeed.TimeStamp = uint64(event.Timestamp) // time of exit is already a timestamp
exitFeed.TaskHash = utils.HashTaskID(uint32(event.HostThreadID), uint64(event.ThreadStartTime))
// exitFeed.ParentHash = 0 // regular pipeline does not have parent hash
// exitFeed.LeaderHash = 0 // regular pipeline does not have leader hash
exitFeed.TimeStamp = uint64(event.Timestamp) // already normalized at decode stage
exitFeed.TaskHash = event.ThreadEntityId // already computed at decode stage
exitFeed.ParentHash = event.ParentEntityId // already computed at decode stage
exitFeed.LeaderHash = event.ProcessEntityId // already computed at decode stage

return t.processTree.FeedFromExit(exitFeed)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/time/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const (
// in other functions of the package.
//
// Reference points can be set from two differents clocks: CLOCK_MONOTONIC or CLOCK_BOOTTIME.
// Tracee bpf code tries to use boottime clock if available, otherwise uses monotonic clock.
// Tracee bpf code tries to use boot time clock if available, otherwise uses monotonic clock.
// ClockGettime get time elapsed since start (boot) so tracee can calculate event timestamps.
func Init(clockID int32) error {
var err error
Expand Down

0 comments on commit e01fed9

Please sign in to comment.