Skip to content

Conversation

@kobrineli
Copy link
Contributor

@kobrineli kobrineli commented Oct 27, 2025

Description

This change adds matchParentBinaries selector, which might be useful for proper and granular filtering of parent binaries, which is needed in some specific cases.

For instance, there is a python script, which invokes some system calls, which we want to intercept and report. If such script is executed by some system process, we want to filter it out. Otherwise, we report it. For this filtering we need a selector for parent binary, because we cannot filter out events only by current binary, which in case of python script execution is always python.

For more real example, consider we want to hook all calls of chmod system call to prevent creating new binaries on the system manually. apt-key binary, when it installs some packages (such cases we don't want to report), doesn't call chmod directly, but uses /usr/bin/chmod binary, which calls chmod system call inside. matchParentBinaries selector would help to create accurate exclusion for this case.

Example of policy with matchParentBinaries selector:

Consider we want to get events about all files, which were made executable, with chmod syscall, but don't want to get events about apt-key making files executable. Unfortunately, apt-key doesn't make files executable with syscall directly, but uses /usr/bin/chmod binary, which calls chmod function, so to filter such events we need to have selectors for both parent and current binary, so the resulting policy will look like this:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: chmod-x-bit
spec:
  kprobes:
    - call: "sys_chmod"
      syscall: true
      args:
        - index: 0
          type: "string"
          label: "pathname"
        - index: 1
          type: "int"
          label: "mode"
      selectors:
        - matchArgs:
            - index: 1
              operator: "Mask"
              values:
                - "73" # X bit for owner, group or all (001001001)
          matchBinaries:
            - operator: "NotIn"
              values:
                "/usr/bin/chmod"
        - matchArgs:
            - index: 1
              operator: "Mask"
              values:
                - "73" # X bit for owner, group or all (001001001)
          matchBinaries:
            - operator: "In"
              values:
              - "/usr/bin/chmod"
          matchParentBinaries:
            - operator: "NotIn"
              values:
              - "/usr/bin/apt-key"

If current binary is /usr/bin/chmod, we don't care about parent binary, but if current binary is /usr/bin/chmod, we don't want the parent binary to be /usr/bin/apt-key.

Changelog

@kobrineli kobrineli requested a review from a team as a code owner October 27, 2025 13:10
@kobrineli kobrineli requested a review from kevsecurity October 27, 2025 13:10
@netlify
Copy link

netlify bot commented Oct 27, 2025

Deploy Preview for tetragon ready!

Name Link
🔨 Latest commit 8e33265
🔍 Latest deploy log https://app.netlify.com/projects/tetragon/deploys/695d6a615bc6ff000846eea1
😎 Deploy Preview https://deploy-preview-4254--tetragon.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@kobrineli kobrineli force-pushed the add-match-parents-filter branch 2 times, most recently from 4f73f6d to e31cabe Compare October 27, 2025 13:27
@kobrineli
Copy link
Contributor Author

Probably I should add docs for this selector

@kobrineli kobrineli force-pushed the add-match-parents-filter branch from e31cabe to 70ac377 Compare October 27, 2025 13:41
Copy link
Contributor

@kkourt kkourt left a comment

Choose a reason for hiding this comment

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

Thanks!

One thing that would help reviewing this PR would be to split it into multiple commits.
See https://tetragon.io/docs/contribution-guide/submitting-a-pull-request/.

Before the implementation, though, it would be interesting to specify the fetaure so that's it is clear what the semantics are. This can be done in the PR itself (e.g,. in a commit message), in an issue, or in a CFP (see https://github.com/cilium/design-cfps). Whatever works best for you!

For example, it would be very useful to have an example policy that uses the newly introduced operator and discuss how it works

@kkourt
Copy link
Contributor

kkourt commented Oct 27, 2025

Another thing to note is that the functionality is similar to https://tetragon.io/docs/concepts/tracing-policy/selectors/#follow-children. So I wonder if something like:

- matchBinaries:
  - operator: "In"
    values:
    - "/usr/sbin/sshd"
    followChildren: true
  - operator: "NotIn"
    values:
    - "/usr/sbin/sshd"

Would achieve a similar result. The first thing to check would be if matchBinaries are ANDed as, for example, MatchAargs are. The second thing, is that this will not just match the parent, but every ancestor which might or might not be what we want.

@kobrineli
Copy link
Contributor Author

@kkourt
Tetragon currently supports only one match binary operator by selector:
https://github.com/cilium/tetragon/blob/main/pkg/selectors/kernel.go#L1426

So listing multiple operators in match binary selector will fail.

@kkourt
Copy link
Contributor

kkourt commented Oct 27, 2025

One more thing to note is that this looks related to

@kkourt Tetragon currently supports only one match binary operator by selector: https://github.com/cilium/tetragon/blob/main/pkg/selectors/kernel.go#L1426

So listing multiple operators in match binary selector will fail.

Thanks!

I don't remember why this limitation exists, but my guess is that this is something that can be fixed. So, then, the question in my mind is whether the use-case you are describing is better served by matching on the immediate parent or all on ancestors (for which some support already exists).

PS: Might be worth updating the docs to reflect above.

@kevsecurity
Copy link
Contributor

@kkourt Tetragon currently supports only one match binary operator by selector: https://github.com/cilium/tetragon/blob/main/pkg/selectors/kernel.go#L1426

So listing multiple operators in match binary selector will fail.

I wonder if supporting 2 binary selectors would be easier than supporting N, but would potentially achieve the result without supporting parent selectors altogether?

We could maybe have a limit to how many "generations" of children we follow. In this case, a limit of 1 generation would prevent reporting grandchildren. Haven't looked to see if this is easy or not however.

@kobrineli
Copy link
Contributor Author

I wonder if supporting 2 binary selectors would be easier than supporting N, but would potentially achieve the result without supporting parent selectors altogether?

Currently exactly 1 matchBinaries selector is mapped by selector idx. If it is not complicated to support mapping the array of matchBinaries selectors by selector idx, probably with additional field with index in tg_mb_paths map, then it would be easier, than supporting matchParents selector. But I'm afraid, that such changes will lead to hard refactoring in bpf code, while supporting separate selector, which can also be only one for current selectors set, seems pretty easy.

Moreover, maybe explicit parents filtering, working in the same way as current process binary filtering, is easier to understand and to read.

@kobrineli
Copy link
Contributor Author

kobrineli commented Oct 27, 2025

So, then, the question in my mind is whether the use-case you are describing is better served by matching on the immediate parent or all on ancestors (for which some support already exists).

With current existing support we cannot exclude the binary itself because of the selector number limitation.
Moreover, we may want to apply filter for concrete child of parent process, rather than to all process children (as with followChildren option), combining matchBinaries and matchParents selectors.

@kobrineli kobrineli force-pushed the add-match-parents-filter branch from 70ac377 to d503dbd Compare October 27, 2025 15:28
@kobrineli
Copy link
Contributor Author

So I wonder if something like:

- matchBinaries:
  - operator: "In"
    values:
    - "/usr/sbin/sshd"
    followChildren: true
  - operator: "NotIn"
    values:
    - "/usr/sbin/sshd"

Would achieve a similar result.

If we want to match concrete child process, seems like this wouldn't achieve the same result.

@kobrineli kobrineli force-pushed the add-match-parents-filter branch from d503dbd to 12bf008 Compare October 28, 2025 08:03
@kobrineli
Copy link
Contributor Author

@kkourt
Hi!
I've separated PR into several commits.

@kkourt kkourt added the release-note/major This PR introduces major new functionality label Oct 28, 2025
@kkourt
Copy link
Contributor

kkourt commented Oct 28, 2025

@kkourt Hi! I've separated PR into several commits.

Thanks! Can you please add some context in the commit messages (see: https://tetragon.io/docs/contribution-guide/submitting-a-pull-request/)?

Before merging the PR, we would need tests and documentation updates. If you are looking for early feedback, can you add an example (maybe in the commit message) on how to use the new selector?

@kkourt
Copy link
Contributor

kkourt commented Oct 28, 2025

I'm also seeing some CI failures:

--- FAIL: TestKprobeSigkillExecveMap1 (306.00s)
    kprobe_sigkill_test.go:65: child pid is 32313 and spec file is /tmp/sigkill-3506928072.yaml
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="BTF discovery: default kernel btf file found" btf-file=/sys/kernel/btf/vmlinux
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Creating new EventCache" retries=15 delay=2s
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Starting process manager" enableK8s=false enableProcessCred=true enableProcessNs=true
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Starting JSON exporter"
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Cgroup rate disabled (0/0s)"
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="BTF file: using metadata file" metadata=/sys/kernel/btf/vmlinux
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="Loading sensor" name=__base__
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="Loading kernel version 6.11.11"
    logcapture.go:24: time=2025-10-28T08:17:34.945Z level=INFO msg="Loaded sensor successfully" sensor=__base__
    logcapture.go:24: time=2025-10-28T08:17:34.984Z level=INFO msg="Read ProcFS /proc appended 222/284 entries"
    logcapture.go:24: time=2025-10-28T08:17:34.984Z level=WARN msg="failed to put value in execve_map" value="&{Process:{Pid:10 Pad:0 Ktime:540000000} Parent:{Pid:2 Pad:0 Ktime:540000000} Flags:0 Nspid:0 Namespaces:{UtsInum:4026531838 IpcInum:4026531839 MntInum:4026531841 PidInum:4026531836 PidChildInum:4026531836 NetInum:4026531840 TimeInum:4026531834 TimeChildInum:4026531834 CgroupInum:4026531835 UserInum:4026531837} Capabilities:{Permitted:2199023255551 Effective:2199023255551 Inheritable:0} Binary:{PathLength:27 Reversed:0 Path:[107 119 111 114 107 101 114 47 48 58 48 72 45 101 118 101 110 116 115 95 104 105 103 104 112 114 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] End:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] EndR:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Args:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] MBSet:0 MBGen:0}}" error="update: key too big for map: argument list too long"

@kobrineli
Copy link
Contributor Author

I'm also seeing some CI failures:

--- FAIL: TestKprobeSigkillExecveMap1 (306.00s)
    kprobe_sigkill_test.go:65: child pid is 32313 and spec file is /tmp/sigkill-3506928072.yaml
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="BTF discovery: default kernel btf file found" btf-file=/sys/kernel/btf/vmlinux
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Creating new EventCache" retries=15 delay=2s
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Starting process manager" enableK8s=false enableProcessCred=true enableProcessNs=true
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Starting JSON exporter"
    logcapture.go:24: time=2025-10-28T08:17:34.681Z level=INFO msg="Cgroup rate disabled (0/0s)"
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="BTF file: using metadata file" metadata=/sys/kernel/btf/vmlinux
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="Loading sensor" name=__base__
    logcapture.go:24: time=2025-10-28T08:17:34.682Z level=INFO msg="Loading kernel version 6.11.11"
    logcapture.go:24: time=2025-10-28T08:17:34.945Z level=INFO msg="Loaded sensor successfully" sensor=__base__
    logcapture.go:24: time=2025-10-28T08:17:34.984Z level=INFO msg="Read ProcFS /proc appended 222/284 entries"
    logcapture.go:24: time=2025-10-28T08:17:34.984Z level=WARN msg="failed to put value in execve_map" value="&{Process:{Pid:10 Pad:0 Ktime:540000000} Parent:{Pid:2 Pad:0 Ktime:540000000} Flags:0 Nspid:0 Namespaces:{UtsInum:4026531838 IpcInum:4026531839 MntInum:4026531841 PidInum:4026531836 PidChildInum:4026531836 NetInum:4026531840 TimeInum:4026531834 TimeChildInum:4026531834 CgroupInum:4026531835 UserInum:4026531837} Capabilities:{Permitted:2199023255551 Effective:2199023255551 Inheritable:0} Binary:{PathLength:27 Reversed:0 Path:[107 119 111 114 107 101 114 47 48 58 48 72 45 101 118 101 110 116 115 95 104 105 103 104 112 114 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] End:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] EndR:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Args:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] MBSet:0 MBGen:0}}" error="update: key too big for map: argument list too long"

Is it an issue of this PR?
There is error update: key too big for map: argument list too long while putting a value into execve_map, which was not changed in this PR.

@kobrineli kobrineli force-pushed the add-match-parents-filter branch 2 times, most recently from eeab3e7 to 5d54ba8 Compare October 28, 2025 15:30
Copy link
Member

@mtardy mtardy left a comment

Choose a reason for hiding this comment

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

Thanks, overall looks good to me, just a few nits

Probably I should add docs for this selector

Please update the documentation with this new selector (see https://tetragon.io/docs/concepts/tracing-policy/selectors/).

@mtardy
Copy link
Member

mtardy commented Oct 28, 2025

I see checkpatch is complaining and other CI checks, you can run make checkpatch locally and see more docs on how to create a nice PR here https://tetragon.io/docs/contribution-guide/submitting-a-pull-request/ if that can help :)

Copy link
Contributor

@kevsecurity kevsecurity left a comment

Choose a reason for hiding this comment

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

LGTM (barring the red test results).

Copy link
Contributor

@olsajiri olsajiri left a comment

Choose a reason for hiding this comment

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

would it be possible to handle this as special case of current BinarySelector, having some parent bool like:

diff --git a/pkg/k8s/apis/cilium.io/v1alpha1/types.go b/pkg/k8s/apis/cilium.io/v1alpha1/types.go
index 6c3210b0acf5..b5133b1e5fe2 100644
--- a/pkg/k8s/apis/cilium.io/v1alpha1/types.go
+++ b/pkg/k8s/apis/cilium.io/v1alpha1/types.go
@@ -124,6 +124,10 @@ type BinarySelector struct {
        // +kubebuilder:validation:Optional
        // +kubebuilder:default=false
        FollowChildren bool `json:"followChildren"`
+       // match parent binary
+       // +kubebuilder:validation:Optional
+       // +kubebuilder:default=false
+       Parent bool `json:"parent"`
 }

then all the code dups with binary selectors would go away
we could perhaps think a bit about some maps max_entries tuning

@kobrineli kobrineli force-pushed the add-match-parents-filter branch from 5d54ba8 to 600672b Compare October 29, 2025 09:44
@kobrineli
Copy link
Contributor Author

@olsajiri Hi!
Current implementation of matchBinaries selector allows to have only one matchBinaries per selector, so filtering both current binary and parent binary will not be possible.

matchBinaries selector refactoring might be complicated and not so easy as adding additional new selector for direct parent filtering.

Moreover, matchBinaries selector supports followChildren option, which works with mbsetid field of process in tetragon execve map, so combining 2 different matchBinaries selector, which both can change this field, will be a problem, and this place will need to be changed as well.

@kobrineli
Copy link
Contributor Author

kobrineli commented Nov 14, 2025

@olsajiri @kkourt
Hi!
I have a solution, which fixes bug described in #4254 (comment).

The bug is that when we match for parent binaries, we search for parent by current process real parent pid. But if execve system call was invoked in process without previous fork system call, i.e. new binary is executed in the same process, matchParentBinaries selector would lose info about real parent of current process (same pid, different binary), and filter by actual grand parent.

There are 2 possible solutions:

  1. save parent binary in execve_map_value, but it will have big memory overhead (value size will increase almost twice)
  2. create binary_heap_map and binaries_map, which we will use to dynamically store info about parent binary only in cases when cleanup process pid is equal to current process pid.
    The map would look like this:
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __type(key, __u32);
    __type(value, struct binary);
} binary_heap_map SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1);
    __type(key, __u32);
    __type(value, struct binary);
} binaries_map SEC(".maps");

And we will work with them in bpf_execve_event like this:

        if (curr->key.pid == event->cleanup_key.pid) {
            __u32 zero = 0;
            struct binary *bin = map_lookup_elem(&binary_heap_map, &zero);
            if (bin) {
                memcpy(bin, &curr->bin, sizeof(curr->bin));
                map_update_elem(&binaries_map, &curr->key.pid, bin, BPF_ANY);
            }
        }

And then, when we match parent binaries, we check for the entry in binaries map and if it exists, use it instead of parent binary. I checked, that worked.

Separate map could give us not so big memory overhead as parent binary field in execve map value, because we could set its size to be N times less than execve map (because case when parent pid and current process pid are same is not common)

What do you think about it?

@olsajiri
Copy link
Contributor

There are 2 possible solutions:

1. save parent binary in `execve_map_value`, but it will have big memory overhead (value size will increase almost twice)

2. create `binary_heap_map` and `binaries_map`, which we will use to dynamically store info about parent binary only in cases when cleanup process pid is equal to current process pid.
   The map would look like this:

What do you think about it?

we were discussing this with @kkourt and IIUC we lean to have have documented that both binaries selectors match only existing processes.. so we disregard cases were process does multiple execs

that said, if you would have the change for option 2) it'd be great to check that and see if that might be way out

@kobrineli
Copy link
Contributor Author

kobrineli commented Nov 18, 2025

@olsajiri @kkourt
Hi!
I've pushed changes which fix the problem with matching parent in case of multiple execs in same process.

What changed:

  • now we create parent_binaries_map and binary_heap_map maps, which are used for storing parent binaries info in case of multiple execs
  • parent binaries map size is set to be 32 times less than execve map size, so with default 32768 entries in execve map there will be only 1024 entries in parent binaries size. The number was taken almost randomly, I assume that there will be not many processes created without previous fork, but we can make ratio bigger or smaller
  • if parent binary entry was found when matching parent binaries, we check found parent binary instead of parent process binary

Also I've added more tests, which include running bash with -c option only, so child process is created with parent process pid, later I'm going to add tests with followChildren as well.

I've checked changes locally, now it works fine with all cases, including using followChildren (for such cases with multiple execs saving bitset is already handled (here)[https://github.com/cilium/tetragon/blob/b82408601fe54ece9ae1aa0e94b6d9fe52be3e5f/bpf/lib/process.h#L335] ).

All tests passed, I'll fix checkpatch in the end if everything else is fine.

If you don't like the idea, I'm okay with reverting these changes and documenting that current selectors work only with real processes and don't with for processes created with multiple execs.

@kkourt
Copy link
Contributor

kkourt commented Nov 19, 2025

Thanks! I'll take a look when I get a chance.

and documenting that current selectors work only with real processes and don't with for processes created with multiple execs.

I don't see this as an option. We either support this feature for everything or not at all.

@kobrineli
Copy link
Contributor Author

@kkourt @olsajiri
Hi!
Could you check the fix, please?

@kobrineli kobrineli force-pushed the add-match-parents-filter branch 6 times, most recently from a73ef81 to da56f8c Compare December 17, 2025 13:53
@kobrineli
Copy link
Contributor Author

kobrineli commented Dec 17, 2025

@kevsecurity @kkourt

Hi! I've implemented @kevsecurity idea with parents map switch.
Now we save parent binary for every process, and check whether cleanup key pid is same as current process pid, meaning that multiple execve calls occurred in the same process, and in this case save current binary as parent binary.

followChildren option works fine, checked it manually with all possible combinations (child process with parent pid, with different pids and with every operator).

Tests cover both variants when process is executed with same pid (when we just run bash -c), and when process has differnet pid.

@kobrineli kobrineli requested review from kkourt and olsajiri December 17, 2025 14:41
@kobrineli
Copy link
Contributor Author

@olsajiri Hi! Could you check current changes as well, please?

}

func writePrefixStrings(k *KernelSelectorState, values []string) error {
mid, err := writePrefix(k, values, "MatchArgs")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see why this is removed, we could leave this debug info, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we have info about selectors called this function in its callers, so this info is actually redundant. if we leave it, we should also add info about match parent binaries selector here

struct binary *parent_bin = map_lookup_elem(&parent_binaries_map, &enter->key.pid);

if (parent_bin)
/* matchParentBinaries key is in rage [MAX_SELECTORS; MAX_SELECTORS * 2) */
Copy link
Contributor

Choose a reason for hiding this comment

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

angry selector ;-)


struct binary *parent_bin = map_lookup_elem(&parent_binaries_map, &enter->key.pid);

if (parent_bin)
Copy link
Contributor

Choose a reason for hiding this comment

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

please use the { } for the condition code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I removed them because of clang-format errors in CI, but I'll try to put them back

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Warning: WARNING:BRACES: braces {} are not necessary for single statement blocks

there are only warnings in style ci job, but it is still red


if (parent)
map_update_elem(&parent_binaries_map, &curr->key.pid, &parent->bin, BPF_ANY);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

so this is still solution just fort the immediate parent? please put some explanation on how this new map works.. there's nothing in changelog or code AFAICS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure what you meant. the whole selector is for matching current process immediate parent (and not only immediate if followChildren is true).

in branch upper if we don't have event_clone flag, instead of real parent binary we fill map with current binary, because no event_clone flag means we had multiple execs in same process.

I'll add comments to the map in bpf code

Copy link
Contributor Author

@kobrineli kobrineli Jan 6, 2026

Choose a reason for hiding this comment

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

current solution helps to mitigate two problems of the initial implementation:

  1. if parent process exited and it is not stored in execve_map, we will still have its binary in parent_binaries_map
  2. if multiple execs are performed, we can understand it by not having event_clone flag, so we use current binary instead of parent binary

This adds genericMatchBinariesSelector enumeration,
renames match binaries maps and changes match binaries
parsing to allow parsing other matchBinaries-like selectors
in future with the same code.

Signed-off-by: Kobrin Ilay <[email protected]>
This commit introduces new matchParentBinaries selector,
which is used for filtering parent process binaries. New
selector works similarly to matchBinaries selector, including
followChildren option, which allows to match not only
direct parent binaries, but transitive parent binaries
as well.

matchParentBinaries selector is needed in cases when we
want to have granular filters on parent binaries for some
events. For example, we may want to intercept calls for
specific system call from specific binary only in case
it was executed with interactive shell. For that we can
add following selector, which will match only process with
bash, sh or zsh parent:

```
- matchParentBinaries:
  - operator: "In"
    values:
    - "/usr/bin/bash"
    - "/usr/bin/sh"
    - "/usr/bin/zsh"
```

Signed-off-by: Kobrin Ilay <[email protected]>
This commit adds crds generated for matchParentBinaries selector

Signed-off-by: Kobrin Ilay <[email protected]>
This commits adds logic for parsing matchParentBinaries selector.
New selector is stored in same maps as matchBinaries selector,
but has key offset equal to MaxSelectors.

Signed-off-by: Kobrin Ilay <[email protected]>
This commit adds parents binary map to sensors and restricts using
matchParentBinariesMap selector when parents map is not enabled.
Parents map is disabled by default due to additional memory
overhead from storing info about parent binaries. Default parents
map size is same as default execve map size.

Signed-off-by: Kobrin Ilay <[email protected]>
This commit adds bpf code for parent binaries filtering for new matchParentBinaries selector.
Match binaries bpf maps sizes are increased to MAX_SELECTORS * 2 to store both selectors options.
Parent filtering is processed with the same code, but key for matchParentBinaries has offset
equal to MAX_SELECTORS.

Signed-off-by: Kobrin Ilay <[email protected]>
This commit adds test for new matchParentBinaries selector.
In the test we verify that all operations (In, NotIn, Prefix, NotPrefix,
Postfix, NotPostfix) work correctly.

Signed-off-by: Kobrin Ilay <[email protected]>
@kobrineli kobrineli force-pushed the add-match-parents-filter branch 2 times, most recently from 8e33265 to 6f2a2db Compare January 6, 2026 20:10
pin and update map only when option is enabled.
check for event_clone flag instead of cleanup process pid

Signed-off-by: Kobrin Ilay <[email protected]>
@kobrineli kobrineli force-pushed the add-match-parents-filter branch from 6f2a2db to 142f650 Compare January 6, 2026 20:29
@kobrineli kobrineli requested a review from olsajiri January 6, 2026 20:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/major This PR introduces major new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants